From 7fb566a976ad7acfa79b78742dfaa1d8995bd37c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 10 Apr 2024 17:52:55 +0000 Subject: [PATCH 001/423] Failed to perform coherency update for one or more dependencies. From 111ec282e7c86ca6cdf525672bd9569f3b8c9946 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 16 Apr 2024 10:21:09 +0200 Subject: [PATCH 002/423] Warn for `yield return` in `lock` (#72755) * Warn for `yield return` in `lock` * Encapsulate utility for filtering diagnostics * Filter warning for `yield return` in the new `lock` * Test `yield` in `lock` * Update existing test * Revert "Filter warning for `yield return` in the new `lock`" This reverts commit eafc5948c82c8b0cd28150dc68980a47d821157a. * Revert "Encapsulate utility for filtering diagnostics" This reverts commit 654d458b91692d7bab3066b8a16ed7bc31e64ad8. * Add a comment separating error and warning conditions * Update tests after removing warning filtering * Remove runtime-dependent output verification * Remove unused code from a test --- .../CSharp/Warnversion Warning Waves.md | 8 + .../Portable/Binder/Binder_Statements.cs | 5 + .../CSharp/Portable/CSharpResources.resx | 6 + .../CSharp/Portable/Errors/ErrorCode.cs | 2 + .../CSharp/Portable/Errors/ErrorFacts.cs | 5 + .../Generated/ErrorFacts.Generated.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 10 ++ .../Portable/xlf/CSharpResources.de.xlf | 10 ++ .../Portable/xlf/CSharpResources.es.xlf | 10 ++ .../Portable/xlf/CSharpResources.fr.xlf | 10 ++ .../Portable/xlf/CSharpResources.it.xlf | 10 ++ .../Portable/xlf/CSharpResources.ja.xlf | 10 ++ .../Portable/xlf/CSharpResources.ko.xlf | 10 ++ .../Portable/xlf/CSharpResources.pl.xlf | 10 ++ .../Portable/xlf/CSharpResources.pt-BR.xlf | 10 ++ .../Portable/xlf/CSharpResources.ru.xlf | 10 ++ .../Portable/xlf/CSharpResources.tr.xlf | 10 ++ .../Portable/xlf/CSharpResources.zh-Hans.xlf | 10 ++ .../Portable/xlf/CSharpResources.zh-Hant.xlf | 10 ++ .../EditAndContinueStateMachineTests.cs | 5 +- .../CSharp/Test/Emit2/Semantics/LockTests.cs | 10 +- .../Test/Semantic/Semantics/IteratorTests.cs | 162 ++++++++++++++++++ .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 4 + 23 files changed, 335 insertions(+), 3 deletions(-) diff --git a/docs/compilers/CSharp/Warnversion Warning Waves.md b/docs/compilers/CSharp/Warnversion Warning Waves.md index 65d25e8b1b528..fc070915f27bf 100644 --- a/docs/compilers/CSharp/Warnversion Warning Waves.md +++ b/docs/compilers/CSharp/Warnversion Warning Waves.md @@ -13,6 +13,14 @@ In a typical project, this setting is controlled by the `AnalysisLevel` property which determines the `WarningLevel` property (passed to the `Csc` task). For more information on `AnalysisLevel`, see https://devblogs.microsoft.com/dotnet/automatically-find-latent-bugs-in-your-code-with-net-5/ +## Warning level 9 + +The compiler shipped with .NET 9 (the C# 13 compiler) contains the following warnings which are reported only under `/warn:9` or higher. + +| Warning ID | Description | +|------------|-------------| +| CS9230 | ['yield return' should not be used in the body of a lock statement](https://github.com/dotnet/roslyn/issues/72443) | + ## Warning level 8 The compiler shipped with .NET 8 (the C# 12 compiler) contains the following warnings which are reported only under `/warn:8` or higher. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 23271214ea97a..9a453523f720a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -268,6 +268,11 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi { Error(diagnostics, ErrorCode.ERR_YieldNotAllowedInScript, node.YieldKeyword); } + // NOTE: Error conditions should be checked above this point; only warning conditions below. + else if (this.Flags.Includes(BinderFlags.InLockBody)) + { + Error(diagnostics, ErrorCode.WRN_BadYieldInLock, node.YieldKeyword); + } CheckRequiredLangVersionForIteratorMethods(node, diagnostics); return new BoundYieldReturnStatement(node, argument); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 1c597710b3960..bca68783ecce8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7905,4 +7905,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Modifiers cannot be placed on using declarations + + 'yield return' should not be used in the body of a lock statement + + + 'yield return' should not be used in the body of a lock statement + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 70c3ed6d8b861..08f0c3e6b45cf 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2304,6 +2304,8 @@ internal enum ErrorCode #endregion + WRN_BadYieldInLock = 9230, + // Note: you will need to do the following after adding warnings: // 1) Re-generate compiler code (eng\generate-compiler-code.cmd). } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index b4eaef25f8e7a..2d0a6c799d702 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -207,6 +207,10 @@ internal static int GetWarningLevel(ErrorCode code) // docs/compilers/CSharp/Warnversion Warning Waves.md switch (code) { + case ErrorCode.WRN_BadYieldInLock: + // Warning level 9 is exclusively for warnings introduced in the compiler + // shipped with dotnet 9 (C# 13) and that can be reported for pre-existing code. + return 9; case ErrorCode.WRN_AddressOfInAsync: case ErrorCode.WRN_ByValArraySizeConstRequired: // Warning level 8 is exclusively for warnings introduced in the compiler @@ -2431,6 +2435,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_ParamsCollectionExtensionAddMethod: case ErrorCode.ERR_ParamsCollectionMissingConstructor: case ErrorCode.ERR_NoModifiersOnUsing: + case ErrorCode.WRN_BadYieldInLock: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index 64e748fa825be..008a81230a7b2 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -341,6 +341,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod: case ErrorCode.WRN_DynamicDispatchToParamsCollectionIndexer: case ErrorCode.WRN_DynamicDispatchToParamsCollectionConstructor: + case ErrorCode.WRN_BadYieldInLock: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 52bc7a06848ef..43a0b6a8deff2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2512,6 +2512,16 @@ Modifikátor ref pro argument odpovídající parametru in je ekvivalentem in. Místo toho zvažte použití in. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Je nutné zadat parametr atributu SizeConst. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 31fdb8ad5d94e..5bf51a1f40385 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2512,6 +2512,16 @@ Der ref-Modifizierer für ein Argument, das dem in-Parameter entspricht, entspricht "in". Erwägen Sie stattdessen die Verwendung von "in". + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Der Attributparameter "SizeConst" muss angegeben werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 46d2302cf07e3..169002cf621a1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2512,6 +2512,16 @@ El modificador "ref" de un argumento correspondiente al parámetro "in" es equivalente a "in". Considere la posibilidad de usar "in" en su lugar. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Se debe especificar el parámetro de atributo "SizeConst". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 21d95c543a69c..127c92447318d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2512,6 +2512,16 @@ Le modificateur « ref » d’un argument correspondant au paramètre « in » est équivalent à « in ». Envisagez d’utiliser « in » à la place. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Vous devez spécifier un paramètre d’attribut « SizeConst ». diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 4b6f9d4393503..af24bf98301b5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2512,6 +2512,16 @@ Il modificatore 'ref' per un argomento corrispondente al parametro 'in' equivale a 'in'. Provare a usare 'in'. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. È necessario specificare il parametro di attributo 'SizeConst'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index d22e46da8f2c8..0f77641625862 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2512,6 +2512,16 @@ 'in' パラメーターに対応する引数の 'ref' 修飾子は 'in' と同じです。代わりに 'in' を使用することを検討してください。 + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 属性パラメーター 'SizeConst' を指定する必要があります。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3c3e4232b34f0..fd7b9c8c83b4d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2512,6 +2512,16 @@ 'in' 매개 변수에 해당하는 인수의 'ref' 한정자는 'in'에 해당합니다. 대신 'in'을 사용하는 것이 좋습니다. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 특성 매개 변수 'SizeConst'를 지정해야 합니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 236da182cee77..4092f9b5e67c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2512,6 +2512,16 @@ Modyfikator „ref” dla argumentu odpowiadającego parametrowi „in” jest równoważny parametrowi „in”. Zamiast tego rozważ użycie parametru „in”. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Należy określić parametr atrybutu „SizeConst”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index ed4a82cea33ad..06cd639492e03 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2512,6 +2512,16 @@ O modificador 'ref' do argumento correspondente ao parâmetro 'in' é equivalente a 'in'. Considere usar 'in' em vez disso. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. O parâmetro de atribuição 'SizeConst' deve ser especificado. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 197239919e8d6..954057392e614 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2512,6 +2512,16 @@ Модификатор "ref" для аргумента, соответствующего параметру "in", эквивалентен "in". Попробуйте вместо этого использовать "in". + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. Должен быть указан параметр атрибута "SizeConst". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 44f14a30f0b80..a44ca7bfed87c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2512,6 +2512,16 @@ 'in' parametresine karşılık gelen bir bağımsız değişken için 'ref' değiştiricisi 'in' ile eşdeğerdir. Bunun yerine 'in' kullanmayı düşünün. + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 'SizeConst' öznitelik parametresi belirtilmelidir. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 0983b4f8fc93c..3c164338d3da3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2512,6 +2512,16 @@ 与 “in” 参数对应的参数的 “ref” 修饰符等效于 “in”。请考虑改用 “in”。 + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 必须指定属性参数 “SizeConst”。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ac7f52744cd5c..30d0776683b21 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2512,6 +2512,16 @@ 對應至 'in' 參數之引數的 'ref' 修飾元相當於 'in'。請考慮改為使用 'in'。 + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + + + 'yield return' should not be used in the body of a lock statement + 'yield return' should not be used in the body of a lock statement + + Attribute parameter 'SizeConst' must be specified. 必須指定屬性參數 'SizeConst'。 diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index ed6be2357601c..4119d42f5590f 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -5463,7 +5463,10 @@ static IEnumerable F() var compilation1 = compilation0.WithSource(source1.Tree); var v0 = CompileAndVerify(compilation0); - v0.VerifyDiagnostics(); + v0.VerifyDiagnostics( + // (17,34): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return 1; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(17, 34)); var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); var f0 = compilation0.GetMember("C.F"); diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs index 61b08bdf399e5..a28521e1fd683 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs @@ -2013,7 +2013,10 @@ IEnumerable M() CreateCompilation([source, LockTypeDefinition]).VerifyEmitDiagnostics( // (9,15): error CS4013: Instance of type 'Lock.Scope' cannot be used inside a nested function, query expression, iterator block or async method // lock (new Lock()) - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15)); + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15), + // (11,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return 2; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(11, 13)); } [Fact] @@ -2041,7 +2044,10 @@ async IAsyncEnumerable M() CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes]).VerifyDiagnostics( // (10,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. // lock (new Lock()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(10, 15)); + Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(10, 15), + // (12,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return 2; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(12, 13)); } [Theory, CombinatorialData] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs index 34be3f12005c3..eb0310f80ca82 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs @@ -106,6 +106,168 @@ IEnumerable I() ); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72443")] + public void YieldInLock_Async() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + public class C + { + public async Task ProcessValueAsync() + { + await foreach (int item in GetValuesAsync()) + { + await Task.Yield(); + Console.Write(item); + } + } + + private async IAsyncEnumerable GetValuesAsync() + { + await Task.Yield(); + lock (this) + { + for (int i = 0; i < 10; i++) + { + yield return i; + + if (i == 3) + { + yield break; + } + } + } + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseDll.WithWarningLevel(8)); + CompileAndVerify(comp).VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (23,17): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return i; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(23, 17) + }; + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseDll.WithWarningLevel(9)); + CompileAndVerify(comp).VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseDll); + CompileAndVerify(comp).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72443")] + public void YieldInLock_Sync() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading; + + object o = new object(); + Console.WriteLine($"Before: {Monitor.IsEntered(o)}"); + using (IEnumerator e = GetValues(o).GetEnumerator()) + { + Console.WriteLine($"Inside: {Monitor.IsEntered(o)}"); + while (e.MoveNext()) + { + Console.WriteLine($"{e.Current}: {Monitor.IsEntered(o)}"); + } + Console.WriteLine($"Done: {Monitor.IsEntered(o)}"); + } + Console.WriteLine($"After: {Monitor.IsEntered(o)}"); + + static IEnumerable GetValues(object obj) + { + lock (obj) + { + for (int i = 0; i < 3; i++) + { + yield return i; + + if (i == 1) + { + yield break; + } + } + } + } + """; + + var expectedOutput = """ + Before: False + Inside: False + 0: True + 1: True + Done: False + After: False + """; + + CompileAndVerify(source, options: TestOptions.ReleaseExe.WithWarningLevel(8), + expectedOutput: expectedOutput).VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (24,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return i; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(24, 13) + }; + + CompileAndVerify(source, options: TestOptions.ReleaseExe.WithWarningLevel(9), + expectedOutput: expectedOutput).VerifyDiagnostics(expectedDiagnostics); + + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72443")] + public void YieldInLock_Nested() + { + var source = """ + using System.Collections.Generic; + + class C + { + IEnumerable M() + { + yield return 1; + lock (this) + { + yield return 2; + + local(); + + IEnumerable local() + { + yield return 3; + + lock (this) + { + yield return 4; + + yield break; + } + } + + yield break; + } + } + } + """; + + CreateCompilation(source).VerifyDiagnostics( + // (10,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return 2; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(10, 13), + // (20,21): warning CS9230: 'yield return' should not be used in the body of a lock statement + // yield return 4; + Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(20, 21)); + } + [WorkItem(546081, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546081")] [Fact] public void IteratorBlockWithUnreachableCode() diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 7e07986d321a0..1a83fa88bfbd7 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -470,6 +470,10 @@ public void WarningLevel_2() // These are the warnings introduced with the warning "wave" shipped with dotnet 8 and C# 12. Assert.Equal(8, ErrorFacts.GetWarningLevel(errorCode)); break; + case ErrorCode.WRN_BadYieldInLock: + // These are the warnings introduced with the warning "wave" shipped with dotnet 9 and C# 13. + Assert.Equal(9, ErrorFacts.GetWarningLevel(errorCode)); + break; default: // If a new warning is added, this test will fail // and whoever is adding the new warning will have to update it with the expected error level. From 8ff156fadd851f26b056ec7385d3ea0837a07a20 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 16 Apr 2024 12:38:27 +0200 Subject: [PATCH 003/423] Allow ref-like locals in iterators and async methods (#72664) * Allow ref-like locals in iterators and async methods * Return check of ref-like locals in async methods * Move declaration to block where it's used * Add todo for improving error message of ERR_SpecialByRefInLambda * Test `ref` in `await foreach` and iterator * Extend tests * Skip incompatible desktop tests * Test `yield break` in the new `lock` * Extend tests * Mark removed unreleased error code as available * Improve check for ref locals that can be hoisted * Return error `ERR_BadSpecialByRefIterator` * Extend tests * Uncapitalize feature name to be like others * Use better errors for refs across awaits * Improve errors for spilled ref locals across awaits * Replace remaining old error messages * Return wrongly removed fact condition * Extend tests * Report errors close to problematic usage where possible * Add more tests * Test foreach on ref local with iterator inside * Report spill local await boundary errors at their declaration * Revert accidentally changed test name * Test ref struct Current of async enumerator --- .../Portable/Binder/Binder_Expressions.cs | 12 +- .../Portable/Binder/Binder_Statements.cs | 10 +- .../CSharp/Portable/Binder/LockBinder.cs | 7 - .../Portable/Binder/UsingStatementBinder.cs | 2 +- .../CSharp/Portable/CSharpResources.resx | 26 +- .../CSharp/Portable/Errors/ErrorCode.cs | 10 +- .../CSharp/Portable/Errors/ErrorFacts.cs | 7 +- .../CSharp/Portable/Errors/MessageID.cs | 3 + .../IteratorAndAsyncCaptureWalker.cs | 43 +- .../Symbols/Source/SourceMethodSymbol.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 48 +- .../Portable/xlf/CSharpResources.de.xlf | 48 +- .../Portable/xlf/CSharpResources.es.xlf | 48 +- .../Portable/xlf/CSharpResources.fr.xlf | 48 +- .../Portable/xlf/CSharpResources.it.xlf | 48 +- .../Portable/xlf/CSharpResources.ja.xlf | 48 +- .../Portable/xlf/CSharpResources.ko.xlf | 48 +- .../Portable/xlf/CSharpResources.pl.xlf | 48 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 48 +- .../Portable/xlf/CSharpResources.ru.xlf | 48 +- .../Portable/xlf/CSharpResources.tr.xlf | 48 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 48 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 48 +- .../Emit/CodeGen/CodeGenAsyncIteratorTests.cs | 281 ++- .../Emit/CodeGen/CodeGenAsyncSpillTests.cs | 26 +- .../Emit/CodeGen/CodeGenAwaitForeachTests.cs | 230 +- .../Test/Emit2/Semantics/InlineArrayTests.cs | 1698 +++++++++++--- .../CSharp/Test/Emit2/Semantics/LockTests.cs | 2042 ++++++++++++++++- .../Test/Emit2/Semantics/OutVarTests.cs | 12 +- .../Semantics/AwaitExpressionTests.cs | 4 +- .../Semantic/Semantics/BindingAsyncTests.cs | 39 +- .../Test/Semantic/Semantics/ForEachTests.cs | 20 +- .../Test/Semantic/Semantics/LambdaTests.cs | 12 +- .../Semantic/Semantics/LocalFunctionTests.cs | 4 +- .../Semantic/Semantics/RefEscapingTests.cs | 82 +- .../Semantics/RefLocalsAndReturnsTests.cs | 187 +- .../Semantic/Semantics/SemanticErrorTests.cs | 34 +- .../Semantics/SpanStackSafetyTests.cs | 382 ++- .../Semantics/StackAllocInitializerTests.cs | 24 +- .../Semantics/TopLevelStatementsTests.cs | 21 +- .../Semantic/Semantics/UsingStatementTests.cs | 63 +- .../Test/Syntax/Diagnostics/DiagnosticTest.cs | 1 + .../CSharpLspBuildOnlyDiagnostics.cs | 3 +- 43 files changed, 4835 insertions(+), 1076 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index c577341e48531..d884574b41d96 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -3182,21 +3182,13 @@ private BoundExpression BindOutVariableDeclarationArgument( /// /// Reports an error when a bad special by-ref local was found. /// - internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax, ErrorCode errorCode = ErrorCode.ERR_BadSpecialByRefLocal) + internal static void CheckRestrictedTypeInAsyncMethod(Symbol containingSymbol, TypeSymbol type, BindingDiagnosticBag diagnostics, SyntaxNode syntax) { - Debug.Assert(errorCode is ErrorCode.ERR_BadSpecialByRefLocal or ErrorCode.ERR_BadSpecialByRefUsing or ErrorCode.ERR_BadSpecialByRefLock); if (containingSymbol.Kind == SymbolKind.Method && ((MethodSymbol)containingSymbol).IsAsync && type.IsRestrictedType()) { - if (errorCode == ErrorCode.ERR_BadSpecialByRefLock) - { - Error(diagnostics, errorCode, syntax); - } - else - { - Error(diagnostics, errorCode, syntax, type); - } + CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 9a453523f720a..faeeb28982488 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1170,15 +1170,9 @@ protected BoundLocalDeclaration BindVariableDeclaration( protected bool CheckRefLocalInAsyncOrIteratorMethod(SyntaxToken identifierToken, BindingDiagnosticBag diagnostics) { - if (IsInAsyncMethod()) - { - Error(diagnostics, ErrorCode.ERR_BadAsyncLocalType, identifierToken); - return true; - } - else if (IsDirectlyInIterator) + if (IsDirectlyInIterator || IsInAsyncMethod()) { - Error(diagnostics, ErrorCode.ERR_BadIteratorLocalType, identifierToken); - return true; + return !CheckFeatureAvailability(identifierToken, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics); } return false; diff --git a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs index c29d038662a25..0899da0cfdb69 100644 --- a/src/Compilers/CSharp/Portable/Binder/LockBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/LockBinder.cs @@ -64,13 +64,6 @@ internal override BoundStatement BindLockStatementParts(BindingDiagnosticBag dia _ = diagnostics.ReportUseSite(lockTypeInfo.EnterScopeMethod, exprSyntax) || diagnostics.ReportUseSite(lockTypeInfo.ScopeType, exprSyntax) || diagnostics.ReportUseSite(lockTypeInfo.ScopeDisposeMethod, exprSyntax); - - CheckRestrictedTypeInAsyncMethod( - originalBinder.ContainingMemberOrLambda, - lockTypeInfo.ScopeType, - diagnostics, - exprSyntax, - errorCode: ErrorCode.ERR_BadSpecialByRefLock); } BoundStatement stmt = originalBinder.BindPossibleEmbeddedStatement(_syntax.Statement, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs index 522d191faa3db..e35930b124152 100644 --- a/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs @@ -119,7 +119,7 @@ internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNo Debug.Assert(expressionOpt is not null); if (expressionOpt.Type is not null) { - CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax, errorCode: ErrorCode.ERR_BadSpecialByRefUsing); + CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax); } } else diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index bca68783ecce8..546daa5c1d4b1 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -3810,7 +3810,7 @@ Give the compiler some way to differentiate the methods. For example, you can gi __arglist is not allowed in the parameter list of async methods - 'await' cannot be used in an expression containing the type '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. Async methods cannot have pointer type parameters @@ -3851,15 +3851,12 @@ Give the compiler some way to differentiate the methods. For example, you can gi The 'async' modifier can only be used in methods that have a body. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Security attribute '{0}' cannot be applied to an Async method. @@ -5290,12 +5287,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot use ref local '{0}' inside an anonymous method, lambda expression, or query expression - - Iterators cannot have by-reference locals - - - Async methods cannot have by-reference locals - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. @@ -7851,9 +7842,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - Lock object @@ -7911,4 +7899,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 'yield return' should not be used in the body of a lock statement + + ref and unsafe in async and iterator methods + + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 08f0c3e6b45cf..b4fb86f47e14b 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1111,7 +1111,7 @@ internal enum ErrorCode ERR_NonTaskMainCantBeAsync = 4009, ERR_CantConvAsyncAnonFuncReturns = 4010, ERR_BadAwaiterPattern = 4011, - ERR_BadSpecialByRefLocal = 4012, + ERR_BadSpecialByRefParameter = 4012, ERR_SpecialByRefInLambda = 4013, WRN_UnobservedAwaitableExpression = 4014, ERR_SynchronizedAsyncMethod = 4015, @@ -1429,8 +1429,8 @@ internal enum ErrorCode ERR_RefAssignmentMustHaveIdentityConversion = 8173, ERR_ByReferenceVariableMustBeInitialized = 8174, ERR_AnonDelegateCantUseLocal = 8175, - ERR_BadIteratorLocalType = 8176, - ERR_BadAsyncLocalType = 8177, + // ERR_BadIteratorLocalType = 8176, + // ERR_BadAsyncLocalType = 8177, ERR_RefReturningCallAndAwait = 8178, #endregion diagnostics for ref locals and ref returns introduced in C# 7 @@ -2161,7 +2161,7 @@ internal enum ErrorCode ERR_UnscopedRefAttributeUnsupportedMemberTarget = 9101, ERR_UnscopedRefAttributeInterfaceImplementation = 9102, ERR_UnrecognizedRefSafetyRulesAttributeVersion = 9103, - ERR_BadSpecialByRefUsing = 9104, + // ERR_BadSpecialByRefUsing = 9104, ERR_InvalidPrimaryConstructorParameterReference = 9105, ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver = 9106, @@ -2286,7 +2286,7 @@ internal enum ErrorCode ERR_CollectionExpressionMissingAdd = 9215, WRN_ConvertingLock = 9216, - ERR_BadSpecialByRefLock = 9217, + ERR_RefLocalAcrossAwait = 9217, ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections = 9218, ERR_ParamsCollectionAmbiguousDynamicArgument = 9219, diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 2d0a6c799d702..4722401c000b1 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -626,6 +626,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_InterceptableMethodMustBeOrdinary: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYield: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait: + case ErrorCode.ERR_RefLocalAcrossAwait: // Update src\EditorFeatures\CSharp\LanguageServer\CSharpLspBuildOnlyDiagnostics.cs // whenever new values are added here. return true; @@ -1537,7 +1538,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_NonTaskMainCantBeAsync: case ErrorCode.ERR_CantConvAsyncAnonFuncReturns: case ErrorCode.ERR_BadAwaiterPattern: - case ErrorCode.ERR_BadSpecialByRefLocal: + case ErrorCode.ERR_BadSpecialByRefParameter: case ErrorCode.WRN_UnobservedAwaitableExpression: case ErrorCode.ERR_SynchronizedAsyncMethod: case ErrorCode.ERR_BadAsyncReturnExpression: @@ -1780,8 +1781,6 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_RefAssignmentMustHaveIdentityConversion: case ErrorCode.ERR_ByReferenceVariableMustBeInitialized: case ErrorCode.ERR_AnonDelegateCantUseLocal: - case ErrorCode.ERR_BadIteratorLocalType: - case ErrorCode.ERR_BadAsyncLocalType: case ErrorCode.ERR_PredefinedValueTupleTypeNotFound: case ErrorCode.ERR_SemiOrLBraceOrArrowExpected: case ErrorCode.ERR_NewWithTupleTypeSyntax: @@ -2333,7 +2332,6 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget: case ErrorCode.ERR_UnscopedRefAttributeInterfaceImplementation: case ErrorCode.ERR_UnrecognizedRefSafetyRulesAttributeVersion: - case ErrorCode.ERR_BadSpecialByRefUsing: case ErrorCode.ERR_InvalidPrimaryConstructorParameterReference: case ErrorCode.ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver: case ErrorCode.WRN_CapturedPrimaryConstructorParameterPassedToBase: @@ -2422,7 +2420,6 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_CollectionExpressionMissingConstructor: case ErrorCode.ERR_CollectionExpressionMissingAdd: case ErrorCode.WRN_ConvertingLock: - case ErrorCode.ERR_BadSpecialByRefLock: case ErrorCode.ERR_CantInferMethTypeArgs_DynamicArgumentWithParamsCollections: case ErrorCode.ERR_ParamsCollectionAmbiguousDynamicArgument: case ErrorCode.WRN_DynamicDispatchToParamsCollectionMethod: diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 72f876baaa909..eb580e98e2ac6 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -282,6 +282,8 @@ internal enum MessageID IDS_FeatureLockObject = MessageBase + 12841, IDS_FeatureParamsCollections = MessageBase + 12842, + + IDS_FeatureRefUnsafeInIteratorAsync = MessageBase + 12843, } // Message IDs may refer to strings that need to be localized. @@ -466,6 +468,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureImplicitIndexerInitializer: case MessageID.IDS_FeatureLockObject: case MessageID.IDS_FeatureParamsCollections: + case MessageID.IDS_FeatureRefUnsafeInIteratorAsync: return LanguageVersion.Preview; // C# 12.0 features. diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs index 91d75e4f81586..d8ce97fa4088f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs @@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// /// /// Data flow analysis is used to calculate the locals. At yield/await we mark all variables as "unassigned". - /// When a read from an unassigned variables is reported we add the variable to the captured set. + /// When a read from an unassigned variable is reported we add the variable to the captured set. /// "this" parameter is captured if a reference to "this", "base" or an instance field is encountered. /// Variables used in finally also need to be captured if there is a yield in the corresponding try block. /// @@ -76,19 +76,33 @@ public static OrderedSet Analyze(CSharpCompilation compilation, MethodSy foreach (var kvp in lazyDisallowedCaptures) { var variable = kvp.Key; - var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type; - if (variable is SynthesizedLocal local && local.SynthesizedKind == SynthesizedLocalKind.Spill) + if (variable is LocalSymbol local) { - Debug.Assert(local.TypeWithAnnotations.IsRestrictedType()); - diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, local.GetFirstLocation(), local.TypeWithAnnotations); + foreach (var syntax in kvp.Value) + { + if (local.TypeWithAnnotations.IsRestrictedType()) + { + // CS4007: Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, syntax.Location, local.TypeWithAnnotations); + } + else + { + Debug.Assert(local.RefKind != RefKind.None); + // CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + diagnostics.Add(ErrorCode.ERR_RefLocalAcrossAwait, syntax.Location); + } + } } else { - foreach (CSharpSyntaxNode syntax in kvp.Value) + var parameter = (ParameterSymbol)variable; + Debug.Assert(parameter.TypeWithAnnotations.IsRestrictedType()); + + foreach (var syntax in kvp.Value) { - // CS4013: Instance of type '{0}' cannot be used inside an anonymous function, query expression, iterator block or async method - diagnostics.Add(ErrorCode.ERR_SpecialByRefInLambda, syntax.Location, type); + // CS4007: Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + diagnostics.Add(ErrorCode.ERR_ByRefTypeAndAwait, syntax.Location, parameter.TypeWithAnnotations); } } } @@ -195,14 +209,23 @@ protected override ImmutableArray Scan(ref bool badRegion) private void CaptureVariable(Symbol variable, SyntaxNode syntax) { var type = (variable.Kind == SymbolKind.Local) ? ((LocalSymbol)variable).Type : ((ParameterSymbol)variable).Type; - if (type.IsRestrictedType()) + if (type.IsRestrictedType() || + (variable is LocalSymbol { RefKind: not RefKind.None } refLocal && !canRefLocalBeHoisted(refLocal))) { (_lazyDisallowedCaptures ??= new MultiDictionary()).Add(variable, syntax); } else { if (_variablesToHoist.Add(variable) && variable is LocalSymbol local && _boundRefLocalInitializers.TryGetValue(local, out var variableInitializer)) - CaptureRefInitializer(variableInitializer, syntax); + CaptureRefInitializer(variableInitializer, local.SynthesizedKind != SynthesizedLocalKind.UserDefined ? variableInitializer.Syntax : syntax); + } + + static bool canRefLocalBeHoisted(LocalSymbol refLocal) + { + return refLocal.SynthesizedKind == SynthesizedLocalKind.Spill || + (refLocal.SynthesizedKind == SynthesizedLocalKind.ForEachArray && + refLocal.Type.HasInlineArrayAttribute(out _) && + refLocal.Type.TryGetInlineArrayElementField() is not null); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs index a1771beb99a70..8cce2057e5200 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs @@ -72,7 +72,7 @@ internal void ReportAsyncParameterErrors(BindingDiagnosticBag diagnostics, Locat } else if (parameter.Type.IsRestrictedType()) { - diagnostics.Add(ErrorCode.ERR_BadSpecialByRefLocal, getLocation(parameter, location), parameter.Type); + diagnostics.Add(ErrorCode.ERR_BadSpecialByRefParameter, getLocation(parameter, location), parameter.Type); } } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 43a0b6a8deff2..fc8ee36929979 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -267,14 +267,14 @@ Člen záznamu {0} musí být čitelná vlastnost instance nebo pole typu {1}, která se bude shodovat s pozičním parametrem {2}. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Prostředek použitého příkazu typu {0} nejde použít v asynchronních metodách ani v asynchronních výrazech lambda. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Pole ref lze deklarovat pouze ve struktuře ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Levá strana přiřazení odkazu musí být parametr Ref. @@ -2347,6 +2352,11 @@ parametry ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator uvolněný operátor směny @@ -10540,8 +10550,8 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např - 'await' cannot be used in an expression containing the type '{0}' - 'Operátor await nejde použít ve výrazu, který obsahuje typ {0}. + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'Operátor await nejde použít ve výrazu, který obsahuje typ {0}. @@ -10609,16 +10619,6 @@ Poskytněte kompilátoru nějaký způsob, jak metody rozlišit. Můžete např Modifikátor async se dá použít jenom v metodách, které mají tělo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Parametry nebo lokální proměnné typu {0} nemůžou být deklarované v asynchronních metodách nebo asynchronních výrazech lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Výraz foreach nejde použít na enumerátorech typu {0} v asynchronních metodách nebo metodách iterátoru, protože {0} je struktura REF. - - Security attribute '{0}' cannot be applied to an Async method. Atribut zabezpečení {0} nejde použít pro metodu Async. @@ -11138,7 +11138,7 @@ Potlačení upozornění zvažte jenom v případě, když určitě nechcete če Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Instance typu {0} nelze použít uvnitř vnořené funkce, výrazu dotazu, bloku iterátoru nebo asynchronní metody. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Místní hodnotu odkazu {0} nejde použít uvnitř anonymní metody, výrazu lambda nebo výrazu dotazu. - - Iterators cannot have by-reference locals - Iterátory nemůžou mít lokální proměnné podle odkazu. - - - - Async methods cannot have by-reference locals - Asynchronní metody nemůžou mít lokální proměnné podle odkazu. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Odkaz vrácený voláním funkce {0} nelze zachovat v rámci hranice await nebo yield. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 5bf51a1f40385..d91808e6fc8ff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -267,14 +267,14 @@ Das Datensatzelement "{0}" muss eine lesbare Instanzeigenschaft oder ein Feld vom Typ "{1}" sein, um dem Positionsparameter "{2}" zu entsprechen. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Eine using-Anweisungsressource vom Typ „{0}“ kann nicht in asynchronen Methoden oder asynchronen Lambdaausdrücken verwendet werden. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Ein Verweisfeld kann nur in einer Verweisstruktur deklariert werden. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Die linke Seite einer Ref-Zuweisung muss eine Ref-Variable sein. @@ -2347,6 +2352,11 @@ ref readonly-Parameter + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator entspannter Schichtoperator @@ -10540,8 +10550,8 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz - 'await' cannot be used in an expression containing the type '{0}' - '"await" kann nicht in einem Ausdruck verwendet werden, der den Typ "{0}" enthält + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + '"await" kann nicht in einem Ausdruck verwendet werden, der den Typ "{0}" enthält @@ -10609,16 +10619,6 @@ Unterstützen Sie den Compiler bei der Unterscheidung zwischen den Methoden. Daz Der Modifizierer "async" kann nur in Methoden verwendet werden, die über einen Textkörper verfügen. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Parameter oder lokale Variablen des Typs "{0}" können nicht in asynchronen Methoden oder in asynchronen Lambdaausdrücken deklariert werden. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Die foreach-Anweisung kann nicht für Enumeratoren vom Typ "{0}" in asynchronen oder Iteratormethoden verwendet werden, weil "{0}" eine Referenzstruktur ist. - - Security attribute '{0}' cannot be applied to an Async method. Das Sicherheitsattribut "{0}" kann nicht auf eine Async-Methode angewendet werden. @@ -11138,7 +11138,7 @@ Sie sollten das Unterdrücken der Warnung nur in Betracht ziehen, wenn Sie siche Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Eine Instanz des Typs "{0}" kann nicht in einer geschachtelten Funktion, einem Abfrageausdruck, einem Iteratorblock oder einer Async-Methode verwendet werden. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Der lokale Verweis "{0}" kann nicht in einer anonymen Methode, einem Lambdaausdruck oder einem Abfrageausdruck verwendet werden. - - Iterators cannot have by-reference locals - Iteratoren dürfen keine lokalen by-reference-Elemente aufweisen. - - - - Async methods cannot have by-reference locals - Asynchrone Methoden dürfen keine lokalen by-reference-Elemente aufweisen. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Ein Verweis, der von einem Aufruf von "{0}" zurückgegeben wird, kann nicht über die Grenzen "await" oder "yield" hinweg beibehalten werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 169002cf621a1..519a716d29c04 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -267,14 +267,14 @@ El miembro de registro '{0}' debe ser una propiedad de instancia legible o un campo de tipo '{1}' para coincidir con el parámetro posicional '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Un recurso de instrucción using de tipo '{0}' no se puede usar en métodos asincrónicos ni expresiones lambda asincrónicas. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Un campo ref solo se puede declarar en una estructura ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. La parte izquierda de una asignación de referencias debe ser una variable local. @@ -2347,6 +2352,11 @@ parámetros ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operador de cambios relajado @@ -10540,8 +10550,8 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue - 'await' cannot be used in an expression containing the type '{0}' - 'await' no se puede usar en una expresión que contenga el tipo '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' no se puede usar en una expresión que contenga el tipo '{0}' @@ -10609,16 +10619,6 @@ Indique al compilador alguna forma de diferenciar los métodos. Por ejemplo, pue El modificador 'async' solo se puede usar en métodos que tengan un cuerpo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Los parámetros o locales de tipo '{0}' no pueden declararse en expresiones lambda o métodos asincrónicos. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - La instrucción foreach no puede funcionar en enumeradores de tipo "{0}" en métodos async o iterator porque "{0}" es una estructura ref. - - Security attribute '{0}' cannot be applied to an Async method. El atributo de seguridad '{0}' no se puede aplicar a un método Async. @@ -11138,7 +11138,7 @@ Considere la posibilidad de suprimir la advertencia solo si tiene la seguridad d Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - La instancia de tipo "{0}" no se puede usar dentro de una función anidada, una expresión de consulta, un bloque iterador ni un método asincrónico. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe No se puede usar la variable local de tipo ref '{0}' dentro de un método anónimo, una expresión lambda o una expresión de consulta. - - Iterators cannot have by-reference locals - Los iteradores no pueden tener variables locales por referencia. - - - - Async methods cannot have by-reference locals - Los métodos asincrónicos no pueden tener variables locales por referencia. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Una referencia devuelta por una llamada a '{0}' no se puede conservar a través del límite "await" o "yield". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 127c92447318d..ceb81446301bf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -267,14 +267,14 @@ Le membre d'enregistrement '{0}' doit être une propriété d'instance our champ lisible de type '{1}' pour correspondre au paramètre positionnel '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Une ressource d’instruction d’utilisation de type '{0}' ne peut pas être utilisée dans des méthodes asynchrones ou des expressions lambda asynchrones. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Un champ de référence ne peut être déclaré que dans une sructure de référence. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Le côté gauche d’une affectation ref doit être une variable ref. @@ -2347,6 +2352,11 @@ paramètres ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator opérateur shift souple @@ -10540,8 +10550,8 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve - 'await' cannot be used in an expression containing the type '{0}' - 'await' ne peut pas être utilisé dans une expression contenant le type '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' ne peut pas être utilisé dans une expression contenant le type '{0}' @@ -10609,16 +10619,6 @@ Permettez au compilateur de différencier les méthodes. Par exemple, vous pouve Le modificateur 'async' ne peut être utilisé que dans des méthodes ayant un corps. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Les paramètres ou variables locales de type '{0}' ne peuvent pas être déclarés dans des méthodes asynchrones ou des expressions asynchrones lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - L'instruction foreach ne peut pas fonctionner sur les énumérateurs de type '{0}' dans les méthodes asynchrones ou les méthodes d'itérateurs, car '{0}' est un struct par référence. - - Security attribute '{0}' cannot be applied to an Async method. Impossible d'appliquer l'attribut de sécurité '{0}' à une méthode Async. @@ -11138,7 +11138,7 @@ Supprimez l'avertissement seulement si vous êtes sûr de ne pas vouloir attendr Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Impossible d'utiliser une instance de type '{0}' dans une fonction imbriquée, une expression de requête, un bloc itérateur ou une méthode async + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Impossible d'utiliser ref local '{0}' dans une méthode anonyme, une expression lambda ou une expression de requête - - Iterators cannot have by-reference locals - Les itérateurs ne peuvent pas avoir de variables locales par référence - - - - Async methods cannot have by-reference locals - Les méthodes async ne peuvent pas avoir de variables locales par référence - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Une référence renvoyée par un appel à '{0}' ne peut pas être conservée à travers la limite 'wait' ou 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index af24bf98301b5..f1568d3b31829 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -267,14 +267,14 @@ Il membro di record '{0}' deve essere una proprietà di istanza leggibile o campo di tipo '{1}' per corrispondere al parametro posizionale '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Non è possibile usare una risorsa di istruzione using di tipo '{0}' in metodi asincroni o espressioni lambda asincrone. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Un campo ref può essere dichiarato solo in uno struct ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. La parte sinistra di un'assegnazione ref deve essere una variabile ref. @@ -2347,6 +2352,11 @@ parametri di sola lettura ref + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operatore di spostamento rilassato @@ -10540,8 +10550,8 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn - 'await' cannot be used in an expression containing the type '{0}' - 'non è possibile usare 'await' in un'espressione contenente il tipo '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'non è possibile usare 'await' in un'espressione contenente il tipo '{0}' @@ -10609,16 +10619,6 @@ Impostare il compilatore in modo tale da distinguere i metodi, ad esempio assegn Il modificatore 'async' può essere usato solo nei metodi con un corpo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Non è possibile dichiarare parametri o variabili locali di tipo '{0}' in metodi asincroni o espressioni lambda asincrone. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - L'istruzione foreach non può funzionare con enumeratori di tipo '{0}' in metodi async o iterator perché '{0}' è uno struct ref. - - Security attribute '{0}' cannot be applied to an Async method. Non è possibile applicare l'attributo di sicurezza '{0}' a un metodo Async @@ -11138,7 +11138,7 @@ Come procedura consigliata, è consigliabile attendere sempre la chiamata. Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - L'istanza di tipo '{0}' non può essere usata all'interno di una funzione annidata, un'espressione di query, un blocco iteratore o un metodo asincrono + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Non è possibile usare la variabile locale ref '{0}' in un metodo anonimo, in un'espressione lambda o in un'espressione di query - - Iterators cannot have by-reference locals - Gli iteratori non possono includere variabili locali per riferimento - - - - Async methods cannot have by-reference locals - I metodi Async non possono includere variabili locali per riferimento - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Non è possibile mantenere un riferimento restituito da una chiamata a '{0}' oltre il limite 'await' o 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 0f77641625862..e98478c3c0f65 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -267,14 +267,14 @@ レコード メンバー '{0}' は、位置指定パラメーター '{2}' に一致させるための型 '{1}' の読み取り可能なインスタンス プロパティまたはフィールドである必要があります。 - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - 型 '{0}' の using ステートメント リソースは、非同期メソッドまたは非同期ラムダ式では使用できません。 + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ ref フィールドは ref 構造体でのみ宣言できます。 + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref 代入の左辺は ref 変数である必要があります。 @@ -2347,6 +2352,11 @@ ref readonly パラメーター + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 緩和されたシフト演算子 @@ -10540,8 +10550,8 @@ C# では out と ref を区別しますが、CLR では同じと認識します - 'await' cannot be used in an expression containing the type '{0}' - 'await' は、型 '{0}' を含む式では使用できません + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' は、型 '{0}' を含む式では使用できません @@ -10609,16 +10619,6 @@ C# では out と ref を区別しますが、CLR では同じと認識します async' 修飾子は、本体があるメソッドでのみ使用できます。 - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - '{0}' 型のパラメーターまたはローカルは、非同期メソッドまたは非同期ラムダ式で宣言することができません。 - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - '{0}' は ref 構造体であるため、非同期または反復子のメソッド内で型 '{0}' の列挙子に対して foreach ステートメントは機能しません。 - - Security attribute '{0}' cannot be applied to an Async method. セキュリティ属性 '{0}' を非同期メソッドに適用することはできません。 @@ -11138,7 +11138,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - 型 '{0}' のインスタンスは、入れ子になった関数、クエリ式、反復子ブロック、または非同期メソッドの中では使用できません + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 匿名メソッド、ラムダ式、クエリ式内で ref ローカル変数 '{0}' は使用できません - - Iterators cannot have by-reference locals - 反復子は参照渡しのローカル変数を持つことができません - - - - Async methods cannot have by-reference locals - 非同期メソッドは参照渡しのローカル変数を持つことができません - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. '{0}' への呼び出しによって返された参照は、'await' または 'yield' 境界を越えて保持することはできません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index fd7b9c8c83b4d..7e87768b4bf62 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -267,14 +267,14 @@ 위치 매개 변수 '{0}'과(와) 일치하려면 레코드 멤버 '{1}'이(가) 유형 '{2}'의 읽을 수 있는 인스턴스 속성 또는 필드여야 합니다. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - '{0}' 형식의 using 문 리소스는 비동기 메서드 또는 비동기 람다 식에 사용할 수 없습니다. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ ref 필드는 ref 구조체에서만 선언할 수 있습니다. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref 할당의 왼쪽은 ref 변수여야 합니다. @@ -2347,6 +2352,11 @@ ref readonly 매개 변수 + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 완화된 시프트 연산자 @@ -10540,8 +10550,8 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 - 'await' cannot be used in an expression containing the type '{0}' - 'await'는 '{0}' 형식이 포함된 식에 사용할 수 없습니다. + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await'는 '{0}' 형식이 포함된 식에 사용할 수 없습니다. @@ -10609,16 +10619,6 @@ C#에서는 out과 ref를 구분하지만 CLR에서는 동일한 것으로 간 async' 한정자는 본문이 있는 메서드에서만 사용할 수 있습니다. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - '{0}' 형식의 매개 변수 또는 로컬은 비동기 메서드나 비동기 람다 식에서 선언할 수 없습니다. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - '{0}'은(는) ref struct이므로 비동기 또는 반복기 메서드의 '{0}' 형식 열거자에서 foreach 문을 수행할 수 없습니다. - - Security attribute '{0}' cannot be applied to an Async method. '{0}' 보안 특성은 비동기 메서드에 적용할 수 없습니다. @@ -11138,7 +11138,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - '{0}' 형식의 인스턴스는 중첩된 함수, 쿼리 식, 반복기 블록 또는 비동기 메서드 내에서 사용할 수 없습니다. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 무명 메서드, 람다 식 또는 쿼리 식에는 참조 로컬 '{0}'을(를) 사용할 수 없습니다. - - Iterators cannot have by-reference locals - 반복기에 by-reference 로컬을 사용할 수 없습니다. - - - - Async methods cannot have by-reference locals - 비동기 메서드에 by-reference 로컬을 사용할 수 없습니다. - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. '{0}'에 대한 호출로 반환된 참조는 'await' 또는 'yield' 경계에서 보존할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 4092f9b5e67c5..7dcc59fdbd7f6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -267,14 +267,14 @@ Składowa rekordu "{0}" musi być możliwą do odczytu właściwością wystąpienia typu "{1}", aby dopasować parametr pozycyjny "{2}". - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Zasobu instrukcji przy użyciu typu '{0}' nie można używać w metodach asynchronicznych ani asynchronicznych wyrażeniach lambda. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Pole referencyjne można zadeklarować tylko w strukturze referencyjnej. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Lewa strona przypisania referencyjnego musi być zmienną referencyjną. @@ -2347,6 +2352,11 @@ parametry tylko do odczytu ref + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operator swobodnej zmiany @@ -10540,8 +10550,8 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada - 'await' cannot be used in an expression containing the type '{0}' - 'Operatora „await” nie można użyć w wyrażeniu zawierającym typ „{0}” + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'Operatora „await” nie można użyć w wyrażeniu zawierającym typ „{0}” @@ -10609,16 +10619,6 @@ Musisz umożliwić kompilatorowi rozróżnienie metod. Możesz na przykład nada Modyfikatora „async” można używać tylko w metodach mających treść. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Parametrów ani elementów lokalnych typu „{0}” nie można deklarować w metodach asynchronicznych ani wyrażeniach lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Instrukcja foreach nie może działać na modułach wyliczających typu „{0}” w metodach asynchronicznych lub iteratora, ponieważ element „{0}” jest strukturą ref. - - Security attribute '{0}' cannot be applied to an Async method. Atrybutu zabezpieczeń „{0}” nie można zastosować dla metody asynchronicznej. @@ -11138,7 +11138,7 @@ Pominięcie ostrzeżenia należy wziąć pod uwagę tylko w sytuacji, gdy na pew Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Wystąpienia typu „{0}” nie można użyć wewnątrz funkcji zagnieżdżonej, wyrażenia zapytania, bloku iteratora ani metody asynchronicznej + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Nie można użyć zmiennej lokalnej typu ref „{0}” wewnątrz metody anonimowej, wyrażenia lambda ani wyrażenia zapytania - - Iterators cannot have by-reference locals - Iteratory nie mogą mieć zmiennych lokalnych dostępnych przez odwołanie - - - - Async methods cannot have by-reference locals - Metody asynchroniczne nie mogą mieć zmiennych lokalnych dostępnych przez odwołanie - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Odwołanie zwrócone przez wywołanie „{0}” nie może zostać zachowane w granicach „await” lub „yield”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 06cd639492e03..578dd52d42e00 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -267,14 +267,14 @@ O membro do registro '{0}' precisa ser uma propriedade de instância legível ou campo do tipo '{1}' para corresponder ao parâmetro posicional '{2}'. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Um recurso de instrução using do tipo "{0}" não pode ser usado em métodos assíncronos ou expressões lambda assíncronas. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Um campo ref só pode ser declarado em uma estrutura ref. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. O lado esquerdo de uma atribuição ref deve ser uma variável ref. @@ -2347,6 +2352,11 @@ parâmetros ref readonly + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator operador de deslocamento flexível @@ -10540,8 +10550,8 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc - 'await' cannot be used in an expression containing the type '{0}' - 'aguardar' não pode ser usado em uma expressão que contém o tipo '{0}' + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'aguardar' não pode ser usado em uma expressão que contém o tipo '{0}' @@ -10609,16 +10619,6 @@ Forneça ao compilador alguma forma de diferenciar os métodos. Por exemplo, voc O modificador 'async' só pode ser usado em métodos que têm um corpo. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Os parâmetros ou locais do tipo '{0}' não podem ser declarados nos métodos async ou expressões async lambda. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - a instrução foreach não pode operar em enumeradores do tipo '{0}' em métodos assíncronos ou iteradores porque '{0}' é uma struct de referência. - - Security attribute '{0}' cannot be applied to an Async method. Atributo de segurança "{0}" não pode ser aplicado a um método Assíncrono. @@ -11138,7 +11138,7 @@ Você pode suprimir o aviso se tiver certeza de que não vai querer aguardar a c Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - A instância do tipo '{0}' não pode ser usada dentro de uma função aninhada, expressão de consulta, bloco de iteradores ou método assíncrono + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Não é possível usar a referência local '{0}' em um método anônimo, expressão lambda ou expressão de consulta - - Iterators cannot have by-reference locals - Os iteradores não podem ter locais por referência - - - - Async methods cannot have by-reference locals - Os métodos assíncronos não podem ter locais por referência - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Uma referência retornada por uma chamada para '{0}' não pode ser preservada no limite 'await' ou 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 954057392e614..619ac7320a17b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -267,14 +267,14 @@ Элемент записи "{0}" должен быть доступным для чтения свойством экземпляра или полем типа "{1}", чтобы соответствовать позиционному параметру "{2}". - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - Ресурс оператора использования типа "{0}" нельзя применять в асинхронных методах или асинхронных лямбда-выражениях. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Поле ссылки может быть объявлено только в структуре ссылки. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. Левая сторона назначения ref должна быть переменной ref. @@ -2347,6 +2352,11 @@ Параметры ref, доступные только для чтения + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator нестрогий оператор сдвига @@ -10541,8 +10551,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - 'await' cannot be used in an expression containing the type '{0}' - '"await" нельзя использовать в выражении, содержащем тип "{0}" + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + '"await" нельзя использовать в выражении, содержащем тип "{0}" @@ -10610,16 +10620,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi Модификатор "async" можно использовать только в методах, имеющих тело. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Параметры или локальные переменные типа "{0}" не могут объявляться в асинхронных методах и в асинхронных лямбда-выражениях. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - Оператор foreach нельзя использовать с перечислителями типа "{0}" в методах с модификатором Async или Iterator, так как "{0}" является ссылочной структурой. - - Security attribute '{0}' cannot be applied to an Async method. Атрибут безопасности "{0}" нельзя применить к асинхронному методу. @@ -11139,7 +11139,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - Экземпляр типа "{0}" нельзя использовать внутри вложенной функции, выражения запроса, блока итератора или асинхронного метода. + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12450,16 +12450,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Невозможно использовать локальную переменную ref "{0}" внутри анонимного метода, лямбда-выражения или выражения запроса - - Iterators cannot have by-reference locals - Итераторы не могут иметь локальных переменных по ссылке - - - - Async methods cannot have by-reference locals - Асинхронные методы не могут иметь локальных переменных по ссылке - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. Ссылка, возвращенная вызовом ' {0} ', не может быть сохранена за границей 'wait' или 'yield'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index a44ca7bfed87c..981f7741495aa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -267,14 +267,14 @@ {0} kayıt üyesi, {1} konumsal parametresi ile eşleşmesi için {2} türünde okunabilir bir örnek özelliği veya alan olmalıdır. - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - '{0}' türündeki bir using deyimi kaynağı, asenkron yöntemlerde veya asenkron lambda ifadelerinde kullanılamaz. + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ Başvuru alanı yalnızca başvuru yapısında bildirilebilir. + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref atamasının sol tarafı, ref değişkeni olmalıdır. @@ -2347,6 +2352,11 @@ ref salt okunur parametreleri + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator esnek kaydırma işleci @@ -10540,8 +10550,8 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl - 'await' cannot be used in an expression containing the type '{0}' - 'await', '{0}' türünü içeren bir ifadede kullanılamaz + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await', '{0}' türünü içeren bir ifadede kullanılamaz @@ -10609,16 +10619,6 @@ Derleyiciye yöntemleri ayrıştırma yolu verin. Örneğin, bunlara farklı adl Async' değiştiricisi yalnızca gövdesi olan metotlarda kullanılabilir. - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - Zaman uyumsuz yöntemlerde veya zaman uyumsuz lambda ifadelerinde '{0}' türündeki parametreler veya yerel öğeler bildirilemez. - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - '{0}' bir başvuru yapısı olduğundan foreach deyimi, async veya iterator metotlarındaki '{0}' türü numaralandırıcılar üzerinde çalışamaz. - - Security attribute '{0}' cannot be applied to an Async method. '{0}' güvenlik özniteliği bir Async yöntemine uygulanamaz. @@ -11138,7 +11138,7 @@ Yalnızca asenkron çağrının tamamlanmasını beklemek istemediğinizden ve Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - '{0}' türünün örneği iç içe geçmiş bir işlevde, sorgu ifadesinde, yineleyici bloğunda veya zaman uyumsuz bir metotta kullanılamaz + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T '{0}' ref yerel değeri bir anonim metotta, lambda ifadesinde veya sorgu ifadesinde kullanılamaz - - Iterators cannot have by-reference locals - Yineleyiciler başvuruya göre yerel değerlere sahip olamaz - - - - Async methods cannot have by-reference locals - Zaman uyumsuz metotlar başvuruya göre yerel değerlere sahip olamaz - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. '{0}' hedefine bir çağrı tarafından döndürülen başvuru, 'await' veya 'yield' sınırında korunamıyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 3c164338d3da3..5aceeb57b426c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -267,14 +267,14 @@ 记录成员 '{0}' 必须为类型 '{1}' 的可读实例属性或字段,以匹配位置参数 '{2}'。 - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - 无法在异步方法或异步 lambda 表达式中使用类型为“{0}”的 using 语句资源。 + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ ref 字段只能在 ref 结构中声明。 + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. ref 赋值的左侧必须为 ref 变量。 @@ -2347,6 +2352,11 @@ ref readonly 参数 + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 移位运算符 @@ -10540,8 +10550,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - 'await' cannot be used in an expression containing the type '{0}' - '“等待”不能在包含“{0}”类型的表达式中使用 + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + '“等待”不能在包含“{0}”类型的表达式中使用 @@ -10609,16 +10619,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi 只能在具有正文的方法中使用 "async" 修饰符。 - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - 不能在异步方法或异步 lambda 表达式中声明类型“{0}”的参数或局部变量。 - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - foreach 语句无法在类型“{0}”的枚举器上使用异步或迭代器方法操作,因为“{0}”是 ref 结构。 - - Security attribute '{0}' cannot be applied to an Async method. 安全特性“{0}”不可应用于异步方法。 @@ -11138,7 +11138,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - “{0}”类型的实例不能在嵌套函数、查询表达式、迭代器块或异步方法中使用 + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 不能在匿名方法、lambda 表达式或查询表达式内使用 ref 局部变量“{0}” - - Iterators cannot have by-reference locals - 迭代器不能有按引用局部变量 - - - - Async methods cannot have by-reference locals - 异步方法不能有按引用局部变量 - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. 调用“{0}”所返回的引用不能跨 "await" 或 "yield" 边界保留。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 30d0776683b21..3b36564d36e33 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -267,14 +267,14 @@ 記錄成員 '{0}' 必須是類型 '{1}' 的可讀取執行個體屬性或欄位,才能符合位置參數 '{2}'。 - - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. + foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - - A using statement resource of type '{0}' cannot be used in async methods or async lambda expressions. - 類型 '{0}' 的 using 陳述式資源不能用於非同步方法或非同步 Lambda 運算式。 + + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. + Parameters of type '{0}' cannot be declared in async methods or async lambda expressions. @@ -1712,6 +1712,11 @@ ref 欄位只能在 ref 結構中宣告。 + + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + The left-hand side of a ref assignment must be a ref variable. 參考指派的左側必須為 ref 變數。 @@ -2347,6 +2352,11 @@ ref readonly 參數 + + ref and unsafe in async and iterator methods + ref and unsafe in async and iterator methods + + relaxed shift operator 寬鬆移位 (Shift) 運算子 @@ -10540,8 +10550,8 @@ Give the compiler some way to differentiate the methods. For example, you can gi - 'await' cannot be used in an expression containing the type '{0}' - 'await' 不得用於包含類型 '{0}' 的運算式中 + Instance of type '{0}' cannot be preserved across 'await' or 'yield' boundary. + 'await' 不得用於包含類型 '{0}' 的運算式中 @@ -10609,16 +10619,6 @@ Give the compiler some way to differentiate the methods. For example, you can gi async' 修飾元只可用於具有主體的方法。 - - Parameters or locals of type '{0}' cannot be declared in async methods or async lambda expressions. - 類型 '{0}' 的參數或區域變數,不可在非同步方法或非同步 Lambda 運算式中宣告。 - - - - foreach statement cannot operate on enumerators of type '{0}' in async or iterator methods because '{0}' is a ref struct. - foreach 陳述式無法對 async 或 iterator 方法中類型 '{0}' 的列舉值進行操作,因為 '{0}' 為 ref struct。 - - Security attribute '{0}' cannot be applied to an Async method. 安全屬性 '{0}' 無法套用至非同步方法。 @@ -11138,7 +11138,7 @@ You should consider suppressing the warning only if you're sure that you don't w Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method - 類型 '{0}' 的執行個體不可用於巢狀函式、查詢運算式、迭代區塊或非同步方法中 + Instance of type '{0}' cannot be used inside a nested function, query expression, iterator block or async method @@ -12449,16 +12449,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 無法在匿名方法、Lambda 運算式或查詢運算式中使用參考本機 '{0}' - - Iterators cannot have by-reference locals - Iterator 不可有 by-reference local - - - - Async methods cannot have by-reference locals - 非同步方法不可有 by-reference local - - A reference returned by a call to '{0}' cannot be preserved across 'await' or 'yield' boundary. 對 '{0}' 之呼叫所傳回的參考無法在 'await' 或 'yield' 界限間保留。 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs index 778817985654a..da3d964d5a24a 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs @@ -133,7 +133,7 @@ private static void VerifyMissingType(string source, WellKnownType type, params // Instrumentation to investigate CI failure: https://github.com/dotnet/roslyn/issues/34207 private CSharpCompilation CreateCompilationWithAsyncIterator(string source, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) => CreateCompilationWithTasksExtensions(new[] { (CSharpTestSource)CSharpTestBase.Parse(source, filename: "source", parseOptions), CSharpTestBase.Parse(AsyncStreamsTypes, filename: "AsyncStreamsTypes", parseOptions) }, - options: options, parseOptions: parseOptions); + options: options); private CSharpCompilation CreateCompilationWithAsyncIterator(CSharpTestSource source, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) => CreateCompilationWithTasksExtensions(new[] { source, AsyncStreamsTypes }, options: options, parseOptions: parseOptions); @@ -631,15 +631,288 @@ static async System.Threading.Tasks.Task Main() ref struct S { }"; + + var expectedDiagnostics = new[] + { + // source(4,65): error CS0306: The type 'S' may not be used as a type argument + // static async System.Collections.Generic.IAsyncEnumerable M() + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("S").WithLocation(4, 65) + }; + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( // source(4,65): error CS0306: The type 'S' may not be used as a type argument // static async System.Collections.Generic.IAsyncEnumerable M() Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("S").WithLocation(4, 65), - // source(11,24): error CS4012: Parameters or locals of type 'S' cannot be declared in async methods or async lambda expressions. + // source(11,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await foreach (var s in M()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("S").WithLocation(11, 24) - ); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 24)); + } + + [Fact] + public void RefStructElementType_NonGeneric() + { + string source = """ + using System.Threading.Tasks; + + class C + { + public E GetAsyncEnumerator() => new E(); + static async Task Main() + { + await foreach (var s in new C()) + { + System.Console.Write(s.F); + } + } + } + class E + { + bool _done; + public S Current => new S { F = 123 }; + public async Task MoveNextAsync() + { + await Task.Yield(); + return !_done ? (_done = true) : false; + } + } + ref struct S + { + public int F; + } + """; + + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // source(8,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 24)); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 238 (0xee) + .maxstack 3 + .locals init (int V_0, + S V_1, //s + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.
d__1 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0016 + IL_0012: br.s IL_0082 + IL_0014: br.s IL_0082 + IL_0016: nop + IL_0017: nop + IL_0018: ldarg.0 + IL_0019: newobj "C..ctor()" + IL_001e: call "E C.GetAsyncEnumerator()" + IL_0023: stfld "E C.
d__1.<>s__1" + IL_0028: br.s IL_0044 + IL_002a: ldarg.0 + IL_002b: ldfld "E C.
d__1.<>s__1" + IL_0030: callvirt "S E.Current.get" + IL_0035: stloc.1 + IL_0036: nop + IL_0037: ldloc.1 + IL_0038: ldfld "int S.F" + IL_003d: call "void System.Console.Write(int)" + IL_0042: nop + IL_0043: nop + IL_0044: ldarg.0 + IL_0045: ldfld "E C.
d__1.<>s__1" + IL_004a: callvirt "System.Threading.Tasks.Task E.MoveNextAsync()" + IL_004f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0054: stloc.2 + IL_0055: ldloca.s V_2 + IL_0057: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_005c: brtrue.s IL_009e + IL_005e: ldarg.0 + IL_005f: ldc.i4.0 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int C.
d__1.<>1__state" + IL_0067: ldarg.0 + IL_0068: ldloc.2 + IL_0069: stfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__1.<>u__1" + IL_006e: ldarg.0 + IL_006f: stloc.3 + IL_0070: ldarg.0 + IL_0071: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__1.<>t__builder" + IL_0076: ldloca.s V_2 + IL_0078: ldloca.s V_3 + IL_007a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.
d__1>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.
d__1)" + IL_007f: nop + IL_0080: leave.s IL_00ed + IL_0082: ldarg.0 + IL_0083: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__1.<>u__1" + IL_0088: stloc.2 + IL_0089: ldarg.0 + IL_008a: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.
d__1.<>u__1" + IL_008f: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0095: ldarg.0 + IL_0096: ldc.i4.m1 + IL_0097: dup + IL_0098: stloc.0 + IL_0099: stfld "int C.
d__1.<>1__state" + IL_009e: ldarg.0 + IL_009f: ldloca.s V_2 + IL_00a1: call "bool System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_00a6: stfld "bool C.
d__1.<>s__2" + IL_00ab: ldarg.0 + IL_00ac: ldfld "bool C.
d__1.<>s__2" + IL_00b1: brtrue IL_002a + IL_00b6: ldarg.0 + IL_00b7: ldnull + IL_00b8: stfld "E C.
d__1.<>s__1" + IL_00bd: leave.s IL_00d9 + } + catch System.Exception + { + IL_00bf: stloc.s V_4 + IL_00c1: ldarg.0 + IL_00c2: ldc.i4.s -2 + IL_00c4: stfld "int C.
d__1.<>1__state" + IL_00c9: ldarg.0 + IL_00ca: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__1.<>t__builder" + IL_00cf: ldloc.s V_4 + IL_00d1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_00d6: nop + IL_00d7: leave.s IL_00ed + } + IL_00d9: ldarg.0 + IL_00da: ldc.i4.s -2 + IL_00dc: stfld "int C.
d__1.<>1__state" + IL_00e1: ldarg.0 + IL_00e2: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__1.<>t__builder" + IL_00e7: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_00ec: nop + IL_00ed: ret + } + """); + } + + [Fact] + public void RefStructElementType_NonGeneric_AwaitAfter() + { + string source = """ + using System.Threading.Tasks; + + class C + { + public E GetAsyncEnumerator() => new E(); + static async Task Main() + { + await foreach (var s in new C()) + { + System.Console.Write(s.F); + await Task.Yield(); + } + } + } + class E + { + bool _done; + public S Current => new S { F = 123 }; + public async Task MoveNextAsync() + { + await Task.Yield(); + return !_done ? (_done = true) : false; + } + } + ref struct S + { + public int F; + } + """; + + var comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // source(8,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 24)); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilationWithAsyncIterator(source, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: "123", verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void RefStructElementType_NonGeneric_AwaitBefore() + { + string source = """ + using System.Threading.Tasks; + + class C + { + public E GetAsyncEnumerator() => new E(); + static async Task Main() + { + await foreach (var s in new C()) + { + await Task.Yield(); + System.Console.Write(s.F); + } + } + } + class E + { + bool _done; + public S Current => new S { F = 123 }; + public async Task MoveNextAsync() + { + await Task.Yield(); + return !_done ? (_done = true) : false; + } + } + ref struct S + { + public int F; + } + """; + + var comp = CreateCompilationWithAsyncIterator(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // source(8,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 24)); + + var expectedDiagnostics = new[] + { + // source(11,34): error CS4007: Instance of type 'S' cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(s.F); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s.F").WithArguments("S").WithLocation(11, 34) + }; + + comp = CreateCompilationWithAsyncIterator(source, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithAsyncIterator(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs index 0a7f649ff7d64..999bf6667331d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncSpillTests.cs @@ -3789,9 +3789,11 @@ static async Task Main() var comp = CreateCompilationWithMscorlibAndSpan(source, options: options); comp.VerifyDiagnostics(); comp.VerifyEmitDiagnostics( - // (9,66): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' - // await Async1(F1(), G(F2(), stackalloc int[] { 1, 2, 3 }, await F3())); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await F3()").WithArguments("System.Span").WithLocation(9, 66) + // (8,5): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // { + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"{ + await Async1(F1(), G(F2(), stackalloc int[] { 1, 2, 3 }, await F3())); + }").WithArguments("System.Span").WithLocation(8, 5) ); } } @@ -3897,16 +3899,16 @@ public ref struct S public bool P2 => true; } "; - CreateCompilation(source, options: TestOptions.DebugDll).VerifyDiagnostics().VerifyEmitDiagnostics( - // (9,17): error CS4013: Instance of type 'S' cannot be used inside a nested function, query expression, iterator block or async method - // Q { F: { P1: true } } when await c => r, // error: cached Q.F is alive - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "F").WithArguments("S").WithLocation(9, 17) - ); - CreateCompilation(source, options: TestOptions.ReleaseDll).VerifyDiagnostics().VerifyEmitDiagnostics( - // (9,17): error CS4013: Instance of type 'S' cannot be used inside a nested function, query expression, iterator block or async method + + var expectedDiagnostics = new[] + { + // (9,17): error CS4007: Instance of type 'S' cannot be preserved across 'await' or 'yield' boundary. // Q { F: { P1: true } } when await c => r, // error: cached Q.F is alive - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "F").WithArguments("S").WithLocation(9, 17) - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "F").WithArguments("S").WithLocation(9, 17) + }; + + CreateCompilation(source, options: TestOptions.DebugDll).VerifyDiagnostics().VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.ReleaseDll).VerifyDiagnostics().VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs index bce2519fce4d0..9a099be01840b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAwaitForeachTests.cs @@ -1695,12 +1695,26 @@ public System.Threading.Tasks.Task MoveNextAsync() => throw null; public int Current { get => throw null; } } -}"; - var comp = CreateCompilationWithTasksExtensions(source + s_IAsyncEnumerable); +}" + s_IAsyncEnumerable; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); comp.VerifyDiagnostics( - // (6,32): error CS8177: Async methods cannot have by-reference locals + // (6,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // await foreach (ref var i in new C()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "i").WithLocation(6, 32)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 32)); + + var expectedDiagnostics = new[] + { + // (6,37): error CS1510: A ref or out value must be an assignable variable + // await foreach (ref var i in new C()) + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new C()").WithLocation(6, 37) + }; + + comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithTasksExtensions(source); + comp.VerifyDiagnostics(expectedDiagnostics); var tree = comp.SyntaxTrees.Single(); var model = (SyntaxTreeSemanticModel)comp.GetSemanticModel(tree, ignoreAccessibility: false); @@ -1708,6 +1722,119 @@ public System.Threading.Tasks.Task MoveNextAsync() Assert.Equal(default, model.GetForEachStatementInfo(foreachSyntax)); } + [Theory, CombinatorialData] + public void TestWithPattern_Ref_Iterator([CombinatorialValues(" ", "readonly")] string modifier) + { + var source = $$""" + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + class C + { + static async Task Main() + { + await foreach (int i in F()) + { + Console.Write(i); + } + } + + static async IAsyncEnumerable F() + { + await foreach (ref {{modifier}} var i in new C()) + { + yield return i; + } + } + + public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) => new(); + + public sealed class Enumerator + { + private readonly int[] _array = [1, 2, 3]; + private int _index = -1; + public Task MoveNextAsync() + { + if (_index < _array.Length) _index++; + return Task.FromResult(_index < _array.Length); + } + public ref int Current => ref _array[_index]; + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (17,41): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref readonly var i in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(17, 41)); + + var expectedOutput = "123"; + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularNext); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void TestWithPattern_Ref_Iterator_Used() + { + var source = """ + using System.Collections.Generic; + using System.Threading.Tasks; + + class C + { + static async IAsyncEnumerable F() + { + await foreach (ref var i in new C()) + { + yield return i; + M(ref i); + } + } + + static void M(ref int i) { } + + public Enumerator GetAsyncEnumerator(System.Threading.CancellationToken token = default) => new(); + + public sealed class Enumerator + { + private readonly int[] _array = [1, 2, 3]; + private int _index = -1; + public Task MoveNextAsync() + { + if (_index < _array.Length) _index++; + return Task.FromResult(_index < _array.Length); + } + public ref int Current => ref _array[_index]; + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (8,32): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (ref var i in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 32)); + + var expectedDiagnostics = new[] + { + // (11,19): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // M(ref i); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "i").WithLocation(11, 19) + }; + + comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + + comp = CreateCompilationWithTasksExtensions(source); + comp.VerifyEmitDiagnostics(expectedDiagnostics); + } + [Fact] public void TestWithPattern_PointerType() { @@ -1934,6 +2061,101 @@ public S(int i) CompileAndVerify(comp, expectedOutput: "1 2 Done"); } + [Fact] + public void TestWithPattern_RefStructEnumerator() + { + var source = """ + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var s in new C()) + { + } + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public ref struct Enumerator + { + public int Current => 0; + public Task MoveNextAsync() => throw null; + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,15): error CS8344: foreach statement cannot operate on enumerators of type 'C.Enumerator' in async or iterator methods because 'C.Enumerator' is a ref struct. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_BadSpecialByRefIterator, "foreach").WithArguments("C.Enumerator").WithLocation(6, 15) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void TestWithPattern_RefStructCurrent() + { + var source = """ + using System; + using System.Threading.Tasks; + public class C + { + public static async Task Main() + { + await foreach (var s in new C()) + { + Console.Write($"{s.ToString()} "); + } + Console.Write("Done"); + } + public Enumerator GetAsyncEnumerator() => new Enumerator(); + public sealed class Enumerator : IAsyncDisposable + { + int i = 0; + public S Current => new S(i); + public async Task MoveNextAsync() + { + i++; + return await Task.FromResult(i < 3); + } + public async ValueTask DisposeAsync() + { + await Task.Yield(); + } + } + } + public ref struct S + { + int i; + public S(int i) + { + this.i = i; + } + public override string ToString() => i.ToString(); + } + """ + s_IAsyncEnumerable; + + var expectedDiagnostics = new[] + { + // (7,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // await foreach (var s in new C()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 24) + }; + + CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + + var expectedOutput = "1 2 Done"; + + var comp = CreateCompilationWithTasksExtensions(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify).VerifyDiagnostics(); + } + [Fact] public void TestWithPattern_RefReturningCurrent() { diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs index 1963a7532e9f2..3c90e1f58fc4f 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/InlineArrayTests.cs @@ -4691,9 +4691,12 @@ static async Task FromResult(T r) "; var comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( - // (24,22): error CS4007: 'await' cannot be used in an expression containing the type 'System.ReadOnlySpan>' - // [Get01()][await FromResult(Get02(x))]; - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await FromResult(Get02(x))").WithArguments("System.ReadOnlySpan>").WithLocation(24, 22) + // (20,12): error CS4007: Instance of type 'System.ReadOnlySpan>' cannot be preserved across 'await' or 'yield' boundary. + // => MemoryMarshal.CreateReadOnlySpan( + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As>, Buffer10>( + ref Unsafe.AsRef(in GetC(x).F)), + 10)").WithArguments("System.ReadOnlySpan>").WithLocation(20, 12) ); } @@ -4743,9 +4746,12 @@ static async Task FromResult(T r) "; var comp = CreateCompilation(src + Buffer10Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( - // (24,13): error CS4007: 'await' cannot be used in an expression containing the type 'System.ReadOnlySpan' - // [await FromResult(Get02(x))]; - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await FromResult(Get02(x))").WithArguments("System.ReadOnlySpan").WithLocation(24, 13) + // (20,12): error CS4007: Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary. + // => MemoryMarshal.CreateReadOnlySpan( + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, @"MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As, int>( + ref Unsafe.AsRef(in GetC(x).F[Get01()])), + 10)").WithArguments("System.ReadOnlySpan").WithLocation(20, 12) ); } @@ -20154,30 +20160,45 @@ .locals init (int V_0, CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); } - [Fact] + [ConditionalFact(typeof(CoreClrOnly))] public void Foreach_InAsync_03() { var src = @" class Program { - static async void Test() + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { + s_buffer[1] = 3; + foreach (ref int y in GetBuffer()) { + y *= y; + System.Console.Write(y); } await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } - static ref Buffer4 GetBuffer() => throw null; + static ref Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,26): error CS8177: Async methods cannot have by-reference locals +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // foreach (ref int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "y").WithLocation(6, 26) - ); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26)); + + var expectedOutput = "09009"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -20598,30 +20619,48 @@ .locals init (int V_0, CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); } - [Fact] + [ConditionalFact(typeof(CoreClrOnly))] public void Foreach_InAsync_07() { var src = @" class Program { - static async void Test() + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { + s_buffer[1] = 3; + + int i = 0; foreach (ref readonly int y in GetBuffer()) { + System.Console.Write(y); + s_buffer[i++]++; + System.Console.Write(y); + System.Console.Write(' '); } await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } - static ref readonly Buffer4 GetBuffer() => throw null; + static ref readonly Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,35): error CS8177: Async methods cannot have by-reference locals +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (11,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // foreach (ref readonly int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "y").WithLocation(6, 35) - ); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 35)); + + var expectedOutput = "01 34 01 01 4"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -20798,132 +20837,209 @@ .locals init (int V_0, } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_01() + public void Foreach_InAsync_10() { var src = @" class Program { - static private Buffer4 F = default; - private static int index = 0; + static Buffer4 s_buffer; - static void Main() + static async System.Threading.Tasks.Task Main() { - foreach (var a in Test()) - {} + s_buffer[1] = 3; + + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + } + + await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } - static System.Collections.Generic.IEnumerable Test() + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26), + // (11,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 26)); + + var expectedOutput = "09009"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InAsync_11() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { - yield return -1; + s_buffer[1] = 3; - foreach (var y in GetBuffer()) + foreach (ref int y in GetBuffer()) { - Increment(); - System.Console.Write(' '); + await System.Threading.Tasks.Task.Yield(); + y *= y; System.Console.Write(y); } - yield return -2; - } + await System.Threading.Tasks.Task.Yield(); - static ref Buffer4 GetBuffer() - { - System.Console.Write(-1); - return ref F; + System.Console.Write(s_buffer[1]); } - static void Increment() + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26)); + + var expectedDiagnostics = new[] + { + // (13,13): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(13, 13), + // (13,18): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(13, 18), + // (14,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(14, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_12() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { - index++; + s_buffer[1] = 3; - if (index < 4) + foreach (ref int y in GetBuffer()) { - F[index] = index; + y *= y; + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); } + + await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); } + + static ref Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; - verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", -@" + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26)); + + var expectedDiagnostics = new[] + { + // (10,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + }").WithArguments("Program.GetBuffer()").WithLocation(10, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_13() + { + var src = @" +class Program { - // Code size 126 (0x7e) - .maxstack 2 - .locals init (int V_0, - Buffer4& V_1, - int V_2) - IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__3.<>1__state"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: switch ( - IL_001b, - IL_0032, - IL_0075) - IL_0019: ldc.i4.0 - IL_001a: ret - IL_001b: ldarg.0 - IL_001c: ldc.i4.m1 - IL_001d: stfld ""int Program.d__3.<>1__state"" - IL_0022: ldarg.0 - IL_0023: ldc.i4.m1 - IL_0024: stfld ""int Program.d__3.<>2__current"" - IL_0029: ldarg.0 - IL_002a: ldc.i4.1 - IL_002b: stfld ""int Program.d__3.<>1__state"" - IL_0030: ldc.i4.1 - IL_0031: ret - IL_0032: ldarg.0 - IL_0033: ldc.i4.m1 - IL_0034: stfld ""int Program.d__3.<>1__state"" - IL_0039: call ""ref Buffer4 Program.GetBuffer()"" - IL_003e: stloc.1 - IL_003f: ldc.i4.0 - IL_0040: stloc.2 - IL_0041: br.s IL_0060 - IL_0043: ldloc.1 - IL_0044: ldloc.2 - IL_0045: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" - IL_004a: ldind.i4 - IL_004b: call ""void Program.Increment()"" - IL_0050: ldc.i4.s 32 - IL_0052: call ""void System.Console.Write(char)"" - IL_0057: call ""void System.Console.Write(int)"" - IL_005c: ldloc.2 - IL_005d: ldc.i4.1 - IL_005e: add - IL_005f: stloc.2 - IL_0060: ldloc.2 - IL_0061: ldc.i4.4 - IL_0062: blt.s IL_0043 - IL_0064: ldarg.0 - IL_0065: ldc.i4.s -2 - IL_0067: stfld ""int Program.d__3.<>2__current"" - IL_006c: ldarg.0 - IL_006d: ldc.i4.2 - IL_006e: stfld ""int Program.d__3.<>1__state"" - IL_0073: ldc.i4.1 - IL_0074: ret - IL_0075: ldarg.0 - IL_0076: ldc.i4.m1 - IL_0077: stfld ""int Program.d__3.<>1__state"" - IL_007c: ldc.i4.0 - IL_007d: ret + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() + { + s_buffer[1] = 3; + + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + } + + await System.Threading.Tasks.Task.Yield(); + + System.Console.Write(s_buffer[1]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; } -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (10,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 26), + // (11,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 26)); + + var expectedDiagnostics = new[] + { + // (11,31): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "buffer").WithLocation(11, 31) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } - [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_02() + [Fact] + public void Foreach_InAsync_14() { var src = @" +using System.Threading.Tasks; + class C { - public Buffer4 F = default; + public readonly Buffer4 F = default; } class Program @@ -20933,19 +21049,20 @@ class Program static void Main() { - foreach (var a in Test(c)) - {} + Test(c).Wait(); } - static System.Collections.Generic.IEnumerable Test(C x) + static async Task Test(C x) { - foreach (var y in x.F) + ref readonly Buffer4 f = ref x.F; + foreach (var y in f) { Increment(); System.Console.Write(' '); System.Console.Write(y); - yield return -1; + await Task.Yield(); + await Task.Delay(2); } } @@ -20955,146 +21072,266 @@ static void Increment() if (index < 4) { - c.F[index] = index; + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; } } } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; - verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", -@" + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (21,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(21, 35)); + + var expectedDiagnostics = new[] + { + // (22,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(22, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_15() + { + var src = @" +class Program { - // Code size 151 (0x97) - .maxstack 3 - .locals init (int V_0) - IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__3.<>1__state"" - IL_0006: stloc.0 - IL_0007: ldloc.0 - IL_0008: brfalse.s IL_0010 - IL_000a: ldloc.0 - IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0070 - IL_000e: ldc.i4.0 - IL_000f: ret - IL_0010: ldarg.0 - IL_0011: ldc.i4.m1 - IL_0012: stfld ""int Program.d__3.<>1__state"" - IL_0017: ldarg.0 - IL_0018: ldarg.0 - IL_0019: ldfld ""C Program.d__3.x"" - IL_001e: stfld ""C Program.d__3.<>7__wrap2"" - IL_0023: ldarg.0 - IL_0024: ldfld ""C Program.d__3.<>7__wrap2"" - IL_0029: ldfld ""Buffer4 C.F"" - IL_002e: pop - IL_002f: ldarg.0 - IL_0030: ldc.i4.0 - IL_0031: stfld ""int Program.d__3.<>7__wrap1"" - IL_0036: br.s IL_0085 - IL_0038: ldarg.0 - IL_0039: ldfld ""C Program.d__3.<>7__wrap2"" - IL_003e: ldflda ""Buffer4 C.F"" - IL_0043: ldarg.0 - IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" - IL_0049: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" - IL_004e: ldind.i4 - IL_004f: call ""void Program.Increment()"" - IL_0054: ldc.i4.s 32 - IL_0056: call ""void System.Console.Write(char)"" - IL_005b: call ""void System.Console.Write(int)"" - IL_0060: ldarg.0 - IL_0061: ldc.i4.m1 - IL_0062: stfld ""int Program.d__3.<>2__current"" - IL_0067: ldarg.0 - IL_0068: ldc.i4.1 - IL_0069: stfld ""int Program.d__3.<>1__state"" - IL_006e: ldc.i4.1 - IL_006f: ret - IL_0070: ldarg.0 - IL_0071: ldc.i4.m1 - IL_0072: stfld ""int Program.d__3.<>1__state"" - IL_0077: ldarg.0 - IL_0078: ldarg.0 - IL_0079: ldfld ""int Program.d__3.<>7__wrap1"" - IL_007e: ldc.i4.1 - IL_007f: add - IL_0080: stfld ""int Program.d__3.<>7__wrap1"" - IL_0085: ldarg.0 - IL_0086: ldfld ""int Program.d__3.<>7__wrap1"" - IL_008b: ldc.i4.4 - IL_008c: blt.s IL_0038 - IL_008e: ldarg.0 - IL_008f: ldnull - IL_0090: stfld ""C Program.d__3.<>7__wrap2"" - IL_0095: ldc.i4.0 - IL_0096: ret + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() + { + foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + } + + await System.Threading.Tasks.Task.Yield(); + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; } -"); - comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (8,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + await System.Threading.Tasks.Task.Yield(); + }").WithArguments("Program.GetBuffer()").WithLocation(8, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] - public void Foreach_InIterator_03() + public void Foreach_InAsync_16() { var src = @" class Program { - static System.Collections.Generic.IEnumerable Test() + static Buffer4 s_buffer; + + static async System.Threading.Tasks.Task Main() { - foreach (ref int y in GetBuffer()) + foreach (ref readonly int y in GetBuffer()) { + await System.Threading.Tasks.Task.Yield(); + System.Console.Write(y); } - yield return -1; + await System.Threading.Tasks.Task.Yield(); } - static ref Buffer4 GetBuffer() => throw null; + static ref readonly Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,26): error CS8176: Iterators cannot have by-reference locals - // foreach (ref int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "y").WithLocation(6, 26) - ); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (11,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(11, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InAsync_17() + { + var src = @" +using System.Threading.Tasks; + +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + Test(c).Wait(); + } + + static async Task Test(C x) + { + foreach (ref readonly int y in x.F) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + await Task.Yield(); + await Task.Delay(2); + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +" + Buffer4Definition; + var expectedOutput = " 0 1 2 3"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); } [Fact] - public void Foreach_InIterator_04() + public void Foreach_InAsync_18() { var src = @" +using System.Threading.Tasks; + +class C +{ + public readonly Buffer4 F = default; +} + class Program { - static System.Collections.Generic.IEnumerable Test() + static async Task Test(C x) { - foreach (int y in GetBuffer()) + foreach (ref readonly int y in x.F) { - yield return -1; + await Task.Yield(); + System.Console.Write(y); } } +} +" + Buffer4Definition; - static ref Buffer4 GetBuffer() => throw null; + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (13,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in x.F) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 35)); + + var expectedDiagnostics = new[] + { + // (16,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(16, 34) + }; + + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InAsync_19() + { + var src = @" +using System.Threading.Tasks; + +class C +{ + public readonly Buffer4 F = default; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyEmitDiagnostics( - // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. - // foreach (int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, - @"foreach (int y in GetBuffer()) +class Program +{ + static async Task Test(C x) + { + ref readonly Buffer4 f = ref x.F; + + foreach (var i in f) System.Console.Write(i); + + foreach (var y in f) { - yield return -1; - }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) - ); + System.Console.Write(y); + await Task.Yield(); + } + + foreach (var j in f) System.Console.Write(j); + + foreach (var z in f) + { + System.Console.Write(z); + await Task.Yield(); + } + } +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (13,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 35)); + + var expectedDiagnostics = new[] + { + // (17,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(17, 27), + // (23,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var j in f) System.Console.Write(j); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(23, 27), + // (25,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var z in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(25, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_05() + public void Foreach_InIterator_01() { var src = @" class Program @@ -21122,7 +21359,7 @@ static System.Collections.Generic.IEnumerable Test() yield return -2; } - static ref readonly Buffer4 GetBuffer() + static ref Buffer4 GetBuffer() { System.Console.Write(-1); return ref F; @@ -21174,14 +21411,14 @@ .locals init (int V_0, IL_0032: ldarg.0 IL_0033: ldc.i4.m1 IL_0034: stfld ""int Program.d__3.<>1__state"" - IL_0039: call ""ref readonly Buffer4 Program.GetBuffer()"" + IL_0039: call ""ref Buffer4 Program.GetBuffer()"" IL_003e: stloc.1 IL_003f: ldc.i4.0 IL_0040: stloc.2 IL_0041: br.s IL_0060 IL_0043: ldloc.1 IL_0044: ldloc.2 - IL_0045: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_0045: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" IL_004a: ldind.i4 IL_004b: call ""void Program.Increment()"" IL_0050: ldc.i4.s 32 @@ -21214,12 +21451,12 @@ .locals init (int V_0, } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_06() + public void Foreach_InIterator_02() { var src = @" class C { - public readonly Buffer4 F = default; + public Buffer4 F = default; } class Program @@ -21251,7 +21488,7 @@ static void Increment() if (index < 4) { - System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + c.F[index] = index; } } } @@ -21295,7 +21532,7 @@ .locals init (int V_0) IL_003e: ldflda ""Buffer4 C.F"" IL_0043: ldarg.0 IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" - IL_0049: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_0049: call ""ref int .InlineArrayElementRef, int>(ref Buffer4, int)"" IL_004e: ldind.i4 IL_004f: call ""void Program.Increment()"" IL_0054: ldc.i4.s 32 @@ -21333,34 +21570,57 @@ .locals init (int V_0) CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); } - [Fact] - public void Foreach_InIterator_07() + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_03() { var src = @" class Program { + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + static System.Collections.Generic.IEnumerable Test() { - foreach (ref readonly int y in GetBuffer()) + foreach (ref int y in GetBuffer()) { + y *= y; + System.Console.Write(y); } yield return -1; + + System.Console.Write(s_buffer[2]); } - static ref readonly Buffer4 GetBuffer() => throw null; + static ref Buffer4 GetBuffer() => ref s_buffer; } -"; - var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); - comp.VerifyDiagnostics( - // (6,35): error CS8176: Iterators cannot have by-reference locals - // foreach (ref readonly int y in GetBuffer()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "y").WithLocation(6, 35) - ); +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26)); + + var expectedOutput = "0090-19"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] - public void Foreach_InIterator_08() + public void Foreach_InIterator_04() { var src = @" class Program @@ -21373,7 +21633,7 @@ static System.Collections.Generic.IEnumerable Test() } } - static ref readonly Buffer4 GetBuffer() => throw null; + static ref Buffer4 GetBuffer() => throw null; } "; var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); @@ -21390,11 +21650,14 @@ static System.Collections.Generic.IEnumerable Test() } [ConditionalFact(typeof(CoreClrOnly))] - public void Foreach_InIterator_09() + public void Foreach_InIterator_05() { var src = @" class Program { + static private Buffer4 F = default; + private static int index = 0; + static void Main() { foreach (var a in Test()) @@ -21403,92 +21666,929 @@ static void Main() static System.Collections.Generic.IEnumerable Test() { + yield return -1; + foreach (var y in GetBuffer()) { + Increment(); System.Console.Write(' '); System.Console.Write(y); - yield return -1; } + + yield return -2; } - static Buffer4 GetBuffer() + static ref readonly Buffer4 GetBuffer() { - Buffer4 x = default; - x[0] = 111; - x[1] = 112; - x[2] = 113; - x[3] = 114; - System.Console.Write(-1); - return x; + return ref F; + } + + static void Increment() + { + index++; + + if (index < 4) + { + F[index] = index; + } } } "; var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); - var verifier = CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); - verifier.VerifyIL("Program.d__1.System.Collections.IEnumerator.MoveNext", + verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", @" { - // Code size 121 (0x79) + // Code size 126 (0x7e) + .maxstack 2 + .locals init (int V_0, + Buffer4& V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: ldfld ""int Program.d__3.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: switch ( + IL_001b, + IL_0032, + IL_0075) + IL_0019: ldc.i4.0 + IL_001a: ret + IL_001b: ldarg.0 + IL_001c: ldc.i4.m1 + IL_001d: stfld ""int Program.d__3.<>1__state"" + IL_0022: ldarg.0 + IL_0023: ldc.i4.m1 + IL_0024: stfld ""int Program.d__3.<>2__current"" + IL_0029: ldarg.0 + IL_002a: ldc.i4.1 + IL_002b: stfld ""int Program.d__3.<>1__state"" + IL_0030: ldc.i4.1 + IL_0031: ret + IL_0032: ldarg.0 + IL_0033: ldc.i4.m1 + IL_0034: stfld ""int Program.d__3.<>1__state"" + IL_0039: call ""ref readonly Buffer4 Program.GetBuffer()"" + IL_003e: stloc.1 + IL_003f: ldc.i4.0 + IL_0040: stloc.2 + IL_0041: br.s IL_0060 + IL_0043: ldloc.1 + IL_0044: ldloc.2 + IL_0045: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_004a: ldind.i4 + IL_004b: call ""void Program.Increment()"" + IL_0050: ldc.i4.s 32 + IL_0052: call ""void System.Console.Write(char)"" + IL_0057: call ""void System.Console.Write(int)"" + IL_005c: ldloc.2 + IL_005d: ldc.i4.1 + IL_005e: add + IL_005f: stloc.2 + IL_0060: ldloc.2 + IL_0061: ldc.i4.4 + IL_0062: blt.s IL_0043 + IL_0064: ldarg.0 + IL_0065: ldc.i4.s -2 + IL_0067: stfld ""int Program.d__3.<>2__current"" + IL_006c: ldarg.0 + IL_006d: ldc.i4.2 + IL_006e: stfld ""int Program.d__3.<>1__state"" + IL_0073: ldc.i4.1 + IL_0074: ret + IL_0075: ldarg.0 + IL_0076: ldc.i4.m1 + IL_0077: stfld ""int Program.d__3.<>1__state"" + IL_007c: ldc.i4.0 + IL_007d: ret +} +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "-1 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_06() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + foreach (var a in Test(c)) + {} + } + + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (var y in x.F) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + yield return -1; + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + + verifier.VerifyIL("Program.d__3.System.Collections.IEnumerator.MoveNext", +@" +{ + // Code size 151 (0x97) .maxstack 3 .locals init (int V_0) IL_0000: ldarg.0 - IL_0001: ldfld ""int Program.d__1.<>1__state"" + IL_0001: ldfld ""int Program.d__3.<>1__state"" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: brfalse.s IL_0010 IL_000a: ldloc.0 IL_000b: ldc.i4.1 - IL_000c: beq.s IL_0059 + IL_000c: beq.s IL_0070 IL_000e: ldc.i4.0 IL_000f: ret IL_0010: ldarg.0 IL_0011: ldc.i4.m1 - IL_0012: stfld ""int Program.d__1.<>1__state"" + IL_0012: stfld ""int Program.d__3.<>1__state"" IL_0017: ldarg.0 - IL_0018: call ""Buffer4 Program.GetBuffer()"" - IL_001d: stfld ""Buffer4 Program.d__1.<>7__wrap1"" - IL_0022: ldarg.0 - IL_0023: ldc.i4.0 - IL_0024: stfld ""int Program.d__1.<>7__wrap2"" - IL_0029: br.s IL_006e - IL_002b: ldarg.0 - IL_002c: ldflda ""Buffer4 Program.d__1.<>7__wrap1"" - IL_0031: ldarg.0 - IL_0032: ldfld ""int Program.d__1.<>7__wrap2"" - IL_0037: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" - IL_003c: ldind.i4 - IL_003d: ldc.i4.s 32 - IL_003f: call ""void System.Console.Write(char)"" - IL_0044: call ""void System.Console.Write(int)"" - IL_0049: ldarg.0 - IL_004a: ldc.i4.m1 - IL_004b: stfld ""int Program.d__1.<>2__current"" - IL_0050: ldarg.0 - IL_0051: ldc.i4.1 - IL_0052: stfld ""int Program.d__1.<>1__state"" - IL_0057: ldc.i4.1 - IL_0058: ret - IL_0059: ldarg.0 - IL_005a: ldc.i4.m1 - IL_005b: stfld ""int Program.d__1.<>1__state"" + IL_0018: ldarg.0 + IL_0019: ldfld ""C Program.d__3.x"" + IL_001e: stfld ""C Program.d__3.<>7__wrap2"" + IL_0023: ldarg.0 + IL_0024: ldfld ""C Program.d__3.<>7__wrap2"" + IL_0029: ldfld ""Buffer4 C.F"" + IL_002e: pop + IL_002f: ldarg.0 + IL_0030: ldc.i4.0 + IL_0031: stfld ""int Program.d__3.<>7__wrap1"" + IL_0036: br.s IL_0085 + IL_0038: ldarg.0 + IL_0039: ldfld ""C Program.d__3.<>7__wrap2"" + IL_003e: ldflda ""Buffer4 C.F"" + IL_0043: ldarg.0 + IL_0044: ldfld ""int Program.d__3.<>7__wrap1"" + IL_0049: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_004e: ldind.i4 + IL_004f: call ""void Program.Increment()"" + IL_0054: ldc.i4.s 32 + IL_0056: call ""void System.Console.Write(char)"" + IL_005b: call ""void System.Console.Write(int)"" IL_0060: ldarg.0 - IL_0061: ldarg.0 - IL_0062: ldfld ""int Program.d__1.<>7__wrap2"" - IL_0067: ldc.i4.1 - IL_0068: add - IL_0069: stfld ""int Program.d__1.<>7__wrap2"" - IL_006e: ldarg.0 - IL_006f: ldfld ""int Program.d__1.<>7__wrap2"" - IL_0074: ldc.i4.4 - IL_0075: blt.s IL_002b - IL_0077: ldc.i4.0 - IL_0078: ret + IL_0061: ldc.i4.m1 + IL_0062: stfld ""int Program.d__3.<>2__current"" + IL_0067: ldarg.0 + IL_0068: ldc.i4.1 + IL_0069: stfld ""int Program.d__3.<>1__state"" + IL_006e: ldc.i4.1 + IL_006f: ret + IL_0070: ldarg.0 + IL_0071: ldc.i4.m1 + IL_0072: stfld ""int Program.d__3.<>1__state"" + IL_0077: ldarg.0 + IL_0078: ldarg.0 + IL_0079: ldfld ""int Program.d__3.<>7__wrap1"" + IL_007e: ldc.i4.1 + IL_007f: add + IL_0080: stfld ""int Program.d__3.<>7__wrap1"" + IL_0085: ldarg.0 + IL_0086: ldfld ""int Program.d__3.<>7__wrap1"" + IL_008b: ldc.i4.4 + IL_008c: blt.s IL_0038 + IL_008e: ldarg.0 + IL_008f: ldnull + IL_0090: stfld ""C Program.d__3.<>7__wrap2"" + IL_0095: ldc.i4.0 + IL_0096: ret } "); comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); - CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + CompileAndVerify(comp, expectedOutput: " 0 1 2 3", verify: Verification.Fails).VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_07() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + int i = 0; + foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + s_buffer[i++]++; + System.Console.Write(y); + System.Console.Write(' '); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (19,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(19, 35)); + + var expectedOutput = "01 01 34 01 -14"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InIterator_08() + { + var src = @" +class Program +{ + static System.Collections.Generic.IEnumerable Test() + { + foreach (int y in GetBuffer()) + { + yield return -1; + } + } + + static ref readonly Buffer4 GetBuffer() => throw null; +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + + comp.VerifyEmitDiagnostics( + // (6,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, + @"foreach (int y in GetBuffer()) + { + yield return -1; + }").WithArguments("Program.GetBuffer()").WithLocation(6, 9) + ); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_09() + { + var src = @" +class Program +{ + static void Main() + { + foreach (var a in Test()) + {} + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (var y in GetBuffer()) + { + System.Console.Write(' '); + System.Console.Write(y); + yield return -1; + } + } + + static Buffer4 GetBuffer() + { + Buffer4 x = default; + x[0] = 111; + x[1] = 112; + x[2] = 113; + x[3] = 114; + + System.Console.Write(-1); + return x; + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + + verifier.VerifyIL("Program.d__1.System.Collections.IEnumerator.MoveNext", +@" +{ + // Code size 121 (0x79) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""int Program.d__1.<>1__state"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0059 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld ""int Program.d__1.<>1__state"" + IL_0017: ldarg.0 + IL_0018: call ""Buffer4 Program.GetBuffer()"" + IL_001d: stfld ""Buffer4 Program.d__1.<>7__wrap1"" + IL_0022: ldarg.0 + IL_0023: ldc.i4.0 + IL_0024: stfld ""int Program.d__1.<>7__wrap2"" + IL_0029: br.s IL_006e + IL_002b: ldarg.0 + IL_002c: ldflda ""Buffer4 Program.d__1.<>7__wrap1"" + IL_0031: ldarg.0 + IL_0032: ldfld ""int Program.d__1.<>7__wrap2"" + IL_0037: call ""ref readonly int .InlineArrayElementRefReadOnly, int>(in Buffer4, int)"" + IL_003c: ldind.i4 + IL_003d: ldc.i4.s 32 + IL_003f: call ""void System.Console.Write(char)"" + IL_0044: call ""void System.Console.Write(int)"" + IL_0049: ldarg.0 + IL_004a: ldc.i4.m1 + IL_004b: stfld ""int Program.d__1.<>2__current"" + IL_0050: ldarg.0 + IL_0051: ldc.i4.1 + IL_0052: stfld ""int Program.d__1.<>1__state"" + IL_0057: ldc.i4.1 + IL_0058: ret + IL_0059: ldarg.0 + IL_005a: ldc.i4.m1 + IL_005b: stfld ""int Program.d__1.<>1__state"" + IL_0060: ldarg.0 + IL_0061: ldarg.0 + IL_0062: ldfld ""int Program.d__1.<>7__wrap2"" + IL_0067: ldc.i4.1 + IL_0068: add + IL_0069: stfld ""int Program.d__1.<>7__wrap2"" + IL_006e: ldarg.0 + IL_006f: ldfld ""int Program.d__1.<>7__wrap2"" + IL_0074: ldc.i4.4 + IL_0075: blt.s IL_002b + IL_0077: ldc.i4.0 + IL_0078: ret +} +"); + comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "-1 111 112 113 114").VerifyDiagnostics(); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_10() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26), + // (19,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(19, 26)); + + var expectedOutput = "0090-19"; + + CompileAndVerify(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InIterator_11() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref int y in GetBuffer()) + { + yield return 1; + y *= y; + System.Console.Write(y); + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26)); + + var expectedDiagnostics = new[] + { + // (21,13): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(21, 13), + // (21,18): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y *= y; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(21, 18), + // (22,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(22, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_12() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + yield return 1; + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26)); + + var expectedDiagnostics = new[] + { + // (18,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref int y in GetBuffer()) + { + y *= y; + System.Console.Write(y); + yield return 1; + }").WithArguments("Program.GetBuffer()").WithLocation(18, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_13() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static void Main() + { + s_buffer[2] = 3; + + foreach (int x in Test()) + { + System.Console.Write(x); + } + } + + static System.Collections.Generic.IEnumerable Test() + { + ref Buffer4 buffer = ref GetBuffer(); + foreach (ref int y in buffer) + { + y *= y; + System.Console.Write(y); + yield return 1; + } + + yield return -1; + + System.Console.Write(s_buffer[2]); + } + + static ref Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (18,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref Buffer4 buffer = ref GetBuffer(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "buffer").WithArguments("ref and unsafe in async and iterator methods").WithLocation(18, 26), + // (19,26): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(19, 26)); + + var expectedDiagnostics = new[] + { + // (19,31): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref int y in buffer) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "buffer").WithLocation(19, 31) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_14() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + foreach (var a in Test(c)) + {} + } + + static System.Collections.Generic.IEnumerable Test(C x) + { + ref readonly Buffer4 f = ref x.F; + foreach (var y in f) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + yield return -1; + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (20,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(20, 35)); + + var expectedDiagnostics = new[] + { + // (21,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(21, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_15() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + yield return 1; + } + + yield return -1; + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (8,9): error CS8178: A reference returned by a call to 'Program.GetBuffer()' cannot be preserved across 'await' or 'yield' boundary. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, @"foreach (ref readonly int y in GetBuffer()) + { + System.Console.Write(y); + yield return 1; + }").WithArguments("Program.GetBuffer()").WithLocation(8, 9) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_16() + { + var src = @" +class Program +{ + static Buffer4 s_buffer; + + static System.Collections.Generic.IEnumerable Test() + { + foreach (ref readonly int y in GetBuffer()) + { + yield return 1; + System.Console.Write(y); + } + + yield return -1; + } + + static ref readonly Buffer4 GetBuffer() => ref s_buffer; +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (8,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in GetBuffer()) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 35)); + + var expectedDiagnostics = new[] + { + // (11,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(11, 34) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void Foreach_InIterator_17() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + private static C c = new C(); + private static int index = 0; + + static void Main() + { + foreach (var a in Test(c)) + {} + } + + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (ref readonly int y in x.F) + { + Increment(); + System.Console.Write(' '); + System.Console.Write(y); + + yield return -1; + } + } + + static void Increment() + { + index++; + + if (index < 4) + { + System.Runtime.CompilerServices.Unsafe.AsRef(in c.F)[index] = index; + } + } +} +" + Buffer4Definition; + var expectedOutput = " 0 1 2 3"; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + comp = CreateCompilation(src, targetFramework: TargetFramework.Net80, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Fails).VerifyDiagnostics(); + } + + [Fact] + public void Foreach_InIterator_18() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + static System.Collections.Generic.IEnumerable Test(C x) + { + foreach (ref readonly int y in x.F) + { + yield return -1; + System.Console.Write(y); + } + } +} +" + Buffer4Definition; + + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (11,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // foreach (ref readonly int y in x.F) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "y").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 35)); + + var expectedDiagnostics = new[] + { + // (14,34): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // System.Console.Write(y); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(14, 34) + }; + + CreateCompilation(src, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Foreach_InIterator_19() + { + var src = @" +class C +{ + public readonly Buffer4 F = default; +} + +class Program +{ + static System.Collections.Generic.IEnumerable Test(C x) + { + ref readonly Buffer4 f = ref x.F; + + foreach (var i in f) System.Console.Write(i); + + foreach (var y in f) + { + System.Console.Write(y); + yield return -1; + } + + foreach (var j in f) System.Console.Write(j); + + foreach (var z in f) + { + System.Console.Write(z); + yield return -2; + } + } +} +" + Buffer4Definition; + + CreateCompilation(src, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( + // (11,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly Buffer4 f = ref x.F; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "f").WithArguments("ref and unsafe in async and iterator methods").WithLocation(11, 35)); + + var expectedDiagnostics = new[] + { + // (15,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var y in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(15, 27), + // (21,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var j in f) System.Console.Write(j); + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(21, 27), + // (23,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var z in f) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "f").WithLocation(23, 27) + }; + + CreateCompilation(src, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(expectedDiagnostics); } [ConditionalFact(typeof(CoreClrOnly))] diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs index a28521e1fd683..deb134acef381 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs @@ -1861,9 +1861,6 @@ public void Await() } """; CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (4,7): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(4, 7), // (6,5): error CS1996: Cannot await in the body of a lock statement // await Task.Yield(); Diagnostic(ErrorCode.ERR_BadAwaitInLock, "await Task.Yield()").WithLocation(6, 5)); @@ -1875,19 +1872,137 @@ public void AsyncMethod() var source = """ #pragma warning disable 1998 // async method lacks 'await' operators using System.Threading; + using System.Threading.Tasks; class C { - async void M() + static async Task Main() { - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } } } """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (8,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(8, 15)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: nop + IL_0008: newobj "System.Threading.Lock..ctor()" + IL_000d: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0012: stloc.1 + .try + { + IL_0013: nop + IL_0014: ldstr "L" + IL_0019: call "void System.Console.Write(string)" + IL_001e: nop + IL_001f: nop + IL_0020: leave.s IL_002f + } + finally + { + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: bge.s IL_002e + IL_0026: ldloca.s V_1 + IL_0028: call "void System.Threading.Lock.Scope.Dispose()" + IL_002d: nop + IL_002e: endfinally + } + IL_002f: leave.s IL_0049 + } + catch System.Exception + { + IL_0031: stloc.2 + IL_0032: ldarg.0 + IL_0033: ldc.i4.s -2 + IL_0035: stfld "int C.
d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0040: ldloc.2 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0046: nop + IL_0047: leave.s IL_005d + } + IL_0049: ldarg.0 + IL_004a: ldc.i4.s -2 + IL_004c: stfld "int C.
d__0.<>1__state" + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0057: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_005c: nop + IL_005d: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: newobj "System.Threading.Lock..ctor()" + IL_000c: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0011: stloc.1 + .try + { + IL_0012: ldstr "L" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_002a + } + finally + { + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: bge.s IL_0029 + IL_0022: ldloca.s V_1 + IL_0024: call "void System.Threading.Lock.Scope.Dispose()" + IL_0029: endfinally + } + IL_002a: leave.s IL_0043 + } + catch System.Exception + { + IL_002c: stloc.2 + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -2 + IL_0030: stfld "int C.
d__0.<>1__state" + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_003b: ldloc.2 + IL_003c: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0041: leave.s IL_0056 + } + IL_0043: ldarg.0 + IL_0044: ldc.i4.s -2 + IL_0046: stfld "int C.
d__0.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0051: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0056: ret + } + """); } [Fact] @@ -1899,17 +2014,527 @@ public void AsyncMethod_WithAwait() class C { - async void M() + static async Task Main() { await Task.Yield(); - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } + await Task.Yield(); } } """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (9,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(9, 15)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 311 (0x137) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + C.
d__0 V_3, + System.Threading.Lock.Scope V_4, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_5, + System.Exception V_6) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0019 + IL_0012: br.s IL_0058 + IL_0014: br IL_00e1 + IL_0019: nop + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001f: stloc.2 + IL_0020: ldloca.s V_2 + IL_0022: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0027: stloc.1 + IL_0028: ldloca.s V_1 + IL_002a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0074 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int C.
d__0.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.1 + IL_003c: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0041: ldarg.0 + IL_0042: stloc.3 + IL_0043: ldarg.0 + IL_0044: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0049: ldloca.s V_1 + IL_004b: ldloca.s V_3 + IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_0052: nop + IL_0053: leave IL_0136 + IL_0058: ldarg.0 + IL_0059: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_005e: stloc.1 + IL_005f: ldarg.0 + IL_0060: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0065: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_006b: ldarg.0 + IL_006c: ldc.i4.m1 + IL_006d: dup + IL_006e: stloc.0 + IL_006f: stfld "int C.
d__0.<>1__state" + IL_0074: ldloca.s V_1 + IL_0076: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_007b: nop + IL_007c: newobj "System.Threading.Lock..ctor()" + IL_0081: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0086: stloc.s V_4 + .try + { + IL_0088: nop + IL_0089: ldstr "L" + IL_008e: call "void System.Console.Write(string)" + IL_0093: nop + IL_0094: nop + IL_0095: leave.s IL_00a4 + } + finally + { + IL_0097: ldloc.0 + IL_0098: ldc.i4.0 + IL_0099: bge.s IL_00a3 + IL_009b: ldloca.s V_4 + IL_009d: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a2: nop + IL_00a3: endfinally + } + IL_00a4: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00a9: stloc.2 + IL_00aa: ldloca.s V_2 + IL_00ac: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00b1: stloc.s V_5 + IL_00b3: ldloca.s V_5 + IL_00b5: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00ba: brtrue.s IL_00fe + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld "int C.
d__0.<>1__state" + IL_00c5: ldarg.0 + IL_00c6: ldloc.s V_5 + IL_00c8: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00cd: ldarg.0 + IL_00ce: stloc.3 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00d5: ldloca.s V_5 + IL_00d7: ldloca.s V_3 + IL_00d9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_00de: nop + IL_00df: leave.s IL_0136 + IL_00e1: ldarg.0 + IL_00e2: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00e7: stloc.s V_5 + IL_00e9: ldarg.0 + IL_00ea: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00ef: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00f5: ldarg.0 + IL_00f6: ldc.i4.m1 + IL_00f7: dup + IL_00f8: stloc.0 + IL_00f9: stfld "int C.
d__0.<>1__state" + IL_00fe: ldloca.s V_5 + IL_0100: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0105: nop + IL_0106: leave.s IL_0122 + } + catch System.Exception + { + IL_0108: stloc.s V_6 + IL_010a: ldarg.0 + IL_010b: ldc.i4.s -2 + IL_010d: stfld "int C.
d__0.<>1__state" + IL_0112: ldarg.0 + IL_0113: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0118: ldloc.s V_6 + IL_011a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011f: nop + IL_0120: leave.s IL_0136 + } + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int C.
d__0.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0130: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0135: nop + IL_0136: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 282 (0x11a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + System.Threading.Lock.Scope V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_004b + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00c8 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_0067 + IL_0028: ldarg.0 + IL_0029: ldc.i4.0 + IL_002a: dup + IL_002b: stloc.0 + IL_002c: stfld "int C.
d__0.<>1__state" + IL_0031: ldarg.0 + IL_0032: ldloc.1 + IL_0033: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0038: ldarg.0 + IL_0039: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_003e: ldloca.s V_1 + IL_0040: ldarg.0 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_0046: leave IL_0119 + IL_004b: ldarg.0 + IL_004c: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0051: stloc.1 + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_0058: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_005e: ldarg.0 + IL_005f: ldc.i4.m1 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int C.
d__0.<>1__state" + IL_0067: ldloca.s V_1 + IL_0069: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006e: newobj "System.Threading.Lock..ctor()" + IL_0073: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0078: stloc.3 + .try + { + IL_0079: ldstr "L" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0091 + } + finally + { + IL_0085: ldloc.0 + IL_0086: ldc.i4.0 + IL_0087: bge.s IL_0090 + IL_0089: ldloca.s V_3 + IL_008b: call "void System.Threading.Lock.Scope.Dispose()" + IL_0090: endfinally + } + IL_0091: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0096: stloc.2 + IL_0097: ldloca.s V_2 + IL_0099: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_009e: stloc.1 + IL_009f: ldloca.s V_1 + IL_00a1: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00a6: brtrue.s IL_00e4 + IL_00a8: ldarg.0 + IL_00a9: ldc.i4.1 + IL_00aa: dup + IL_00ab: stloc.0 + IL_00ac: stfld "int C.
d__0.<>1__state" + IL_00b1: ldarg.0 + IL_00b2: ldloc.1 + IL_00b3: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00b8: ldarg.0 + IL_00b9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00be: ldloca.s V_1 + IL_00c0: ldarg.0 + IL_00c1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedd__0>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref C.
d__0)" + IL_00c6: leave.s IL_0119 + IL_00c8: ldarg.0 + IL_00c9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00ce: stloc.1 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter C.
d__0.<>u__1" + IL_00d5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.m1 + IL_00dd: dup + IL_00de: stloc.0 + IL_00df: stfld "int C.
d__0.<>1__state" + IL_00e4: ldloca.s V_1 + IL_00e6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00eb: leave.s IL_0106 + } + catch System.Exception + { + IL_00ed: stloc.s V_4 + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.s -2 + IL_00f2: stfld "int C.
d__0.<>1__state" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00fd: ldloc.s V_4 + IL_00ff: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0104: leave.s IL_0119 + } + IL_0106: ldarg.0 + IL_0107: ldc.i4.s -2 + IL_0109: stfld "int C.
d__0.<>1__state" + IL_010e: ldarg.0 + IL_010f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0114: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0119: ret + } + """); + } + + [Fact] + public void AsyncMethod_AwaitResource() + { + var source = """ + #pragma warning disable 1998 // async method lacks 'await' operators + using System.Threading; + using System.Threading.Tasks; + + class C + { + static async Task Main() + { + lock (await GetLock()) { System.Console.Write("L"); } + } + + static async Task GetLock() => new Lock(); + } + """; + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 211 (0xd3) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + C.
d__0 V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_000c + IL_000a: br.s IL_000e + IL_000c: br.s IL_004a + IL_000e: nop + IL_000f: call "System.Threading.Tasks.Task C.GetLock()" + IL_0014: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0019: stloc.2 + IL_001a: ldloca.s V_2 + IL_001c: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_0021: brtrue.s IL_0066 + IL_0023: ldarg.0 + IL_0024: ldc.i4.0 + IL_0025: dup + IL_0026: stloc.0 + IL_0027: stfld "int C.
d__0.<>1__state" + IL_002c: ldarg.0 + IL_002d: ldloc.2 + IL_002e: stfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0033: ldarg.0 + IL_0034: stloc.3 + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_003b: ldloca.s V_2 + IL_003d: ldloca.s V_3 + IL_003f: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.
d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.
d__0)" + IL_0044: nop + IL_0045: leave IL_00d2 + IL_004a: ldarg.0 + IL_004b: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0050: stloc.2 + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0057: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_005d: ldarg.0 + IL_005e: ldc.i4.m1 + IL_005f: dup + IL_0060: stloc.0 + IL_0061: stfld "int C.
d__0.<>1__state" + IL_0066: ldarg.0 + IL_0067: ldloca.s V_2 + IL_0069: call "System.Threading.Lock System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_006e: stfld "System.Threading.Lock C.
d__0.<>s__1" + IL_0073: ldarg.0 + IL_0074: ldfld "System.Threading.Lock C.
d__0.<>s__1" + IL_0079: callvirt "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_007e: stloc.1 + IL_007f: ldarg.0 + IL_0080: ldnull + IL_0081: stfld "System.Threading.Lock C.
d__0.<>s__1" + .try + { + IL_0086: nop + IL_0087: ldstr "L" + IL_008c: call "void System.Console.Write(string)" + IL_0091: nop + IL_0092: nop + IL_0093: leave.s IL_00a2 + } + finally + { + IL_0095: ldloc.0 + IL_0096: ldc.i4.0 + IL_0097: bge.s IL_00a1 + IL_0099: ldloca.s V_1 + IL_009b: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a0: nop + IL_00a1: endfinally + } + IL_00a2: leave.s IL_00be + } + catch System.Exception + { + IL_00a4: stloc.s V_4 + IL_00a6: ldarg.0 + IL_00a7: ldc.i4.s -2 + IL_00a9: stfld "int C.
d__0.<>1__state" + IL_00ae: ldarg.0 + IL_00af: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00b4: ldloc.s V_4 + IL_00b6: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_00bb: nop + IL_00bc: leave.s IL_00d2 + } + IL_00be: ldarg.0 + IL_00bf: ldc.i4.s -2 + IL_00c1: stfld "int C.
d__0.<>1__state" + IL_00c6: ldarg.0 + IL_00c7: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00cc: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_00d1: nop + IL_00d2: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.
d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 172 (0xac) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld "int C.
d__0.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_003e + IL_000a: call "System.Threading.Tasks.Task C.GetLock()" + IL_000f: callvirt "System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()" + IL_0014: stloc.2 + IL_0015: ldloca.s V_2 + IL_0017: call "bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get" + IL_001c: brtrue.s IL_005a + IL_001e: ldarg.0 + IL_001f: ldc.i4.0 + IL_0020: dup + IL_0021: stloc.0 + IL_0022: stfld "int C.
d__0.<>1__state" + IL_0027: ldarg.0 + IL_0028: ldloc.2 + IL_0029: stfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_002e: ldarg.0 + IL_002f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0034: ldloca.s V_2 + IL_0036: ldarg.0 + IL_0037: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, C.
d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref C.
d__0)" + IL_003c: leave.s IL_00ab + IL_003e: ldarg.0 + IL_003f: ldfld "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_0044: stloc.2 + IL_0045: ldarg.0 + IL_0046: ldflda "System.Runtime.CompilerServices.TaskAwaiter C.
d__0.<>u__1" + IL_004b: initobj "System.Runtime.CompilerServices.TaskAwaiter" + IL_0051: ldarg.0 + IL_0052: ldc.i4.m1 + IL_0053: dup + IL_0054: stloc.0 + IL_0055: stfld "int C.
d__0.<>1__state" + IL_005a: ldloca.s V_2 + IL_005c: call "System.Threading.Lock System.Runtime.CompilerServices.TaskAwaiter.GetResult()" + IL_0061: callvirt "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0066: stloc.1 + .try + { + IL_0067: ldstr "L" + IL_006c: call "void System.Console.Write(string)" + IL_0071: leave.s IL_007f + } + finally + { + IL_0073: ldloc.0 + IL_0074: ldc.i4.0 + IL_0075: bge.s IL_007e + IL_0077: ldloca.s V_1 + IL_0079: call "void System.Threading.Lock.Scope.Dispose()" + IL_007e: endfinally + } + IL_007f: leave.s IL_0098 + } + catch System.Exception + { + IL_0081: stloc.3 + IL_0082: ldarg.0 + IL_0083: ldc.i4.s -2 + IL_0085: stfld "int C.
d__0.<>1__state" + IL_008a: ldarg.0 + IL_008b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_0090: ldloc.3 + IL_0091: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0096: leave.s IL_00ab + } + IL_0098: ldarg.0 + IL_0099: ldc.i4.s -2 + IL_009b: stfld "int C.
d__0.<>1__state" + IL_00a0: ldarg.0 + IL_00a1: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.
d__0.<>t__builder" + IL_00a6: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_00ab: ret + } + """); } [Fact] @@ -1921,15 +2546,132 @@ public void AsyncLocalFunction() async void local() { - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } } local(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (6,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(6, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: nop + IL_0008: newobj "System.Threading.Lock..ctor()" + IL_000d: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0012: stloc.1 + .try + { + IL_0013: nop + IL_0014: ldstr "L" + IL_0019: call "void System.Console.Write(string)" + IL_001e: nop + IL_001f: nop + IL_0020: leave.s IL_002f + } + finally + { + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: bge.s IL_002e + IL_0026: ldloca.s V_1 + IL_0028: call "void System.Threading.Lock.Scope.Dispose()" + IL_002d: nop + IL_002e: endfinally + } + IL_002f: leave.s IL_0049 + } + catch System.Exception + { + IL_0031: stloc.2 + IL_0032: ldarg.0 + IL_0033: ldc.i4.s -2 + IL_0035: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0040: ldloc.2 + IL_0041: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0046: nop + IL_0047: leave.s IL_005d + } + IL_0049: ldarg.0 + IL_004a: ldc.i4.s -2 + IL_004c: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0057: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_005c: nop + IL_005d: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: newobj "System.Threading.Lock..ctor()" + IL_000c: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0011: stloc.1 + .try + { + IL_0012: ldstr "L" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_002a + } + finally + { + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: bge.s IL_0029 + IL_0022: ldloca.s V_1 + IL_0024: call "void System.Threading.Lock.Scope.Dispose()" + IL_0029: endfinally + } + IL_002a: leave.s IL_0043 + } + catch System.Exception + { + IL_002c: stloc.2 + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -2 + IL_0030: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_003b: ldloc.2 + IL_003c: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException(System.Exception)" + IL_0041: leave.s IL_0056 + } + IL_0043: ldarg.0 + IL_0044: ldc.i4.s -2 + IL_0046: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldflda "System.Runtime.CompilerServices.AsyncVoidMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0051: call "void System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult()" + IL_0056: ret + } + """); } [Fact] @@ -1939,18 +2681,304 @@ public void AsyncLocalFunction_WithAwait() using System.Threading; using System.Threading.Tasks; - async void local() + async Task local() { await Task.Yield(); - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } + await Task.Yield(); } - local(); + await local(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (7,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(7, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 311 (0x137) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + Program.<<
$>g__local|0_0>d V_3, + System.Threading.Lock.Scope V_4, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_5, + System.Exception V_6) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0019 + IL_0012: br.s IL_0058 + IL_0014: br IL_00e1 + IL_0019: nop + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001f: stloc.2 + IL_0020: ldloca.s V_2 + IL_0022: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0027: stloc.1 + IL_0028: ldloca.s V_1 + IL_002a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0074 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.1 + IL_003c: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0041: ldarg.0 + IL_0042: stloc.3 + IL_0043: ldarg.0 + IL_0044: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0049: ldloca.s V_1 + IL_004b: ldloca.s V_3 + IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_0052: nop + IL_0053: leave IL_0136 + IL_0058: ldarg.0 + IL_0059: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_005e: stloc.1 + IL_005f: ldarg.0 + IL_0060: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0065: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_006b: ldarg.0 + IL_006c: ldc.i4.m1 + IL_006d: dup + IL_006e: stloc.0 + IL_006f: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0074: ldloca.s V_1 + IL_0076: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_007b: nop + IL_007c: newobj "System.Threading.Lock..ctor()" + IL_0081: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0086: stloc.s V_4 + .try + { + IL_0088: nop + IL_0089: ldstr "L" + IL_008e: call "void System.Console.Write(string)" + IL_0093: nop + IL_0094: nop + IL_0095: leave.s IL_00a4 + } + finally + { + IL_0097: ldloc.0 + IL_0098: ldc.i4.0 + IL_0099: bge.s IL_00a3 + IL_009b: ldloca.s V_4 + IL_009d: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a2: nop + IL_00a3: endfinally + } + IL_00a4: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00a9: stloc.2 + IL_00aa: ldloca.s V_2 + IL_00ac: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00b1: stloc.s V_5 + IL_00b3: ldloca.s V_5 + IL_00b5: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00ba: brtrue.s IL_00fe + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00c5: ldarg.0 + IL_00c6: ldloc.s V_5 + IL_00c8: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00cd: ldarg.0 + IL_00ce: stloc.3 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_00d5: ldloca.s V_5 + IL_00d7: ldloca.s V_3 + IL_00d9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_00de: nop + IL_00df: leave.s IL_0136 + IL_00e1: ldarg.0 + IL_00e2: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00e7: stloc.s V_5 + IL_00e9: ldarg.0 + IL_00ea: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00ef: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00f5: ldarg.0 + IL_00f6: ldc.i4.m1 + IL_00f7: dup + IL_00f8: stloc.0 + IL_00f9: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00fe: ldloca.s V_5 + IL_0100: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0105: nop + IL_0106: leave.s IL_0122 + } + catch System.Exception + { + IL_0108: stloc.s V_6 + IL_010a: ldarg.0 + IL_010b: ldc.i4.s -2 + IL_010d: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0112: ldarg.0 + IL_0113: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0118: ldloc.s V_6 + IL_011a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011f: nop + IL_0120: leave.s IL_0136 + } + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0130: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0135: nop + IL_0136: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<<
$>g__local|0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 282 (0x11a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + System.Threading.Lock.Scope V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_004b + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00c8 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_0067 + IL_0028: ldarg.0 + IL_0029: ldc.i4.0 + IL_002a: dup + IL_002b: stloc.0 + IL_002c: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0031: ldarg.0 + IL_0032: ldloc.1 + IL_0033: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0038: ldarg.0 + IL_0039: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_003e: ldloca.s V_1 + IL_0040: ldarg.0 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_0046: leave IL_0119 + IL_004b: ldarg.0 + IL_004c: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0051: stloc.1 + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_0058: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_005e: ldarg.0 + IL_005f: ldc.i4.m1 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_0067: ldloca.s V_1 + IL_0069: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006e: newobj "System.Threading.Lock..ctor()" + IL_0073: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0078: stloc.3 + .try + { + IL_0079: ldstr "L" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0091 + } + finally + { + IL_0085: ldloc.0 + IL_0086: ldc.i4.0 + IL_0087: bge.s IL_0090 + IL_0089: ldloca.s V_3 + IL_008b: call "void System.Threading.Lock.Scope.Dispose()" + IL_0090: endfinally + } + IL_0091: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0096: stloc.2 + IL_0097: ldloca.s V_2 + IL_0099: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_009e: stloc.1 + IL_009f: ldloca.s V_1 + IL_00a1: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00a6: brtrue.s IL_00e4 + IL_00a8: ldarg.0 + IL_00a9: ldc.i4.1 + IL_00aa: dup + IL_00ab: stloc.0 + IL_00ac: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00b1: ldarg.0 + IL_00b2: ldloc.1 + IL_00b3: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00b8: ldarg.0 + IL_00b9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_00be: ldloca.s V_1 + IL_00c0: ldarg.0 + IL_00c1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted$>g__local|0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<<
$>g__local|0_0>d)" + IL_00c6: leave.s IL_0119 + IL_00c8: ldarg.0 + IL_00c9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00ce: stloc.1 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<<
$>g__local|0_0>d.<>u__1" + IL_00d5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.m1 + IL_00dd: dup + IL_00de: stloc.0 + IL_00df: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00e4: ldloca.s V_1 + IL_00e6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00eb: leave.s IL_0106 + } + catch System.Exception + { + IL_00ed: stloc.s V_4 + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.s -2 + IL_00f2: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_00fd: ldloc.s V_4 + IL_00ff: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0104: leave.s IL_0119 + } + IL_0106: ldarg.0 + IL_0107: ldc.i4.s -2 + IL_0109: stfld "int Program.<<
$>g__local|0_0>d.<>1__state" + IL_010e: ldarg.0 + IL_010f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<<
$>g__local|0_0>d.<>t__builder" + IL_0114: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0119: ret + } + """); } [Fact] @@ -1962,13 +2990,132 @@ public void AsyncLambda() var lam = async () => { - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } }; + + await lam(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (6,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(6, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 94 (0x5e) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: nop + IL_0008: newobj "System.Threading.Lock..ctor()" + IL_000d: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0012: stloc.1 + .try + { + IL_0013: nop + IL_0014: ldstr "L" + IL_0019: call "void System.Console.Write(string)" + IL_001e: nop + IL_001f: nop + IL_0020: leave.s IL_002f + } + finally + { + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: bge.s IL_002e + IL_0026: ldloca.s V_1 + IL_0028: call "void System.Threading.Lock.Scope.Dispose()" + IL_002d: nop + IL_002e: endfinally + } + IL_002f: leave.s IL_0049 + } + catch System.Exception + { + IL_0031: stloc.2 + IL_0032: ldarg.0 + IL_0033: ldc.i4.s -2 + IL_0035: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0040: ldloc.2 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0046: nop + IL_0047: leave.s IL_005d + } + IL_0049: ldarg.0 + IL_004a: ldc.i4.s -2 + IL_004c: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0051: ldarg.0 + IL_0052: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0057: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_005c: nop + IL_005d: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 87 (0x57) + .maxstack 2 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Exception V_2) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: newobj "System.Threading.Lock..ctor()" + IL_000c: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0011: stloc.1 + .try + { + IL_0012: ldstr "L" + IL_0017: call "void System.Console.Write(string)" + IL_001c: leave.s IL_002a + } + finally + { + IL_001e: ldloc.0 + IL_001f: ldc.i4.0 + IL_0020: bge.s IL_0029 + IL_0022: ldloca.s V_1 + IL_0024: call "void System.Threading.Lock.Scope.Dispose()" + IL_0029: endfinally + } + IL_002a: leave.s IL_0043 + } + catch System.Exception + { + IL_002c: stloc.2 + IL_002d: ldarg.0 + IL_002e: ldc.i4.s -2 + IL_0030: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0035: ldarg.0 + IL_0036: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_003b: ldloc.2 + IL_003c: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0041: leave.s IL_0056 + } + IL_0043: ldarg.0 + IL_0044: ldc.i4.s -2 + IL_0046: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_004b: ldarg.0 + IL_004c: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0051: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0056: ret + } + """); } [Fact] @@ -1981,13 +3128,301 @@ public void AsyncLambda_WithAwait() var lam = async () => { await Task.Yield(); - lock (new Lock()) { } + lock (new Lock()) { System.Console.Write("L"); } + await Task.Yield(); }; + + await lam(); """; - CreateCompilation([source, LockTypeDefinition]).VerifyDiagnostics( - // (7,11): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. - // lock (new Lock()) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(7, 11)); + var expectedOutput = "ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 311 (0x137) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + Program.<>c.<<
$>b__0_0>d V_3, + System.Threading.Lock.Scope V_4, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_5, + System.Exception V_6) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0012 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ldc.i4.1 + IL_000e: beq.s IL_0014 + IL_0010: br.s IL_0019 + IL_0012: br.s IL_0058 + IL_0014: br IL_00e1 + IL_0019: nop + IL_001a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_001f: stloc.2 + IL_0020: ldloca.s V_2 + IL_0022: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_0027: stloc.1 + IL_0028: ldloca.s V_1 + IL_002a: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_002f: brtrue.s IL_0074 + IL_0031: ldarg.0 + IL_0032: ldc.i4.0 + IL_0033: dup + IL_0034: stloc.0 + IL_0035: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_003a: ldarg.0 + IL_003b: ldloc.1 + IL_003c: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0041: ldarg.0 + IL_0042: stloc.3 + IL_0043: ldarg.0 + IL_0044: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0049: ldloca.s V_1 + IL_004b: ldloca.s V_3 + IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_0052: nop + IL_0053: leave IL_0136 + IL_0058: ldarg.0 + IL_0059: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_005e: stloc.1 + IL_005f: ldarg.0 + IL_0060: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0065: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_006b: ldarg.0 + IL_006c: ldc.i4.m1 + IL_006d: dup + IL_006e: stloc.0 + IL_006f: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0074: ldloca.s V_1 + IL_0076: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_007b: nop + IL_007c: newobj "System.Threading.Lock..ctor()" + IL_0081: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0086: stloc.s V_4 + .try + { + IL_0088: nop + IL_0089: ldstr "L" + IL_008e: call "void System.Console.Write(string)" + IL_0093: nop + IL_0094: nop + IL_0095: leave.s IL_00a4 + } + finally + { + IL_0097: ldloc.0 + IL_0098: ldc.i4.0 + IL_0099: bge.s IL_00a3 + IL_009b: ldloca.s V_4 + IL_009d: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a2: nop + IL_00a3: endfinally + } + IL_00a4: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00a9: stloc.2 + IL_00aa: ldloca.s V_2 + IL_00ac: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00b1: stloc.s V_5 + IL_00b3: ldloca.s V_5 + IL_00b5: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00ba: brtrue.s IL_00fe + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00c5: ldarg.0 + IL_00c6: ldloc.s V_5 + IL_00c8: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00cd: ldarg.0 + IL_00ce: stloc.3 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_00d5: ldloca.s V_5 + IL_00d7: ldloca.s V_3 + IL_00d9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_00de: nop + IL_00df: leave.s IL_0136 + IL_00e1: ldarg.0 + IL_00e2: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00e7: stloc.s V_5 + IL_00e9: ldarg.0 + IL_00ea: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00ef: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00f5: ldarg.0 + IL_00f6: ldc.i4.m1 + IL_00f7: dup + IL_00f8: stloc.0 + IL_00f9: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00fe: ldloca.s V_5 + IL_0100: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0105: nop + IL_0106: leave.s IL_0122 + } + catch System.Exception + { + IL_0108: stloc.s V_6 + IL_010a: ldarg.0 + IL_010b: ldc.i4.s -2 + IL_010d: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0112: ldarg.0 + IL_0113: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0118: ldloc.s V_6 + IL_011a: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_011f: nop + IL_0120: leave.s IL_0136 + } + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0130: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0135: nop + IL_0136: ret + } + """); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.<>c.<<
$>b__0_0>d.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 282 (0x11a) + .maxstack 3 + .locals init (int V_0, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_1, + System.Runtime.CompilerServices.YieldAwaitable V_2, + System.Threading.Lock.Scope V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_004b + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00c8 + IL_0011: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0016: stloc.2 + IL_0017: ldloca.s V_2 + IL_0019: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_001e: stloc.1 + IL_001f: ldloca.s V_1 + IL_0021: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_0026: brtrue.s IL_0067 + IL_0028: ldarg.0 + IL_0029: ldc.i4.0 + IL_002a: dup + IL_002b: stloc.0 + IL_002c: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0031: ldarg.0 + IL_0032: ldloc.1 + IL_0033: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0038: ldarg.0 + IL_0039: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_003e: ldloca.s V_1 + IL_0040: ldarg.0 + IL_0041: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_0046: leave IL_0119 + IL_004b: ldarg.0 + IL_004c: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0051: stloc.1 + IL_0052: ldarg.0 + IL_0053: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_0058: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_005e: ldarg.0 + IL_005f: ldc.i4.m1 + IL_0060: dup + IL_0061: stloc.0 + IL_0062: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_0067: ldloca.s V_1 + IL_0069: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_006e: newobj "System.Threading.Lock..ctor()" + IL_0073: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0078: stloc.3 + .try + { + IL_0079: ldstr "L" + IL_007e: call "void System.Console.Write(string)" + IL_0083: leave.s IL_0091 + } + finally + { + IL_0085: ldloc.0 + IL_0086: ldc.i4.0 + IL_0087: bge.s IL_0090 + IL_0089: ldloca.s V_3 + IL_008b: call "void System.Threading.Lock.Scope.Dispose()" + IL_0090: endfinally + } + IL_0091: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_0096: stloc.2 + IL_0097: ldloca.s V_2 + IL_0099: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_009e: stloc.1 + IL_009f: ldloca.s V_1 + IL_00a1: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00a6: brtrue.s IL_00e4 + IL_00a8: ldarg.0 + IL_00a9: ldc.i4.1 + IL_00aa: dup + IL_00ab: stloc.0 + IL_00ac: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00b1: ldarg.0 + IL_00b2: ldloc.1 + IL_00b3: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00b8: ldarg.0 + IL_00b9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_00be: ldloca.s V_1 + IL_00c0: ldarg.0 + IL_00c1: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompletedc.<<
$>b__0_0>d>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.<>c.<<
$>b__0_0>d)" + IL_00c6: leave.s IL_0119 + IL_00c8: ldarg.0 + IL_00c9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00ce: stloc.1 + IL_00cf: ldarg.0 + IL_00d0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.<>c.<<
$>b__0_0>d.<>u__1" + IL_00d5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00db: ldarg.0 + IL_00dc: ldc.i4.m1 + IL_00dd: dup + IL_00de: stloc.0 + IL_00df: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00e4: ldloca.s V_1 + IL_00e6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00eb: leave.s IL_0106 + } + catch System.Exception + { + IL_00ed: stloc.s V_4 + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.s -2 + IL_00f2: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_00f7: ldarg.0 + IL_00f8: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_00fd: ldloc.s V_4 + IL_00ff: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" + IL_0104: leave.s IL_0119 + } + IL_0106: ldarg.0 + IL_0107: ldc.i4.s -2 + IL_0109: stfld "int Program.<>c.<<
$>b__0_0>d.<>1__state" + IL_010e: ldarg.0 + IL_010f: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder Program.<>c.<<
$>b__0_0>d.<>t__builder" + IL_0114: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" + IL_0119: ret + } + """); } [Fact] @@ -2011,14 +3446,95 @@ IEnumerable M() } """; CreateCompilation([source, LockTypeDefinition]).VerifyEmitDiagnostics( - // (9,15): error CS4013: Instance of type 'Lock.Scope' cannot be used inside a nested function, query expression, iterator block or async method + // (9,15): error CS4007: Instance of type 'System.Threading.Lock.Scope' cannot be preserved across 'await' or 'yield' boundary. // lock (new Lock()) - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15), + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15), // (11,13): warning CS9230: 'yield return' should not be used in the body of a lock statement // yield return 2; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(11, 13)); } + [Fact] + public void Yield_Break() + { + var source = """ + #pragma warning disable CS0162 // Unreachable code detected + using System; + using System.Collections.Generic; + using System.Threading; + + static class Program + { + static void Main() + { + foreach (var x in M()) + { + Console.Write(x); + } + } + + static IEnumerable M() + { + yield return 1; + lock (new Lock()) + { + Console.Write("L"); + yield break; + Console.Write("B"); + } + yield return 2; + } + } + """; + var expectedOutput = "1ELD"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Yield_AroundOnly() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading; + + static class Program + { + static void Main() + { + foreach (var x in M()) + { + Console.Write(x); + } + } + + static IEnumerable M() + { + yield return 1; + lock (new Lock()) + { + Console.Write("L"); + } + yield return 2; + } + } + """; + var expectedOutput = "1ELD2"; + var verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.ReleaseExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + verifier = CompileAndVerify([source, LockTypeDefinition], options: TestOptions.DebugExe, + verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + } + [Fact] public void Yield_Async() { @@ -2041,15 +3557,457 @@ async IAsyncEnumerable M() } } """; - CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes]).VerifyDiagnostics( - // (10,15): error CS9217: A lock statement on a value of type 'System.Threading.Lock' cannot be used in async methods or async lambda expressions. + CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes]).VerifyEmitDiagnostics( + // (10,15): error CS4007: Instance of type 'System.Threading.Lock.Scope' cannot be preserved across 'await' or 'yield' boundary. // lock (new Lock()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLock, "new Lock()").WithLocation(10, 15), + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(10, 15), // (12,13): warning CS9230: 'yield return' should not be used in the body of a lock statement // yield return 2; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(12, 13)); } + [Fact] + public void Yield_Async_Break() + { + var source = """ + #pragma warning disable CS0162 // Unreachable code detected + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + static class Program + { + static async Task Main() + { + await foreach (var x in M()) + { + Console.Write(x); + } + } + + async static IAsyncEnumerable M() + { + yield return 1; + await Task.Yield(); + lock (new Lock()) + { + Console.Write("L"); + yield break; + Console.Write("B"); + } + await Task.Yield(); + yield return 2; + } + } + """; + var expectedOutput = "1ELD"; + var comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + + comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.DebugExe); + verifier = CompileAndVerify(comp, verify: Verification.FailsILVerify, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Yield_Async_AroundOnly() + { + var source = """ + using System; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + static class Program + { + static async Task Main() + { + await foreach (var x in M()) + { + Console.Write(x); + } + } + + static async IAsyncEnumerable M() + { + yield return 1; + lock (new Lock()) + { + Console.Write("L"); + } + await Task.Yield(); + yield return 2; + } + } + """; + var expectedOutput = "1ELD2"; + var comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.DebugExe); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 414 (0x19e) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3, + Program.d__1 V_4, + System.Exception V_5) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -5 + IL_000a: sub + IL_000b: switch ( + IL_002a, + IL_002f, + IL_0031, + IL_0038, + IL_0038, + IL_0033) + IL_0028: br.s IL_0038 + IL_002a: br IL_0125 + IL_002f: br.s IL_0065 + IL_0031: br.s IL_0038 + IL_0033: br IL_00ee + IL_0038: ldarg.0 + IL_0039: ldfld "bool Program.d__1.<>w__disposeMode" + IL_003e: brfalse.s IL_0045 + IL_0040: leave IL_0167 + IL_0045: ldarg.0 + IL_0046: ldc.i4.m1 + IL_0047: dup + IL_0048: stloc.0 + IL_0049: stfld "int Program.d__1.<>1__state" + IL_004e: nop + IL_004f: ldarg.0 + IL_0050: ldc.i4.1 + IL_0051: stfld "int Program.d__1.<>2__current" + IL_0056: ldarg.0 + IL_0057: ldc.i4.s -4 + IL_0059: dup + IL_005a: stloc.0 + IL_005b: stfld "int Program.d__1.<>1__state" + IL_0060: leave IL_0190 + IL_0065: ldarg.0 + IL_0066: ldc.i4.m1 + IL_0067: dup + IL_0068: stloc.0 + IL_0069: stfld "int Program.d__1.<>1__state" + IL_006e: ldarg.0 + IL_006f: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0074: brfalse.s IL_007b + IL_0076: leave IL_0167 + IL_007b: newobj "System.Threading.Lock..ctor()" + IL_0080: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0085: stloc.1 + .try + { + IL_0086: nop + IL_0087: ldstr "L" + IL_008c: call "void System.Console.Write(string)" + IL_0091: nop + IL_0092: nop + IL_0093: leave.s IL_00a2 + } + finally + { + IL_0095: ldloc.0 + IL_0096: ldc.i4.m1 + IL_0097: bne.un.s IL_00a1 + IL_0099: ldloca.s V_1 + IL_009b: call "void System.Threading.Lock.Scope.Dispose()" + IL_00a0: nop + IL_00a1: endfinally + } + IL_00a2: ldarg.0 + IL_00a3: ldfld "bool Program.d__1.<>w__disposeMode" + IL_00a8: brfalse.s IL_00af + IL_00aa: leave IL_0167 + IL_00af: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_00b4: stloc.3 + IL_00b5: ldloca.s V_3 + IL_00b7: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00bc: stloc.2 + IL_00bd: ldloca.s V_2 + IL_00bf: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00c4: brtrue.s IL_010a + IL_00c6: ldarg.0 + IL_00c7: ldc.i4.0 + IL_00c8: dup + IL_00c9: stloc.0 + IL_00ca: stfld "int Program.d__1.<>1__state" + IL_00cf: ldarg.0 + IL_00d0: ldloc.2 + IL_00d1: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00d6: ldarg.0 + IL_00d7: stloc.s V_4 + IL_00d9: ldarg.0 + IL_00da: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_00df: ldloca.s V_2 + IL_00e1: ldloca.s V_4 + IL_00e3: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.d__1)" + IL_00e8: nop + IL_00e9: leave IL_019d + IL_00ee: ldarg.0 + IL_00ef: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00f4: stloc.2 + IL_00f5: ldarg.0 + IL_00f6: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00fb: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_0101: ldarg.0 + IL_0102: ldc.i4.m1 + IL_0103: dup + IL_0104: stloc.0 + IL_0105: stfld "int Program.d__1.<>1__state" + IL_010a: ldloca.s V_2 + IL_010c: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_0111: nop + IL_0112: ldarg.0 + IL_0113: ldc.i4.2 + IL_0114: stfld "int Program.d__1.<>2__current" + IL_0119: ldarg.0 + IL_011a: ldc.i4.s -5 + IL_011c: dup + IL_011d: stloc.0 + IL_011e: stfld "int Program.d__1.<>1__state" + IL_0123: leave.s IL_0190 + IL_0125: ldarg.0 + IL_0126: ldc.i4.m1 + IL_0127: dup + IL_0128: stloc.0 + IL_0129: stfld "int Program.d__1.<>1__state" + IL_012e: ldarg.0 + IL_012f: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0134: brfalse.s IL_0138 + IL_0136: leave.s IL_0167 + IL_0138: leave.s IL_0167 + } + catch System.Exception + { + IL_013a: stloc.s V_5 + IL_013c: ldarg.0 + IL_013d: ldc.i4.s -2 + IL_013f: stfld "int Program.d__1.<>1__state" + IL_0144: ldarg.0 + IL_0145: ldc.i4.0 + IL_0146: stfld "int Program.d__1.<>2__current" + IL_014b: ldarg.0 + IL_014c: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_0151: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0156: nop + IL_0157: ldarg.0 + IL_0158: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_015d: ldloc.s V_5 + IL_015f: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0164: nop + IL_0165: leave.s IL_019d + } + IL_0167: ldarg.0 + IL_0168: ldc.i4.s -2 + IL_016a: stfld "int Program.d__1.<>1__state" + IL_016f: ldarg.0 + IL_0170: ldc.i4.0 + IL_0171: stfld "int Program.d__1.<>2__current" + IL_0176: ldarg.0 + IL_0177: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_017c: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0181: nop + IL_0182: ldarg.0 + IL_0183: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0188: ldc.i4.0 + IL_0189: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_018e: nop + IL_018f: ret + IL_0190: ldarg.0 + IL_0191: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0196: ldc.i4.1 + IL_0197: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_019c: nop + IL_019d: ret + } + """); + + comp = CreateCompilationWithTasksExtensions([source, LockTypeDefinition, AsyncStreamsTypes], options: TestOptions.ReleaseExe); + verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.FailsILVerify); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", """ + { + // Code size 383 (0x17f) + .maxstack 3 + .locals init (int V_0, + System.Threading.Lock.Scope V_1, + System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter V_2, + System.Runtime.CompilerServices.YieldAwaitable V_3, + Program.d__1 V_4, + System.Exception V_5) + IL_0000: ldarg.0 + IL_0001: ldfld "int Program.d__1.<>1__state" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: ldc.i4.s -5 + IL_000a: sub + IL_000b: switch ( + IL_010e, + IL_0054, + IL_0028, + IL_0028, + IL_0028, + IL_00d8) + IL_0028: ldarg.0 + IL_0029: ldfld "bool Program.d__1.<>w__disposeMode" + IL_002e: brfalse.s IL_0035 + IL_0030: leave IL_014b + IL_0035: ldarg.0 + IL_0036: ldc.i4.m1 + IL_0037: dup + IL_0038: stloc.0 + IL_0039: stfld "int Program.d__1.<>1__state" + IL_003e: ldarg.0 + IL_003f: ldc.i4.1 + IL_0040: stfld "int Program.d__1.<>2__current" + IL_0045: ldarg.0 + IL_0046: ldc.i4.s -4 + IL_0048: dup + IL_0049: stloc.0 + IL_004a: stfld "int Program.d__1.<>1__state" + IL_004f: leave IL_0172 + IL_0054: ldarg.0 + IL_0055: ldc.i4.m1 + IL_0056: dup + IL_0057: stloc.0 + IL_0058: stfld "int Program.d__1.<>1__state" + IL_005d: ldarg.0 + IL_005e: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0063: brfalse.s IL_006a + IL_0065: leave IL_014b + IL_006a: newobj "System.Threading.Lock..ctor()" + IL_006f: call "System.Threading.Lock.Scope System.Threading.Lock.EnterScope()" + IL_0074: stloc.1 + .try + { + IL_0075: ldstr "L" + IL_007a: call "void System.Console.Write(string)" + IL_007f: leave.s IL_008d + } + finally + { + IL_0081: ldloc.0 + IL_0082: ldc.i4.m1 + IL_0083: bne.un.s IL_008c + IL_0085: ldloca.s V_1 + IL_0087: call "void System.Threading.Lock.Scope.Dispose()" + IL_008c: endfinally + } + IL_008d: ldarg.0 + IL_008e: ldfld "bool Program.d__1.<>w__disposeMode" + IL_0093: brfalse.s IL_009a + IL_0095: leave IL_014b + IL_009a: call "System.Runtime.CompilerServices.YieldAwaitable System.Threading.Tasks.Task.Yield()" + IL_009f: stloc.3 + IL_00a0: ldloca.s V_3 + IL_00a2: call "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter()" + IL_00a7: stloc.2 + IL_00a8: ldloca.s V_2 + IL_00aa: call "bool System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.IsCompleted.get" + IL_00af: brtrue.s IL_00f4 + IL_00b1: ldarg.0 + IL_00b2: ldc.i4.0 + IL_00b3: dup + IL_00b4: stloc.0 + IL_00b5: stfld "int Program.d__1.<>1__state" + IL_00ba: ldarg.0 + IL_00bb: ldloc.2 + IL_00bc: stfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00c1: ldarg.0 + IL_00c2: stloc.s V_4 + IL_00c4: ldarg.0 + IL_00c5: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_00ca: ldloca.s V_2 + IL_00cc: ldloca.s V_4 + IL_00ce: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompletedd__1>(ref System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ref Program.d__1)" + IL_00d3: leave IL_017e + IL_00d8: ldarg.0 + IL_00d9: ldfld "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00de: stloc.2 + IL_00df: ldarg.0 + IL_00e0: ldflda "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter Program.d__1.<>u__1" + IL_00e5: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter" + IL_00eb: ldarg.0 + IL_00ec: ldc.i4.m1 + IL_00ed: dup + IL_00ee: stloc.0 + IL_00ef: stfld "int Program.d__1.<>1__state" + IL_00f4: ldloca.s V_2 + IL_00f6: call "void System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()" + IL_00fb: ldarg.0 + IL_00fc: ldc.i4.2 + IL_00fd: stfld "int Program.d__1.<>2__current" + IL_0102: ldarg.0 + IL_0103: ldc.i4.s -5 + IL_0105: dup + IL_0106: stloc.0 + IL_0107: stfld "int Program.d__1.<>1__state" + IL_010c: leave.s IL_0172 + IL_010e: ldarg.0 + IL_010f: ldc.i4.m1 + IL_0110: dup + IL_0111: stloc.0 + IL_0112: stfld "int Program.d__1.<>1__state" + IL_0117: ldarg.0 + IL_0118: ldfld "bool Program.d__1.<>w__disposeMode" + IL_011d: pop + IL_011e: leave.s IL_014b + } + catch System.Exception + { + IL_0120: stloc.s V_5 + IL_0122: ldarg.0 + IL_0123: ldc.i4.s -2 + IL_0125: stfld "int Program.d__1.<>1__state" + IL_012a: ldarg.0 + IL_012b: ldc.i4.0 + IL_012c: stfld "int Program.d__1.<>2__current" + IL_0131: ldarg.0 + IL_0132: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_0137: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_013c: ldarg.0 + IL_013d: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0142: ldloc.s V_5 + IL_0144: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetException(System.Exception)" + IL_0149: leave.s IL_017e + } + IL_014b: ldarg.0 + IL_014c: ldc.i4.s -2 + IL_014e: stfld "int Program.d__1.<>1__state" + IL_0153: ldarg.0 + IL_0154: ldc.i4.0 + IL_0155: stfld "int Program.d__1.<>2__current" + IL_015a: ldarg.0 + IL_015b: ldflda "System.Runtime.CompilerServices.AsyncIteratorMethodBuilder Program.d__1.<>t__builder" + IL_0160: call "void System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.Complete()" + IL_0165: ldarg.0 + IL_0166: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_016b: ldc.i4.0 + IL_016c: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_0171: ret + IL_0172: ldarg.0 + IL_0173: ldflda "System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore Program.d__1.<>v__promiseOfValueOrEnd" + IL_0178: ldc.i4.1 + IL_0179: call "void System.Threading.Tasks.Sources.ManualResetValueTaskSourceCore.SetResult(bool)" + IL_017e: ret + } + """); + } + [Theory, CombinatorialData] public void CastToObject([CombinatorialValues("object ", "dynamic")] string type) { diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs index 1c2c8c2f59050..213a94c3598ea 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/OutVarTests.cs @@ -19581,9 +19581,9 @@ static void Test2(object x, System.ArgIterator y) // (11,25): error CS1601: Cannot make reference to variable of type 'ArgIterator' // static object Test1(out System.ArgIterator x) Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out System.ArgIterator x").WithArguments("System.ArgIterator").WithLocation(11, 25), - // (8,25): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + // (8,25): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Test2(Test1(out var x1), x1); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.ArgIterator").WithLocation(8, 25), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 25), // (6,16): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // async void Test() Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Test").WithLocation(6, 16) @@ -19630,12 +19630,12 @@ static void Test2(object x, System.ArgIterator y) // (12,25): error CS1601: Cannot make reference to variable of type 'ArgIterator' // static object Test1(out System.ArgIterator x) Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "out System.ArgIterator x").WithArguments("System.ArgIterator").WithLocation(12, 25), - // (8,25): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + // (8,25): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Test2(Test1(out System.ArgIterator x1), x1); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(8, 25), - // (9,9): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_FeatureInPreview, "System.ArgIterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 25), + // (9,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // var x = default(System.ArgIterator); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.ArgIterator").WithLocation(9, 9), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 9), // (9,13): warning CS0219: The variable 'x' is assigned but its value is never used // var x = default(System.ArgIterator); Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "x").WithArguments("x").WithLocation(9, 13), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs index 58b403f3ed35f..0cdb4221d7784 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/AwaitExpressionTests.cs @@ -241,9 +241,9 @@ static async Task Goo() }"; var comp = CreateCompilationWithMscorlib45(text, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics( - // (8,27): error CS4007: 'await' cannot be used in an expression containing the type 'System.TypedReference' + // (8,27): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. // Console.WriteLine(new TypedReference().Equals(await Task.FromResult(0))); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await Task.FromResult(0)").WithArguments("System.TypedReference").WithLocation(8, 55)); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new TypedReference()").WithArguments("System.TypedReference").WithLocation(8, 27)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs index 740346725b17a..8e550a2a565e8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs @@ -3218,9 +3218,9 @@ async Task M1(TypedReference tr) } }"; CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (7,34): error CS4012: Parameters or locals of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions + // (7,34): error CS4012: Parameters of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions // async Task M1(TypedReference tr) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "tr").WithArguments("System.TypedReference")); + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "tr").WithArguments("System.TypedReference")); } [Fact] @@ -3238,10 +3238,7 @@ async Task M1() await Task.Factory.StartNew(() => { }); } }"; - CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (9,9): error CS4012: Parameters or locals of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions - // TypedReference tr; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "TypedReference").WithArguments("System.TypedReference"), + CreateCompilationWithMscorlib45(source).VerifyEmitDiagnostics( // (9,24): warning CS0168: The variable 'tr' is declared but never used // TypedReference tr; Diagnostic(ErrorCode.WRN_UnreferencedVar, "tr").WithArguments("tr")); @@ -3257,18 +3254,33 @@ public void BadSpecialByRefVarDeclLocal() class Test { async Task M1(bool truth) + { + var tr = new TypedReference(); // 1 + await Task.Factory.StartNew(() => { }); + } + + async Task M2(bool truth) { var tr = new TypedReference(); await Task.Factory.StartNew(() => { }); + var tr2 = tr; // 2 + } + + async Task M3() + { + var tr = new TypedReference(); + await Task.Factory.StartNew(() => { }); + tr = default; + var tr2 = tr; } }"; - CreateCompilationWithMscorlib45(source).VerifyDiagnostics( - // (9,9): error CS4012: Parameters or locals of type 'TypedReference' cannot be declared in async methods or async lambda expressions. - // var tr = new TypedReference(); - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.TypedReference").WithLocation(9, 9), + CreateCompilationWithMscorlib45(source).VerifyEmitDiagnostics( // (9,13): warning CS0219: The variable 'tr' is assigned but its value is never used - // var tr = new TypedReference(); - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "tr").WithArguments("tr").WithLocation(9, 13)); + // var tr = new TypedReference(); // 1 + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "tr").WithArguments("tr").WithLocation(9, 13), + // (17,19): error CS4007: Instance of type 'System.TypedReference' cannot be preserved across 'await' or 'yield' boundary. + // var tr2 = tr; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "tr").WithArguments("System.TypedReference").WithLocation(17, 19)); } [Fact] @@ -3288,9 +3300,6 @@ unsafe async public static void F() // (8,31): error CS0209: The type of a local declared in a fixed statement must be a pointer type // fixed (TypedReference tr) { } Diagnostic(ErrorCode.ERR_BadFixedInitType, "tr"), - // (8,16): error CS4012: Parameters or locals of type 'System.TypedReference' cannot be declared in async methods or async lambda expressions. - // fixed (TypedReference tr) { } - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "TypedReference").WithArguments("System.TypedReference"), // (8,31): error CS0210: You must provide an initializer in a fixed or using statement declaration // fixed (TypedReference tr) { } Diagnostic(ErrorCode.ERR_FixedMustInit, "tr"), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs index bb59d99fef196..fd120694a384c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ForEachTests.cs @@ -3531,10 +3531,7 @@ public async static Task Test() System.Console.Write(x); } } -}").VerifyDiagnostics( - // (20,26): error CS8177: Async methods cannot have by-reference locals - // foreach (ref int x in new E()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(20, 26)); +}").VerifyEmitDiagnostics(); } [Fact] @@ -3565,10 +3562,7 @@ public async static Task Test() System.Console.Write(x); } } -}").VerifyDiagnostics( - // (20,35): error CS8177: Async methods cannot have by-reference locals - // foreach (ref readonly int x in new E()) - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(20, 35)); +}").VerifyEmitDiagnostics(); } [Fact] @@ -3597,10 +3591,7 @@ public static IEnumerable Test() yield return x; } } -}").VerifyDiagnostics( - // (18,26): error CS8176: Iterators cannot have by-reference locals - // foreach (ref int x in new E()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(18, 26)); +}").VerifyEmitDiagnostics(); } [Fact] @@ -3629,10 +3620,7 @@ public static IEnumerable Test() yield return x; } } -}").VerifyDiagnostics( - // (18,35): error CS8176: Iterators cannot have by-reference locals - // foreach (ref readonly int x in new E()) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(18, 35)); +}").VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 9b2f16c772425..2b32616b85553 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -5981,15 +5981,15 @@ static void Main() }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (10,39): error CS4012: Parameters or locals of type 'TypedReference' cannot be declared in async methods or async lambda expressions. + // (10,39): error CS4012: Parameters of type 'TypedReference' cannot be declared in async methods or async lambda expressions. // D1 d1 = async (TypedReference r) => { await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "r").WithArguments("System.TypedReference").WithLocation(10, 39), - // (11,46): error CS4012: Parameters or locals of type 'RuntimeArgumentHandle' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "r").WithArguments("System.TypedReference").WithLocation(10, 39), + // (11,46): error CS4012: Parameters of type 'RuntimeArgumentHandle' cannot be declared in async methods or async lambda expressions. // D2 d2 = async (RuntimeArgumentHandle h) => { await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 46), - // (12,36): error CS4012: Parameters or locals of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 46), + // (12,36): error CS4012: Parameters of type 'ArgIterator' cannot be declared in async methods or async lambda expressions. // D3 d3 = async (ArgIterator i) => { await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "i").WithArguments("System.ArgIterator").WithLocation(12, 36)); + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "i").WithArguments("System.ArgIterator").WithLocation(12, 36)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 74f8efa9b607c..d135ca3ae11dd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -3516,7 +3516,7 @@ static void Main() // (10,31): error CS0190: The __arglist construct is valid only within a variable argument method // Console.WriteLine(__arglist); Diagnostic(ErrorCode.ERR_ArgsInvalid, "__arglist").WithLocation(10, 31), - // (18,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (18,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // Console.WriteLine(__arglist); Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(18, 31), // (24,20): error CS1669: __arglist is not valid in this context @@ -3528,7 +3528,7 @@ static void Main() // (32,20): error CS1669: __arglist is not valid in this context // void Local(__arglist) Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(32, 20), - // (34,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (34,31): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // Console.WriteLine(__arglist); Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(34, 31) ); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs index 9167634ab8a04..9059ff517d961 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefEscapingTests.cs @@ -4962,9 +4962,10 @@ public static unsafe void Test(TestStruct[] ar) [Theory] [InlineData(LanguageVersion.CSharp10)] [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersionFacts.CSharpNext)] public void AwaitRefStruct(LanguageVersion languageVersion) { - CreateCompilation(@" + var comp = CreateCompilation(@" using System.Threading.Tasks; ref struct S { } @@ -4984,20 +4985,67 @@ async Task M(Task t) void M(S t, ref S t1) { } -}", parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.ReleaseDll).VerifyDiagnostics( - // (8,26): error CS0306: The type 'S' may not be used as a type argument - // async Task M(Task t) - Diagnostic(ErrorCode.ERR_BadTypeArgument, "t").WithArguments("S").WithLocation(8, 26), - // (12,9): error CS4012: Parameters or locals of type 'S' cannot be declared in async methods or async lambda expressions. - // var a = await t; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("S").WithLocation(12, 9), - // (14,9): error CS4012: Parameters or locals of type 'S' cannot be declared in async methods or async lambda expressions. - // var r = t.Result; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("S").WithLocation(14, 9), - // (15,9): error CS8350: This combination of arguments to 'C.M(S, ref S)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope - // M(await t, ref r); - Diagnostic(ErrorCode.ERR_CallArgMixing, "M(await t, ref r)").WithArguments("C.M(S, ref S)", "t").WithLocation(15, 9) - ); +}", parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion), options: TestOptions.ReleaseDll); + if (languageVersion < LanguageVersionFacts.CSharpNext) + { + comp.VerifyDiagnostics( + // (8,26): error CS0306: The type 'S' may not be used as a type argument + // async Task M(Task t) + Diagnostic(ErrorCode.ERR_BadTypeArgument, "t").WithArguments("S").WithLocation(8, 26), + // (12,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var a = await t; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(12, 9), + // (14,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var r = t.Result; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "var").WithArguments("ref and unsafe in async and iterator methods").WithLocation(14, 9), + // (15,9): error CS8350: This combination of arguments to 'C.M(S, ref S)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope + // M(await t, ref r); + Diagnostic(ErrorCode.ERR_CallArgMixing, "M(await t, ref r)").WithArguments("C.M(S, ref S)", "t").WithLocation(15, 9) + ); + } + else + { + comp.VerifyDiagnostics( + // (8,26): error CS0306: The type 'S' may not be used as a type argument + // async Task M(Task t) + Diagnostic(ErrorCode.ERR_BadTypeArgument, "t").WithArguments("S").WithLocation(8, 26), + // (15,9): error CS8350: This combination of arguments to 'C.M(S, ref S)' is disallowed because it may expose variables referenced by parameter 't' outside of their declaration scope + // M(await t, ref r); + Diagnostic(ErrorCode.ERR_CallArgMixing, "M(await t, ref r)").WithArguments("C.M(S, ref S)", "t").WithLocation(15, 9) + ); + } + } + + [Fact] + public void AsyncLocals_Reassignment() + { + var code = """ + using System.Threading.Tasks; + class C + { + async Task M1() + { + int x = 42; + ref int y = ref x; + y.ToString(); + await Task.Yield(); + y.ToString(); // 1 + } + async Task M2() + { + int x = 42; + ref int y = ref x; + y.ToString(); + await Task.Yield(); + y = ref x; + y.ToString(); + } + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (10,9): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "y").WithLocation(10, 9)); } [WorkItem(25398, "https://github.com/dotnet/roslyn/issues/25398")] @@ -5338,9 +5386,9 @@ public static IEnumerable Iterator() { } """); compilation.VerifyEmitDiagnostics( - // (20,19): error CS4013: Instance of type 'PooledArrayHandle' cannot be used inside a nested function, query expression, iterator block or async method + // (20,19): error CS4007: Instance of type 'PooledArrayHandle' cannot be preserved across 'await' or 'yield' boundary. // using var handle = RentArray(200, out var array); - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "handle = RentArray(200, out var array)").WithArguments("PooledArrayHandle").WithLocation(20, 19)); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "handle = RentArray(200, out var array)").WithArguments("PooledArrayHandle").WithLocation(20, 19)); } [Theory(Skip = "https://github.com/dotnet/roslyn/issues/40583")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs index 825e55fd10807..6ae6534c5b23f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefLocalsAndReturnsTests.cs @@ -1209,10 +1209,7 @@ async Task M() await Task.FromResult(false); } }"); - comp.VerifyDiagnostics( - // (7,26): error CS8177: Async methods cannot have by-reference locals - // ref readonly int x = ref (new int[1])[0]; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(7, 26)); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -1229,10 +1226,87 @@ IEnumerable M() yield return i; } }"); - comp.VerifyDiagnostics( - // (7,26): error CS8176: Iterators cannot have by-reference locals - // ref readonly int x = ref (new int[1])[0]; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(7, 26)); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void RefReadonlyInIterator_ForEach() + { + var source = """ + using System.Collections.Generic; + class C + { + int[] arr = new int[2]; + IEnumerable M() + { + ref readonly int[] x = ref arr; + + foreach (var i in x) + { + System.Console.Write(i); + yield return 1; + } + + foreach (var j in x) + { + System.Console.Write(j); + yield return 2; + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,28): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int[] x = ref arr; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 28)); + + var expectedDiagnostics = new[] + { + // (15,27): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // foreach (var j in x) + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "x").WithLocation(15, 27) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(expectedDiagnostics); + + CreateCompilation(source).VerifyEmitDiagnostics(expectedDiagnostics); + } + + [Fact] + public void RefReadonlyInIterator_ForEach_02() + { + var source = """ + using System.Collections.Generic; + + foreach (var i in new C().M()) System.Console.Write(i); + + class C + { + int[] arr = new[] { 4, 5 }; + public IEnumerable M() + { + ref readonly int[] x = ref arr; + + foreach (var i in x) + { + System.Console.Write(i); + yield return 1; + } + } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (10,28): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref readonly int[] x = ref arr; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 28)); + + var expectedOutput = "4151"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); } [Fact] @@ -1247,7 +1321,7 @@ IEnumerable M() switch (this) { default: - ref readonly int x = ref (new int[1])[0]; // 1 + ref readonly int x = ref (new int[1])[0]; yield return 1; yield return x; @@ -1260,10 +1334,10 @@ void local() } } }"); - comp.VerifyDiagnostics( - // (10,34): error CS8176: Iterators cannot have by-reference locals - // ref readonly int x = ref (new int[1])[0]; // 1 - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(10, 34)); + comp.VerifyEmitDiagnostics( + // (12,30): error CS9217: A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + // yield return x; + Diagnostic(ErrorCode.ERR_RefLocalAcrossAwait, "x").WithLocation(12, 30)); } [Fact] @@ -1278,22 +1352,19 @@ IEnumerable M() switch (this) { default: - ref readonly int x; // 1, 2 + ref readonly int x; // 1 yield return 1; - yield return x; // 3 + yield return x; // 2 break; } } }"); comp.VerifyDiagnostics( - // (10,34): error CS8176: Iterators cannot have by-reference locals - // ref readonly int x; // 1, 2 - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(10, 34), // (10,34): error CS8174: A declaration of a by-reference variable must have an initializer - // ref readonly int x; // 1, 2 + // ref readonly int x; // 1 Diagnostic(ErrorCode.ERR_ByReferenceVariableMustBeInitialized, "x").WithLocation(10, 34), // (12,30): error CS0165: Use of unassigned local variable 'x' - // yield return x; // 3 + // yield return x; // 2 Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(12, 30)); } @@ -1316,9 +1387,9 @@ IEnumerable M() } }"); comp.VerifyDiagnostics( - // (10,43): error CS8176: Iterators cannot have by-reference locals + // (10,49): error CS1510: A ref or out value must be an assignable variable // foreach (ref readonly int x in (new int[1])) - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(10, 43)); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new int[1]").WithLocation(10, 49)); } [Fact] @@ -1331,18 +1402,15 @@ class C IEnumerable M() { if (true) - ref int x = ref (new int[1])[0]; // 1, 2 + ref int x = ref (new int[1])[0]; // 1 yield return 1; } }"); comp.VerifyDiagnostics( // (8,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13), - // (8,21): error CS8176: Iterators cannot have by-reference locals - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "x").WithLocation(8, 21)); + // ref int x = ref (new int[1])[0]; // 1 + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13)); } [Fact] @@ -1355,18 +1423,15 @@ class C async Task M() { if (true) - ref int x = ref (new int[1])[0]; // 1, 2 + ref int x = ref (new int[1])[0]; // 1 await Task.Yield(); } }"); comp.VerifyDiagnostics( // (8,13): error CS1023: Embedded statement cannot be a declaration or labeled statement - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13), - // (8,21): error CS8177: Async methods cannot have by-reference locals - // ref int x = ref (new int[1])[0]; // 1, 2 - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "x").WithLocation(8, 21)); + // ref int x = ref (new int[1])[0]; // 1 + Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "ref int x = ref (new int[1])[0];").WithLocation(8, 13)); } [Fact] @@ -3088,13 +3153,7 @@ IEnumerable localFunction() } }"; - CreateCompilation(code).VerifyDiagnostics( - // (13,21): error CS8176: Iterators cannot have by-reference locals - // ref int z = ref x; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "z").WithLocation(13, 21), - // (8,17): error CS8176: Iterators cannot have by-reference locals - // ref int y = ref x; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "y").WithLocation(8, 17)); + CreateCompilation(code).VerifyEmitDiagnostics(); } [Fact, WorkItem(13073, "https://github.com/dotnet/roslyn/issues/13073")] @@ -3115,13 +3174,7 @@ await Task.Run(async () => }); } }"; - CreateCompilationWithMscorlib45(code).VerifyDiagnostics( - // (8,17): error CS8177: Async methods cannot have by-reference locals - // ref int y = ref x; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "y").WithLocation(8, 17), - // (11,21): error CS8177: Async methods cannot have by-reference locals - // ref int z = ref x; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "z").WithLocation(11, 21)); + CreateCompilationWithMscorlib45(code).VerifyEmitDiagnostics(); } [Fact, WorkItem(13073, "https://github.com/dotnet/roslyn/issues/13073")] @@ -3434,15 +3487,40 @@ static async void Goo() } "; - CreateCompilationWithMscorlib46(text).VerifyDiagnostics( - // (8,17): error CS8177: Async methods cannot have by-reference locals - // ref int i = ref field; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "i").WithLocation(8, 17), + CreateCompilationWithMscorlib46(text).VerifyEmitDiagnostics( // (6,23): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. // static async void Goo() Diagnostic(ErrorCode.WRN_AsyncLacksAwaits, "Goo").WithLocation(6, 23)); } + [Fact] + public void RefReturnAcrossAwaitExpression() + { + var code = """ + using System.Threading.Tasks; + + class Program + { + static int field = 1; + + static async Task Main() + { + M1(ref GiveMeRef(), await M2()); + } + + static void M1(ref int i, int j) { } + + static ref int GiveMeRef() => ref field; + + static Task M2() => Task.FromResult(field++); + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (9,16): error CS8178: A reference returned by a call to 'Program.GiveMeRef()' cannot be preserved across 'await' or 'yield' boundary. + // M1(ref GiveMeRef(), await M2()); + Diagnostic(ErrorCode.ERR_RefReturningCallAndAwait, "GiveMeRef()").WithArguments("Program.GiveMeRef()").WithLocation(9, 16)); + } + [Fact] public void BadRefLocalInIteratorMethod() { @@ -3461,10 +3539,7 @@ static IEnumerable ObjEnumerable() } "; - CreateCompilationWithMscorlib46(text).VerifyDiagnostics( - // (10,17): error CS8931: Iterators cannot have by-reference locals - // ref int i = ref field; - Diagnostic(ErrorCode.ERR_BadIteratorLocalType, "i").WithLocation(10, 17)); + CreateCompilationWithMscorlib46(text).VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index 50c1950d2a1a4..e83c4cbb6776d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -7269,15 +7269,15 @@ static void Main() }"; var comp = CreateCompilationWithMscorlib40AndSystemCore(source); comp.VerifyDiagnostics( - // (10,14): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (10,14): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // f = x=>f(__arglist); - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle"), - // (11,29): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(10, 14), + // (11,29): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // f = delegate { return f(__arglist); }; - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle"), - // (12,44): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 29), + // (12,44): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // var q = from x in new int[10] select f(__arglist); - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle") + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "__arglist").WithArguments("System.RuntimeArgumentHandle").WithLocation(12, 44) ); } @@ -7304,12 +7304,12 @@ static void Main() } }"; CreateCompilationWithMscorlib45(source).VerifyEmitDiagnostics( - // (10,34): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (10,34): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // RuntimeArgumentHandle h2 = h; // Bad use of h - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle"), - // (11,43): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(10, 34), + // (11,43): error CS4013: Instance of type 'RuntimeArgumentHandle' cannot be used inside a nested function, query expression, iterator block or async method // ArgIterator args1 = new ArgIterator(h); // Bad use of h - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle")); + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h").WithArguments("System.RuntimeArgumentHandle").WithLocation(11, 43)); } [Fact] @@ -7321,9 +7321,9 @@ public void CS4013ERR_SpecialByRefInLambda03() public class C { static void N(RuntimeArgumentHandle x) {} - static IEnumerable M(RuntimeArgumentHandle h1) // Error: hoisted to field + static IEnumerable M(RuntimeArgumentHandle h1) { - N(h1); + N(h1); // Error: hoisted to field yield return 1; RuntimeArgumentHandle h2 = default(RuntimeArgumentHandle); yield return 2; @@ -7339,12 +7339,12 @@ static void Main() CreateCompilation(source).Emit(new System.IO.MemoryStream()).Diagnostics .Verify( - // (7,51): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method - // static IEnumerable M(RuntimeArgumentHandle h1) // Error: hoisted to field - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h1").WithArguments("System.RuntimeArgumentHandle"), - // (13,7): error CS4013: Instance of type 'System.RuntimeArgumentHandle' cannot be used inside an anonymous function, query expression, iterator block or async method + // (9,7): error CS4007: Instance of type 'System.RuntimeArgumentHandle' cannot be preserved across 'await' or 'yield' boundary. + // N(h1); // Error: hoisted to field + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "h1").WithArguments("System.RuntimeArgumentHandle").WithLocation(9, 7), + // (13,7): error CS4007: Instance of type 'System.RuntimeArgumentHandle' cannot be preserved across 'await' or 'yield' boundary. // N(h2); // Error: hoisted to field - Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "h2").WithArguments("System.RuntimeArgumentHandle") + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "h2").WithArguments("System.RuntimeArgumentHandle").WithLocation(13, 7) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs index 922ac7985d750..f2d5e86fc2836 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SpanStackSafetyTests.cs @@ -975,9 +975,9 @@ public static async Task M1(Span arg) CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); comp.VerifyDiagnostics( - // (11,48): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. + // (11,48): error CS4012: Parameters of type 'Span' cannot be declared in async methods or async lambda expressions. // public static async Task M1(Span arg) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "arg").WithArguments("System.Span").WithLocation(11, 48) + Diagnostic(ErrorCode.ERR_BadSpecialByRefParameter, "arg").WithArguments("System.Span").WithLocation(11, 48) ); } @@ -996,48 +996,77 @@ static void Main() public static async Task M1() { - Span local1 = default(Span); // 1 - var local2 = default(Span); // 2 + Span local1 = default(Span); + var local2 = default(Span); await Task.Yield(); return 42; } + + public static async Task M2() + { + Span local1 = default(Span); + var local2 = default(Span); + + await Task.Yield(); + return local1.Length; // 1 + } + + public static async Task M3() + { + Span local1 = default(Span); + var local2 = default(Span); + + await Task.Yield(); + return local2.Length; // 2 + } + + public static async Task M4() + { + Span local1 = default(Span); + var local2 = default(Span); + + await Task.Yield(); + return local1.Length + local2.Length; // 3, 4 + } } "; - CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - - comp.VerifyDiagnostics( - // (13,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // Span local1 = default(Span); // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(13, 9), + var expectedDiagnostics = new[] + { // (13,19): warning CS0219: The variable 'local1' is assigned but its value is never used - // Span local1 = default(Span); // 1 + // Span local1 = default(Span); Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local1").WithArguments("local1").WithLocation(13, 19), - // (14,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(14, 9), // (14,13): warning CS0219: The variable 'local2' is assigned but its value is never used - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(14, 13) - ); + // var local2 = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(14, 13), + // (23,13): warning CS0219: The variable 'local2' is assigned but its value is never used + // var local2 = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(23, 13), + // (26,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length; // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local1").WithArguments("System.Span").WithLocation(26, 16), + // (31,19): warning CS0219: The variable 'local1' is assigned but its value is never used + // Span local1 = default(Span); + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local1").WithArguments("local1").WithLocation(31, 19), + // (35,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local2.Length; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local2").WithArguments("System.Span").WithLocation(35, 16), + // (44,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length + local2.Length; // 3, 4 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local1").WithArguments("System.Span").WithLocation(44, 16), + // (44,32): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length + local2.Length; // 3, 4 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local2").WithArguments("System.Span").WithLocation(44, 32) + }; + + CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); + + comp.VerifyEmitDiagnostics(expectedDiagnostics); comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); - comp.VerifyDiagnostics( - // (13,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // Span local1 = default(Span); // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(13, 9), - // (13,19): warning CS0219: The variable 'local1' is assigned but its value is never used - // Span local1 = default(Span); // 1 - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local1").WithArguments("local1").WithLocation(13, 19), - // (14,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(14, 9), - // (14,13): warning CS0219: The variable 'local2' is assigned but its value is never used - // var local2 = default(Span); // 2 - Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "local2").WithArguments("local2").WithLocation(14, 13) - ); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] @@ -1051,27 +1080,44 @@ public class Program { public static async Task M1() { - M2(out var local1); // 1 - M2(out Span local2); // 2 + M2(out var local1); + M2(out Span local2); await Task.Yield(); return 42; } + public static async Task M3() + { + M2(out var local1); + M2(out Span local2); + + await Task.Yield(); + return local1.Length; // 1 + } + + public static async Task M4() + { + M2(out var local1); + M2(out Span local2); + + await Task.Yield(); + return local2.Length; // 2 + } + static void M2(out Span s) => throw null; } "; CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - comp.VerifyDiagnostics( - // (9,16): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // M2(out var local1); // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(9, 16), - // (10,16): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // M2(out Span local2); // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(10, 16) - ); + comp.VerifyEmitDiagnostics( + // (22,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local1.Length; // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local1").WithArguments("System.Span").WithLocation(22, 16), + // (31,16): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // return local2.Length; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "local2").WithArguments("System.Span").WithLocation(31, 16)); } [Fact, WorkItem(62747, "https://github.com/dotnet/roslyn/issues/62747")] @@ -1086,30 +1132,49 @@ public class Program { public async Task M(IReadOnlyList o) { - foreach (ReadOnlySpan c1 in o) { } // 1 + foreach (ReadOnlySpan c1 in o) { } var enumerator = ((IEnumerable)o).GetEnumerator(); while (enumerator.MoveNext()) { - ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; // 2 + ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; _ = c2.Length; } await Task.Yield(); return; } + + public async Task M2(IReadOnlyList o) + { + foreach (ReadOnlySpan c1 in o) + { + await Task.Yield(); + _ = c1.Length; // 1 + } + + var enumerator = ((IEnumerable)o).GetEnumerator(); + while (enumerator.MoveNext()) + { + ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; + await Task.Yield(); + _ = c2.Length; // 2 + } + + await Task.Yield(); + return; + } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (10,18): error CS4012: Parameters or locals of type 'ReadOnlySpan' cannot be declared in async methods or async lambda expressions. - // foreach (ReadOnlySpan c1 in o) { } // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(10, 18), - // (15,13): error CS4012: Parameters or locals of type 'ReadOnlySpan' cannot be declared in async methods or async lambda expressions. - // ReadOnlySpan c2 = (ReadOnlySpan)(string)enumerator.Current; // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "ReadOnlySpan").WithArguments("System.ReadOnlySpan").WithLocation(15, 13) - ); + comp.VerifyEmitDiagnostics( + // (28,17): error CS4007: Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary. + // _ = c1.Length; // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "c1").WithArguments("System.ReadOnlySpan").WithLocation(28, 17), + // (36,17): error CS4007: Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary. + // _ = c2.Length; // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "c2").WithArguments("System.ReadOnlySpan").WithLocation(36, 17)); } [Fact, WorkItem(62747, "https://github.com/dotnet/roslyn/issues/62747")] @@ -1123,38 +1188,40 @@ public class Program { public async Task M() { - (Span s1, Span s2) = new Program(); // 1, 2 - var (s3, s4) = new Program(); // 3, 4 - (var s5, var s6) = new Program(); // 5, 6 + (Span s1, Span s2) = new Program(); + var (s3, s4) = new Program(); + (var s5, var s6) = new Program(); await Task.Yield(); return; } + public async Task M2() + { + (Span s1, Span s2) = new Program(); + var (s3, s4) = new Program(); + (var s5, var s6) = new Program(); + + await Task.Yield(); + + _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + return; + } + void Deconstruct(out Span s1, out Span s2) => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (9,10): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (Span s1, Span s2) = new Program(); // 1, 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(9, 10), - // (9,24): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (Span s1, Span s2) = new Program(); // 1, 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(9, 24), - // (10,14): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var (s3, s4) = new Program(); // 3, 4 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "s3").WithArguments("System.Span").WithLocation(10, 14), - // (10,18): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // var (s3, s4) = new Program(); // 3, 4 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "s4").WithArguments("System.Span").WithLocation(10, 18), - // (11,10): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (var s5, var s6) = new Program(); // 5, 6 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(11, 10), - // (11,18): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // (var s5, var s6) = new Program(); // 5, 6 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("System.Span").WithLocation(11, 18) - ); + comp.VerifyEmitDiagnostics( + // (25,13): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s1").WithArguments("System.Span").WithLocation(25, 13), + // (25,25): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s4").WithArguments("System.Span").WithLocation(25, 25), + // (25,37): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length + s4.Length + s5.Length; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s5").WithArguments("System.Span").WithLocation(25, 37)); } [Fact, WorkItem(62747, "https://github.com/dotnet/roslyn/issues/62747")] @@ -1167,11 +1234,26 @@ public class Program { public async Task M() { - using (default(RS)) { } // 1 - using (var s1 = default(RS)) { } // 2 - using (RS s2 = default(RS)) { } // 3 - using RS s3 = default(RS); // 4 - using var s4 = default(RS); // 5 + using (default(RS)) { } + using (var s1 = default(RS)) { } + using (RS s2 = default(RS)) { } + using RS s3 = default(RS); // 1 + using var s4 = default(RS); // 2 + + await Task.Yield(); + return; + } + + public async Task M2() + { + using (default(RS)) { await Task.Yield(); } // 3 + using (var s1 = default(RS)) { await Task.Yield(); } // 4 + using (RS s2 = default(RS)) { await Task.Yield(); } // 5 + { + using RS s3 = default(RS); // 6 + await Task.Yield(); + using var s4 = default(RS); + } await Task.Yield(); return; @@ -1184,23 +1266,25 @@ public void Dispose() { } } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (8,16): error CS9104: A using statement resource of type 'RS' cannot be used in async methods or async lambda expressions. - // using (default(RS)) { } // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefUsing, "default(RS)").WithArguments("RS").WithLocation(8, 16), - // (9,16): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using (var s1 = default(RS)) { } // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("RS").WithLocation(9, 16), - // (10,16): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using (RS s2 = default(RS)) { } // 3 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "RS").WithArguments("RS").WithLocation(10, 16), - // (11,15): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using RS s3 = default(RS); // 4 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "RS").WithArguments("RS").WithLocation(11, 15), - // (12,15): error CS4012: Parameters or locals of type 'RS' cannot be declared in async methods or async lambda expressions. - // using var s4 = default(RS); // 5 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "var").WithArguments("RS").WithLocation(12, 15) - ); + comp.VerifyEmitDiagnostics( + // (11,18): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using RS s3 = default(RS); // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s3 = default(RS)").WithArguments("RS").WithLocation(11, 18), + // (12,19): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using var s4 = default(RS); // 2 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s4 = default(RS)").WithArguments("RS").WithLocation(12, 19), + // (20,16): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using (default(RS)) { await Task.Yield(); } // 3 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "default(RS)").WithArguments("RS").WithLocation(20, 16), + // (21,20): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using (var s1 = default(RS)) { await Task.Yield(); } // 4 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s1 = default(RS)").WithArguments("RS").WithLocation(21, 20), + // (22,19): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using (RS s2 = default(RS)) { await Task.Yield(); } // 5 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s2 = default(RS)").WithArguments("RS").WithLocation(22, 19), + // (24,22): error CS4007: Instance of type 'RS' cannot be preserved across 'await' or 'yield' boundary. + // using RS s3 = default(RS); // 6 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s3 = default(RS)").WithArguments("RS").WithLocation(24, 22)); } [Fact] @@ -1214,24 +1298,76 @@ public class Program { public async Task M() { - if (M2() is var s1) { } // 1 - if (M2() is Span s2) { } // 2 + if (M2() is var s1) { } + if (M2() is Span s2) { } + if (M2() is var s3) { await Task.Yield(); } + if (M2() is Span s4) { await Task.Yield(); } await Task.Yield(); return; } + + public async Task M3() + { + if (M2() is var s1) + { + await Task.Yield(); + _ = s1.Length; // 1 + } + if (M2() is Span s2) + { + await Task.Yield(); + _ = s2.Length; // 2 + } + + await Task.Yield(); + return; + } + static Span M2() => throw null; } "; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (9,25): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // if (M2() is var s1) { } // 1 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "s1").WithArguments("System.Span").WithLocation(9, 25), - // (10,21): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // if (M2() is Span s2) { } // 2 - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(10, 21) - ); + comp.VerifyEmitDiagnostics( + // (23,17): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s1.Length; + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s1").WithArguments("System.Span").WithLocation(23, 17), + // (28,17): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // _ = s2.Length; + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s2").WithArguments("System.Span").WithLocation(28, 17)); + } + + [Fact] + public void AsyncLocals_Reassignment() + { + var code = """ + using System; + using System.Threading.Tasks; + class C + { + async Task M1() + { + int x = 42; + Span y = new(ref x); + y.ToString(); + await Task.Yield(); + y.ToString(); // 1 + } + async Task M2() + { + int x = 42; + Span y = new(ref x); + y.ToString(); + await Task.Yield(); + y = new(ref x); + y.ToString(); + } + } + """; + CreateCompilation(code, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( + // (11,9): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "y").WithArguments("System.Span").WithLocation(11, 9)); } [Fact] @@ -1280,19 +1416,18 @@ public static async Task I1() CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - comp.VerifyEmitDiagnostics( - // (17,39): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' + var expectedDiagnostics = new[] + { + // (17,19): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. // TakesSpan(default(Span), await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span") - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "default(Span)").WithArguments("System.Span").WithLocation(17, 19) + }; + + comp.VerifyEmitDiagnostics(expectedDiagnostics); comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); - comp.VerifyEmitDiagnostics( - // (17,39): error CS4007: 'await' cannot be used in an expression containing the type 'System.Span' - // TakesSpan(default(Span), await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span") - ); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] @@ -1331,19 +1466,18 @@ public static async Task I1() CSharpCompilation comp = CreateCompilationWithMscorlibAndSpan(text); - comp.VerifyEmitDiagnostics( - // (14,45): error CS4007: 'await' cannot be used in an expression containing the type 'Span' + var expectedDiagnostics = new[] + { + // (14,22): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. // TakesSpan(s: default(Span), i: await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span").WithLocation(14, 45) - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "default(Span)").WithArguments("System.Span").WithLocation(14, 22) + }; + + comp.VerifyEmitDiagnostics(expectedDiagnostics); comp = CreateCompilationWithMscorlibAndSpan(text, TestOptions.DebugExe); - comp.VerifyEmitDiagnostics( - // (14,45): error CS4007: 'await' cannot be used in an expression containing the type 'Span' - // TakesSpan(s: default(Span), i: await I1()); - Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "await I1()").WithArguments("System.Span").WithLocation(14, 45) - ); + comp.VerifyEmitDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs index d96e6abf25f3b..751ca8595562d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StackAllocInitializerTests.cs @@ -757,13 +757,29 @@ async void M() comp.VerifyDiagnostics( // (8,38): error CS0150: A constant value is expected // Span p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; - Diagnostic(ErrorCode.ERR_ConstantExpected, "await Task.FromResult(1)").WithLocation(8, 38), - // (8,9): error CS4012: Parameters or locals of type 'Span' cannot be declared in async methods or async lambda expressions. - // Span p = stackalloc int[await Task.FromResult(1)] { await Task.FromResult(2) }; - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "Span").WithArguments("System.Span").WithLocation(8, 9) + Diagnostic(ErrorCode.ERR_ConstantExpected, "await Task.FromResult(1)").WithLocation(8, 38) ); } + [Fact] + public void TestAwait_Span_02() + { + var comp = CreateCompilationWithMscorlibAndSpan(""" + using System; + using System.Threading.Tasks; + class Test + { + static async Task Main() + { + Span p = stackalloc int[1] { await Task.FromResult(2) }; + Console.WriteLine(p[0]); + } + } + """, TestOptions.UnsafeReleaseExe); + + CompileAndVerify(comp, expectedOutput: "2", verify: Verification.Fails).VerifyDiagnostics(); + } + [Fact] public void TestSelfInSize() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs index 6ab50856c1131..a8723ce4cb72b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/TopLevelStatementsTests.cs @@ -1113,13 +1113,24 @@ public void LocalDeclarationStatement_18() await System.Threading.Tasks.Task.Yield(); "; + var expectedDiagnostics = new[] + { + // (3,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // ref int d = ref c; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "d").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 9) + }; + var comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: DefaultParseOptions); + comp.VerifyDiagnostics(expectedDiagnostics); - comp.VerifyDiagnostics( - // (3,9): error CS8177: Async methods cannot have by-reference locals - // ref int d = ref c; - Diagnostic(ErrorCode.ERR_BadAsyncLocalType, "d").WithLocation(3, 9) - ); + comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(text, options: TestOptions.DebugExe, parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(text, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs index 3b9371a1c3769..610897b83d561 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UsingStatementTests.cs @@ -1127,12 +1127,67 @@ static async Task Main() } } }"; - var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }).VerifyDiagnostics( - // (16,22): error CS4012: Parameters or locals of type 'S1' cannot be declared in async methods or async lambda expressions. + var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: "Dispose async").VerifyDiagnostics(); + } + + [Fact] + public void UsingPatternAsyncTest_02() + { + var source = """ + using System.Threading.Tasks; + ref struct S1 + { + public ValueTask DisposeAsync() + { + System.Console.WriteLine("Dispose async"); + return new ValueTask(Task.CompletedTask); + } + } + class C2 + { + static async Task Main() + { + await using (S1 c = new S1()) + { + await Task.Yield(); + } + } + } + """; + var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }); + compilation.VerifyEmitDiagnostics( + // 0.cs(14,25): error CS4007: Instance of type 'S1' cannot be preserved across 'await' or 'yield' boundary. // await using (S1 c = new S1()) - Diagnostic(ErrorCode.ERR_BadSpecialByRefLocal, "S1").WithArguments("S1").WithLocation(16, 22) - ); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "c = new S1()").WithArguments("S1").WithLocation(14, 25)); + } + [Fact] + public void UsingPatternAsyncTest_03() + { + var source = """ + using System.Threading.Tasks; + ref struct S1 + { + public S1(int x) { } + public ValueTask DisposeAsync() + { + System.Console.WriteLine("Dispose async"); + return new ValueTask(Task.CompletedTask); + } + } + class C2 + { + static async Task Main() + { + await using (S1 c = new S1(await Task.FromResult(1))) + { + } + } + } + """; + var compilation = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.ReleaseExe); + CompileAndVerify(compilation, expectedOutput: "Dispose async").VerifyDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 1a83fa88bfbd7..a7b6120dc07cc 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -2982,6 +2982,7 @@ public void TestIsBuildOnlyDiagnostic() case ErrorCode.ERR_InterceptableMethodMustBeOrdinary: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYield: case ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait: + case ErrorCode.ERR_RefLocalAcrossAwait: Assert.True(isBuildOnly, $"Check failed for ErrorCode.{errorCode}"); break; diff --git a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs index 8a81b6d09d4b1..6bd117bf73410 100644 --- a/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs +++ b/src/Features/CSharp/Portable/Diagnostics/LanguageServer/CSharpLspBuildOnlyDiagnostics.cs @@ -59,7 +59,8 @@ namespace Microsoft.CodeAnalysis.CSharp.LanguageServer; "CS9178", // ErrorCode.ERR_InterceptorCannotBeGeneric "CS9207", // ErrorCode.ERR_InterceptableMethodMustBeOrdinary "CS8419", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYield - "CS8420" // ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait + "CS8420", // ErrorCode.ERR_PossibleAsyncIteratorWithoutYieldOrAwait + "CS9217" // ErrorCode.ERR_RefLocalAcrossAwait )] [Shared] internal sealed class CSharpLspBuildOnlyDiagnostics : ILspBuildOnlyDiagnostics From de75b3c77d41c21562fc2e9dbcc26b2268c80b26 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 25 Apr 2024 18:50:39 +0200 Subject: [PATCH 004/423] [17.8] Update source build baseline (#73230) --- eng/SourceBuildPrebuiltBaseline.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index bf6d9299a79e6..580cb1b81376b 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -24,6 +24,11 @@ + + + + + From 859d33afa4559f70d851b9bde79eaa12bf182d5e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Fri, 26 Apr 2024 12:50:43 +0000 Subject: [PATCH 005/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 --- eng/Version.Details.xml | 12 ++++++------ global.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 6c1bc8530db77..2e7616c2d8fba 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -110,14 +110,14 @@ - + https://github.com/dotnet/arcade - 188340e12c0a372b1681ad6a5e72c608021efdba + 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 - + https://github.com/dotnet/arcade - 188340e12c0a372b1681ad6a5e72c608021efdba + 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 @@ -144,9 +144,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 188340e12c0a372b1681ad6a5e72c608021efdba + 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 https://github.com/dotnet/roslyn-analyzers diff --git a/global.json b/global.json index 3371f1e96e480..dd2c371a82896 100644 --- a/global.json +++ b/global.json @@ -12,8 +12,8 @@ "xcopy-msbuild": "17.8.1-2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24204.3", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24204.3", + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24225.1", + "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24225.1", "Microsoft.Build.Traversal": "3.4.0" } } From 48f239110a6332a8bc7bef756baa0c255ba4427b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Sat, 27 Apr 2024 12:45:06 +0000 Subject: [PATCH 006/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From 445f1c84a1a129f1bd4b837645d521133a17518f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Sun, 28 Apr 2024 12:35:33 +0000 Subject: [PATCH 007/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From 603f5018dfd5b0f2e27a0e25de5905088b7d0984 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 29 Apr 2024 12:34:20 +0000 Subject: [PATCH 008/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From 81e824d32d72e7bad06b1d13339dd4afdd640697 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 30 Apr 2024 12:49:18 +0000 Subject: [PATCH 009/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From df6aed488cd81515aa6c42715f9bb89f4b8f42a0 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 1 May 2024 12:31:48 +0000 Subject: [PATCH 010/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From 60464713a64ad98779fcf59b44247e4c565381e1 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Tue, 23 Apr 2024 18:04:18 -0700 Subject: [PATCH 011/423] Switch Roslyn protocol types to System.Text.Json serialization --- .../LanguageServerEndpointAttribute.cs | 1 - .../RequestExecutionQueue.cs | 1 - .../CSharpVisualBasicLanguageServerFactory.cs | 2 - ...tocolConversions.MarkdownContentBuilder.cs | 1 - .../CodeCleanup/AbstractCodeCleanupService.cs | 1 - ...CodeFixService.FixAllDiagnosticProvider.cs | 2 - .../CodeFixService.ProjectCodeFixProvider.cs | 2 - .../Features/CodeFixes/ICodeFixService.cs | 1 - .../Diagnostics/DiagnosticAnalyzerService.cs | 2 - .../Diagnostics/DocumentAnalysisExecutor.cs | 1 - ...gnosticIncrementalAnalyzer.AnalysisData.cs | 1 - ...lAnalyzer.IncrementalMemberEditAnalyzer.cs | 1 - ...gnosticIncrementalAnalyzer.ProjectState.cs | 1 - ...gnosticIncrementalAnalyzer.StateManager.cs | 3 - .../DiagnosticIncrementalAnalyzer.StateSet.cs | 1 - .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 1 - ...Span.ProjectAndCompilationWithAnalyzers.cs | 14 - .../FindUsages/SimpleFindUsagesContext.cs | 1 - .../Options/CodeActionOptionsStorage.cs | 1 - .../DocumentationCommentOptionsStorage.cs | 2 - .../GlobalCodeActionOptionsProvider.cs | 3 - .../Options/WorkspaceConfigurationService.cs | 1 - .../UnifiedFixAllCodeFixSuggestedAction.cs | 1 - ...iedFixAllCodeRefactoringSuggestedAction.cs | 1 - .../UnifiedSuggestedAction.cs | 1 - .../Handler/CodeActions/CodeActionHelpers.cs | 2 - .../Handler/CodeActions/CodeActionsHandler.cs | 2 - .../Handler/CodeLens/CodeLensHandler.cs | 2 - .../CodeLens/CodeLensResolveHandler.cs | 2 - .../Handler/Completion/CompletionListCache.cs | 1 - .../ILspCompletionResultCreationService.cs | 1 - .../AbstractGoToDefinitionHandler.cs | 3 - .../AbstractPullDiagnosticHandler.cs | 1 - ...AbstractWorkspacePullDiagnosticsHandler.cs | 3 + .../DocumentPullDiagnosticHandler.cs | 2 + .../Diagnostics/PullDiagnosticConstants.cs | 2 - .../Handler/DocumentChanges/DidOpenHandler.cs | 1 - .../AbstractFormatDocumentHandlerBase.cs | 1 - .../Formatting/FormatDocumentOnTypeHandler.cs | 4 - .../Handler/IDocumentChangeTracker.cs | 2 - .../Handler/InlayHint/InlayHintCache.cs | 1 - .../Handler/InlayHint/InlayHintHandler.cs | 2 - .../InlayHint/InlayHintRefreshQueueFactory.cs | 1 - .../InlayHint/InlayHintResolveHandler.cs | 4 - .../Protocol/Handler/LspErrorCodes.cs | 4 - .../PullHandlers/VersionedPullCache`1.cs | 1 - .../References/FindAllReferencesHandler.cs | 1 - .../References/FindImplementationsHandler.cs | 1 - .../References/FindUsagesLSPContext.cs | 1 - .../Protocol/Handler/RequestContext.cs | 1 - .../SemanticTokensRangeHandler.cs | 1 - .../SpellCheck/DocumentSpellCheckHandler.cs | 1 - ...rpVisualBasicLspServiceFactoryAttribute.cs | 3 - .../LspServices/RoslynLspServiceProvider.cs | 1 - .../Protocol/ApplyWorkspaceEditParams.cs | 10 +- .../Protocol/ApplyWorkspaceEditResponse.cs | 11 +- .../Protocol/Protocol/ClientCapabilities.cs | 16 +- .../Protocol/Protocol/CodeAction.cs | 26 +- .../Protocol/Protocol/CodeActionContext.cs | 14 +- .../Protocol/Protocol/CodeActionKind.cs | 4 +- .../Protocol/CodeActionKindSetting.cs | 5 +- .../Protocol/CodeActionLiteralSetting.cs | 5 +- .../Protocol/Protocol/CodeActionOptions.cs | 16 +- .../Protocol/Protocol/CodeActionParams.cs | 9 +- .../CodeActionResolveSupportSetting.cs | 6 +- .../Protocol/Protocol/CodeActionSetting.cs | 16 +- .../Protocol/CodeActionTriggerKind.cs | 3 - .../Protocol/Protocol/CodeDescription.cs | 6 +- .../Protocol/Protocol/CodeLens.cs | 14 +- .../Protocol/Protocol/CodeLensOptions.cs | 12 +- .../Protocol/Protocol/CodeLensParams.cs | 5 +- .../Protocol/CodeLensWorkspaceSetting.cs | 8 +- .../LanguageServer/Protocol/Protocol/Color.cs | 11 +- .../Protocol/Protocol/ColorInformation.cs | 7 +- .../Protocol/Protocol/Command.cs | 16 +- .../Protocol/Protocol/CompletionContext.cs | 10 +- .../Protocol/Protocol/CompletionItem.cs | 71 ++- .../Protocol/CompletionItemKindSetting.cs | 8 +- .../Protocol/CompletionItemLabelDetails.cs | 12 +- .../Protocol/CompletionItemOptions.cs | 8 +- .../Protocol/CompletionItemSetting.cs | 44 +- .../CompletionItemTagSupportSetting.cs | 6 +- .../Protocol/Protocol/CompletionList.cs | 13 +- .../Protocol/CompletionListItemDefaults.cs | 24 +- .../Protocol/CompletionListSetting.cs | 9 +- .../Protocol/Protocol/CompletionOptions.cs | 24 +- .../Protocol/Protocol/CompletionParams.cs | 12 +- .../Protocol/Protocol/CompletionSetting.cs | 24 +- .../Protocol/CompletionTriggerKind.cs | 3 - .../Protocol/Protocol/ConfigurationItem.cs | 12 +- .../Protocol/Protocol/ConfigurationParams.cs | 5 +- .../Converters/DocumentUriConverter.cs | 65 +-- .../JsonConverterCollectionUtilities.cs | 19 - .../Converters/NaturalObjectConverter.cs | 71 +++ .../ParameterInformationConverter.cs | 82 ++-- .../Converters/StringEnumConverter.cs | 43 +- .../Protocol/Converters/SumConverter.cs | 443 ++++++++++-------- .../Converters/TextDocumentSyncConverter.cs | 117 ++--- .../Protocol/Protocol/CreateFile.cs | 13 +- .../Protocol/Protocol/CreateFileOptions.cs | 12 +- .../Protocol/DefaultBehaviorPrepareRename.cs | 8 +- .../Protocol/Protocol/DefinitionOptions.cs | 8 +- .../Protocol/Protocol/DeleteFile.cs | 13 +- .../Protocol/Protocol/DeleteFileOptions.cs | 13 +- .../Protocol/Protocol/Diagnostic.cs | 32 +- .../Protocol/Protocol/DiagnosticOptions.cs | 16 +- .../Protocol/DiagnosticRegistrationOptions.cs | 12 +- .../Protocol/DiagnosticRelatedInformation.cs | 7 +- .../DiagnosticServerCancellationData.cs | 5 +- .../Protocol/Protocol/DiagnosticSetting.cs | 8 +- .../Protocol/DiagnosticWorkspaceSetting.cs | 8 +- .../Protocol/DidChangeConfigurationParams.cs | 5 +- .../Protocol/DidChangeTextDocumentParams.cs | 7 +- .../Protocol/DidChangeWatchedFilesParams.cs | 6 +- .../Protocol/DidCloseTextDocumentParams.cs | 5 +- .../Protocol/DidOpenTextDocumentParams.cs | 5 +- .../Protocol/DidSaveTextDocumentParams.cs | 10 +- .../Protocol/Protocol/DocumentColorOptions.cs | 8 +- .../Protocol/Protocol/DocumentColorParams.cs | 5 +- .../Protocol/DocumentDiagnosticParams.cs | 18 +- .../DocumentDiagnosticReportPartialResult.cs | 5 +- .../Protocol/Protocol/DocumentFilter.cs | 16 +- .../Protocol/DocumentFormattingOptions.cs | 8 +- .../Protocol/DocumentFormattingParams.cs | 7 +- .../Protocol/Protocol/DocumentHighlight.cs | 10 +- .../Protocol/DocumentHighlightKind.cs | 3 - .../Protocol/DocumentHighlightOptions.cs | 8 +- .../Protocol/DocumentHighlightParams.cs | 7 +- .../Protocol/Protocol/DocumentLink.cs | 10 +- .../Protocol/Protocol/DocumentLinkOptions.cs | 12 +- .../Protocol/Protocol/DocumentLinkParams.cs | 5 +- .../DocumentOnTypeFormattingOptions.cs | 10 +- .../DocumentOnTypeFormattingParams.cs | 7 +- .../DocumentRangeFormattingOptions.cs | 8 +- .../Protocol/DocumentRangeFormattingParams.cs | 9 +- .../Protocol/Protocol/DocumentSymbol.cs | 27 +- .../Protocol/DocumentSymbolOptions.cs | 8 +- .../Protocol/Protocol/DocumentSymbolParams.cs | 5 +- .../Protocol/DocumentSymbolSetting.cs | 12 +- .../Protocol/DynamicRegistrationSetting.cs | 8 +- .../Protocol/ExecuteCommandOptions.cs | 10 +- .../Protocol/Protocol/ExecuteCommandParams.cs | 10 +- .../Converters/VSExtensionConverter.cs | 47 +- .../Converters/VSExtensionUtilities.cs | 80 ++-- .../Protocol/Extensions/VSDiagnostic.cs | 32 +- .../VSDiagnosticProjectInformation.cs | 16 +- .../Extensions/VSGetProjectContextsParams.cs | 5 +- .../Protocol/Protocol/Extensions/VSImageId.cs | 7 +- .../Protocol/Extensions/VSLocation.cs | 12 +- .../Protocol/Extensions/VSProjectContext.cs | 11 +- .../Extensions/VSProjectContextList.cs | 7 +- .../Protocol/Extensions/VSProjectKind.cs | 3 - .../Extensions/VSServerCapabilities.cs | 8 +- .../Extensions/VSSymbolInformation.cs | 16 +- .../Extensions/VSTextDocumentIdentifier.cs | 8 +- .../Protocol/Protocol/FileEvent.cs | 8 +- .../Protocol/Protocol/FoldingRange.cs | 24 +- .../Protocol/Protocol/FoldingRangeKind.cs | 4 +- .../Protocol/Protocol/FoldingRangeOptions.cs | 8 +- .../Protocol/Protocol/FoldingRangeParams.cs | 5 +- .../Protocol/Protocol/FoldingRangeSetting.cs | 16 +- .../Protocol/FoldingRangeSettingOptions.cs | 8 +- .../Protocol/Protocol/FormattingOptions.cs | 9 +- .../Protocol/FullDocumentDiagnosticReport.cs | 12 +- .../LanguageServer/Protocol/Protocol/Hover.cs | 10 +- .../Protocol/Protocol/HoverOptions.cs | 8 +- .../Protocol/Protocol/HoverSetting.cs | 8 +- .../Protocol/ImplementationOptions.cs | 8 +- .../Protocol/Protocol/InitializeError.cs | 5 +- .../Protocol/Protocol/InitializeErrorCode.cs | 3 - .../Protocol/Protocol/InitializeParams.cs | 29 +- .../Protocol/Protocol/InitializeResult.cs | 5 +- .../Protocol/Protocol/InlayHint.cs | 32 +- .../Protocol/Protocol/InlayHintKind.cs | 3 - .../Protocol/Protocol/InlayHintLabelPart.cs | 18 +- .../Protocol/Protocol/InlayHintOptions.cs | 12 +- .../Protocol/Protocol/InlayHintParams.cs | 7 +- .../Protocol/InlayHintRegistrationOptions.cs | 12 +- .../InlayHintResolveSupportSetting.cs | 5 +- .../Protocol/Protocol/InlayHintSetting.cs | 8 +- .../Protocol/InlayHintWorkspaceSetting.cs | 8 +- .../Protocol/Protocol/InsertReplaceEdit.cs | 12 +- .../Protocol/Protocol/InsertReplaceRange.cs | 9 +- .../Protocol/InsertTextModeSupportSetting.cs | 6 +- .../ClassifiedTextElementConverter.cs | 104 ++-- .../Converters/ClassifiedTextRunConverter.cs | 95 ++-- .../Converters/ContainerElementConverter.cs | 119 +++-- .../Converters/DropProgressConverter.cs | 52 -- .../Converters/ImageElementConverter.cs | 99 ++-- .../Internal/Converters/ImageIdConverter.cs | 66 +-- .../Converters/ObjectContentConverter.cs | 172 +++---- .../Internal/Converters/RegexConverter.cs | 56 +-- .../VSCodeInternalExtensionUtilities.cs | 30 +- .../VSInternalExtensionUtilities.cs | 107 ++--- .../Diagnostics/VSInternalDiagnosticKind.cs | 5 +- .../VSInternalDiagnosticOptions.cs | 20 +- .../Diagnostics/VSInternalDiagnosticParams.cs | 16 +- .../Diagnostics/VSInternalDiagnosticReport.cs | 28 +- .../VSInternalDocumentDiagnosticsParams.cs | 12 +- .../VSInternalWorkspaceDiagnosticReport.cs | 6 +- .../VSInternalWorkspaceDiagnosticsParams.cs | 20 +- .../Efficiency/OptimizedVSCompletionList.cs | 4 +- .../OptimizedVSCompletionListJsonConverter.cs | 396 ++++++++-------- .../Internal/Text/ClassifiedTextElement.cs | 1 - .../Internal/VSFoldingRangeSetting.cs | 7 +- .../Internal/VSInternalClientCapabilities.cs | 24 +- .../Internal/VSInternalClipboardContent.cs | 9 +- .../Protocol/Internal/VSInternalCodeAction.cs | 24 +- .../Internal/VSInternalCodeActionContext.cs | 8 +- .../VSInternalCodeActionGroupSetting.cs | 5 +- .../VSInternalCodeActionLiteralSetting.cs | 8 +- .../Internal/VSInternalCommitCharacter.cs | 7 +- .../Internal/VSInternalCompletionContext.cs | 8 +- .../VSInternalCompletionInvokeKind.cs | 3 - .../Internal/VSInternalCompletionItem.cs | 20 +- .../Internal/VSInternalCompletionList.cs | 20 +- .../VSInternalCompletionListSetting.cs | 12 +- .../Internal/VSInternalCompletionSetting.cs | 8 +- .../VSInternalContinueCharacterClass.cs | 12 +- .../VSInternalContinueCharacterRange.cs | 16 +- .../VSInternalContinueCharacterSingle.cs | 12 +- .../VSInternalDocumentOnAutoInsertOptions.cs | 5 +- .../VSInternalDocumentOnAutoInsertParams.cs | 11 +- ...nternalDocumentOnAutoInsertResponseItem.cs | 12 +- .../VSInternalDocumentSpellCheckableParams.cs | 10 +- ...nternalExecuteCommandClientCapabilities.cs | 5 +- .../Protocol/Internal/VSInternalHover.cs | 7 +- .../Internal/VSInternalIconMapping.cs | 12 +- .../VSInternalInlineCompletionContext.cs | 12 +- .../VSInternalInlineCompletionItem.cs | 20 +- .../VSInternalInlineCompletionList.cs | 7 +- .../VSInternalInlineCompletionOptions.cs | 9 +- .../VSInternalInlineCompletionRequest.cs | 17 +- .../VSInternalInlineCompletionTriggerKind.cs | 3 - .../Internal/VSInternalKindAndModifier.cs | 10 +- .../Protocol/Internal/VSInternalLocation.cs | 8 +- .../Internal/VSInternalMapCodeMapping.cs | 14 +- .../Internal/VSInternalMapCodeParams.cs | 10 +- .../Internal/VSInternalProjectContext.cs | 8 +- .../Internal/VSInternalReferenceItem.cs | 53 +-- .../Internal/VSInternalReferenceKind.cs | 2 - .../Internal/VSInternalReferenceParams.cs | 7 +- .../VSInternalRenameOptionSelection.cs | 12 +- .../Internal/VSInternalRenameOptionSupport.cs | 16 +- .../Internal/VSInternalRenameParams.cs | 8 +- .../Internal/VSInternalRenameRange.cs | 8 +- .../VSInternalSelectedCompletionInfo.cs | 19 +- .../Internal/VSInternalServerCapabilities.cs | 58 ++- .../VSInternalSignatureInformation.cs | 8 +- .../VSInternalSpellCheckableRangeReport.cs | 13 +- .../Internal/VSInternalStreamingParams.cs | 11 +- .../Internal/VSInternalSymbolInformation.cs | 8 +- ...SInternalTextDocumentClientCapabilities.cs | 7 +- ...InternalTextDocumentRegistrationOptions.cs | 8 +- .../VSInternalTextPresentationParams.cs | 14 +- .../VSInternalUriPresentationParams.cs | 16 +- .../VSInternalValidateBreakableRangeParams.cs | 7 +- ...VSInternalWorkspaceSpellCheckableParams.cs | 14 +- ...VSInternalWorkspaceSpellCheckableReport.cs | 11 +- .../Protocol/LinkedEditingRangeOptions.cs | 8 +- .../Protocol/LinkedEditingRangeParams.cs | 3 - .../Protocol/Protocol/LinkedEditingRanges.cs | 10 +- .../Protocol/Protocol/Location.cs | 8 +- .../Protocol/Protocol/LogMessageParams.cs | 7 +- .../Protocol/Protocol/MarkedString.cs | 12 +- .../Protocol/Protocol/MarkupContent.cs | 7 +- .../Protocol/Protocol/MarkupKind.cs | 4 +- .../Protocol/Protocol/MessageActionItem.cs | 5 +- .../Protocol/Protocol/MessageType.cs | 3 - ...OptionalVersionedTextDocumentIdentifier.cs | 8 +- .../Protocol/Protocol/ParameterInformation.cs | 10 +- .../Protocol/ParameterInformationSetting.cs | 8 +- .../Protocol/Protocol/Position.cs | 8 +- .../Protocol/Protocol/PrepareRenameParams.cs | 7 +- .../Protocol/PrepareSupportDefaultBehavior.cs | 3 - .../Protocol/Protocol/PreviousResultId.cs | 8 +- .../Protocol/PublishDiagnosticParams.cs | 8 +- .../Protocol/PublishDiagnosticsSetting.cs | 8 +- .../LanguageServer/Protocol/Protocol/Range.cs | 12 +- .../Protocol/Protocol/ReferenceContext.cs | 5 +- .../Protocol/Protocol/ReferenceOptions.cs | 8 +- .../Protocol/Protocol/ReferenceParams.cs | 10 +- .../Protocol/Protocol/Registration.cs | 12 +- .../Protocol/Protocol/RegistrationParams.cs | 5 +- .../RelatedFullDocumentDiagnosticReport.cs | 8 +- ...elatedUnchangedDocumentDiagnosticReport.cs | 8 +- .../Protocol/RenameClientCapabilities.cs | 19 +- .../Protocol/Protocol/RenameFile.cs | 16 +- .../Protocol/Protocol/RenameFileOptions.cs | 12 +- .../Protocol/Protocol/RenameOptions.cs | 12 +- .../Protocol/Protocol/RenameParams.cs | 5 +- .../Protocol/Protocol/RenameRange.cs | 12 +- .../Protocol/ResolveSupportSetting.cs | 6 +- .../Protocol/ResourceOperationKind.cs | 4 +- .../Protocol/Protocol/SaveOptions.cs | 8 +- .../SemanticTokens/SemanticTokenFormat.cs | 4 +- .../Protocol/SemanticTokens/SemanticTokens.cs | 11 +- .../SemanticTokens/SemanticTokensDelta.cs | 12 +- .../SemanticTokensDeltaParams.cs | 12 +- .../SemanticTokensDeltaPartialResult.cs | 8 +- .../SemanticTokens/SemanticTokensEdit.cs | 12 +- .../SemanticTokensFullOptions.cs | 8 +- .../SemanticTokens/SemanticTokensLegend.cs | 7 +- .../SemanticTokens/SemanticTokensOptions.cs | 18 +- .../SemanticTokens/SemanticTokensParams.cs | 10 +- .../SemanticTokensPartialResult.cs | 6 +- .../SemanticTokensRangeParams.cs | 5 +- .../SemanticTokensRequestsFullSetting.cs | 8 +- .../SemanticTokensRequestsSetting.cs | 12 +- .../SemanticTokens/SemanticTokensSetting.cs | 20 +- .../SemanticTokensWorkspaceSetting.cs | 8 +- .../Protocol/Protocol/ServerCapabilities.cs | 106 ++--- .../Protocol/Protocol/ShowMessageParams.cs | 7 +- .../Protocol/ShowMessageRequestParams.cs | 8 +- .../Protocol/Protocol/SignatureHelp.cs | 16 +- .../Protocol/Protocol/SignatureHelpContext.cs | 16 +- .../Protocol/Protocol/SignatureHelpOptions.cs | 16 +- .../Protocol/Protocol/SignatureHelpParams.cs | 8 +- .../Protocol/Protocol/SignatureHelpSetting.cs | 12 +- .../Protocol/SignatureHelpTriggerKind.cs | 3 - .../Protocol/Protocol/SignatureInformation.cs | 14 +- .../Protocol/SignatureInformationSetting.cs | 12 +- .../Protocol/Protocol/SumType.cs | 4 +- .../Protocol/Protocol/SymbolInformation.cs | 14 +- .../Protocol/Protocol/SymbolKind.cs | 4 - .../Protocol/Protocol/SymbolKindSetting.cs | 8 +- .../Protocol/Protocol/SymbolSetting.cs | 8 +- .../Protocol/SynchronizationSetting.cs | 16 +- .../Protocol/Protocol/TagSupport.cs | 5 +- .../TextDocumentClientCapabilities.cs | 94 ++-- .../TextDocumentContentChangeEvent.cs | 12 +- .../Protocol/Protocol/TextDocumentEdit.cs | 9 +- .../Protocol/TextDocumentIdentifier.cs | 6 +- .../Protocol/Protocol/TextDocumentItem.cs | 12 +- .../Protocol/TextDocumentPositionParams.cs | 7 +- .../TextDocumentRegistrationOptions.cs | 7 +- .../Protocol/TextDocumentSaveReason.cs | 4 - .../Protocol/Protocol/TextDocumentSyncKind.cs | 3 - .../Protocol/TextDocumentSyncOptions.cs | 24 +- .../Protocol/Protocol/TextEdit.cs | 8 +- .../Protocol/Protocol/TraceSetting.cs | 2 +- .../Protocol/TypeDefinitionOptions.cs | 8 +- .../UnchangedDocumentDiagnosticReport.cs | 7 +- .../Protocol/Protocol/Unregistration.cs | 7 +- .../Protocol/Protocol/UnregistrationParams.cs | 5 +- .../VersionedTextDocumentIdentifier.cs | 6 +- .../Protocol/WillSaveTextDocumentParams.cs | 7 +- .../Protocol/WorkspaceClientCapabilities.cs | 48 +- .../Protocol/WorkspaceDiagnosticParams.cs | 14 +- .../Protocol/WorkspaceDiagnosticReport.cs | 8 +- .../WorkspaceDiagnosticReportPartialResult.cs | 8 +- .../Protocol/Protocol/WorkspaceEdit.cs | 12 +- .../Protocol/Protocol/WorkspaceEditSetting.cs | 12 +- .../WorkspaceFullDocumentDiagnosticReport.cs | 8 +- .../Protocol/WorkspaceSymbolOptions.cs | 8 +- .../Protocol/WorkspaceSymbolParams.cs | 10 +- ...kspaceUnchangedDocumentDiagnosticReport.cs | 8 +- .../LspMiscellaneousFilesWorkspace.cs | 4 - .../Workspaces/LspWorkspaceManager.cs | 1 - 359 files changed, 2410 insertions(+), 3212 deletions(-) delete mode 100644 src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs create mode 100644 src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.cs delete mode 100644 src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs index a7bb1e13ac058..9f2a608ea16fc 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/LanguageServerEndpointAttribute.cs @@ -6,7 +6,6 @@ #nullable enable using System; -using System.Linq; namespace Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs index cd200a1ca0ec7..60f3bf3271c00 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs @@ -12,7 +12,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Threading; -using Newtonsoft.Json.Linq; namespace Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs index 5e2276385b282..797509201331c 100644 --- a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Composition; -using System.IO; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs index b72b3e00097fa..03bf62eacb2e4 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.MarkdownContentBuilder.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs b/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs index 6e8e167de9c82..6348b6ae6be5e 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeCleanup/AbstractCodeCleanupService.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs index 1d82aa9383dad..9d82ea0fa633e 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.FixAllDiagnosticProvider.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs index fde0bc07d9891..b2c78656b3497 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.ProjectCodeFixProvider.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CodeFixes diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs index e0c55f84e1acf..609d36eb9ee08 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/ICodeFixService.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeFixes diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index be32623daf749..69aa85d102140 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Runtime.CompilerServices; @@ -18,7 +17,6 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics { diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs index 321e5cb7ee1f2..d5de93548ef16 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs index 0afc2829fd4ba..3be6f73e60c14 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.AnalysisData.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs index fd15d29e0bff6..4abe7eb44d1e0 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.IncrementalMemberEditAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 172fb7357b49e..1f45073d32495 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics; using System.Threading; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs index fe01283879f67..6e0b2fc05ab45 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.cs @@ -7,10 +7,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs index 9a2f24933bc04..c7e696eb8d421 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateSet.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 1ddadcfbd2bf1..c42de66646961 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs index 0e9dfb571229b..dd86515b9604f 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnosticsForSpan.ProjectAndCompilationWithAnalyzers.cs @@ -2,20 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { internal partial class DiagnosticIncrementalAnalyzer diff --git a/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs b/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs index 9a607836aa66c..955b3b7eb142d 100644 --- a/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs +++ b/src/Features/LanguageServer/Protocol/Features/FindUsages/SimpleFindUsagesContext.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Notification; namespace Microsoft.CodeAnalysis.FindUsages { diff --git a/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs index bb07b3685be4a..50fd4952d3793 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/CodeActionOptionsStorage.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Xml.Serialization; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs index 514a457c471a0..ef07ceee6afd4 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/DocumentationCommentOptionsStorage.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs b/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs index efa334e23c319..5217e63350ad3 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/GlobalCodeActionOptionsProvider.cs @@ -2,9 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; diff --git a/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs b/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs index 3b6ffa92508a1..d89464da63ae2 100644 --- a/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Options/WorkspaceConfigurationService.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs index 3d6fc0a911d31..6fe985eff906c 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeFixSuggestedAction.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs index 11f6993cb7301..c6806df2a42b2 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedFixAllCodeRefactoringSuggestedAction.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; -using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.UnifiedSuggestions.UnifiedSuggestedActions; namespace Microsoft.CodeAnalysis.UnifiedSuggestions diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs index a0c207fefe169..1e85c481f5a77 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActions/UnifiedSuggestedAction.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.CodeActions; namespace Microsoft.CodeAnalysis.UnifiedSuggestions diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs index 44136ac1f6479..0b8ff43d64255 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionHelpers.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -16,7 +15,6 @@ using Microsoft.CodeAnalysis.UnifiedSuggestions; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; -using StreamJsonRpc; using CodeAction = Microsoft.CodeAnalysis.CodeActions.CodeAction; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs index 0e0ac5a264fec..c13a4e7e1d649 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionsHandler.cs @@ -11,10 +11,8 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Options; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs index 9bb72371dbfa3..116e30bea6158 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensHandler.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; @@ -18,7 +17,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using LSP = Roslyn.LanguageServer.Protocol; using Microsoft.CodeAnalysis.Text; -using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index 930bf05308462..39d3299223f3c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -2,13 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; using Newtonsoft.Json.Linq; using Roslyn.Utilities; -using StreamJsonRpc; using Microsoft.CodeAnalysis.Shared.Extensions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs index eae62bc03704a..4c739a05c89e0 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionListCache.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.Completion; using static Microsoft.CodeAnalysis.LanguageServer.Handler.Completion.CompletionListCache; -using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Completion { diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs index 755d8e8f90813..5fec8b6b20c28 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/ILspCompletionResultCreationService.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Text; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Completion diff --git a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs index 83948cec1e616..9734dafe3a332 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Definitions/AbstractGoToDefinitionHandler.cs @@ -2,12 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.GoToDefinition; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 94bb3f537e1c9..000568c965123 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs index 7e3d6e4619791..ab6ada210db7c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractWorkspacePullDiagnosticsHandler.cs @@ -9,6 +9,9 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSources; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.CodeAnalysis.TaskList; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index 16ee2f87d2bf2..050cfe582e16e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.DiagnosticSources; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs index 0384599ba952f..2a0b22abfaf86 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/PullDiagnosticConstants.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; - namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; internal static class PullDiagnosticConstants diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index 54a811861cab1..b75b6c9793e95 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index 806e79597b426..a9db06c55f7c1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 0c60cfe6e1fa8..a3762bb7fe1ad 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; @@ -15,9 +13,7 @@ using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { diff --git a/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs b/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs index 275c707cdb142..ff2410d5277d7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs +++ b/src/Features/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs @@ -5,10 +5,8 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges; using Microsoft.CodeAnalysis.Text; -using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs index 44c6877e471a6..c15f24183032d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintCache.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.InlineHints; -using Roslyn.LanguageServer.Protocol; using static Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint.InlayHintCache; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs index 8da7e981f854a..08025830a4bca 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintHandler.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; @@ -14,7 +13,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Roslyn.LanguageServer.Protocol; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs index 232a9858c26ac..361bbc4f432e7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintRefreshQueueFactory.cs @@ -5,7 +5,6 @@ using System; using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index 75cf236aee41c..d0e81cc719cfe 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -3,13 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Drawing; -using System.Reflection.Metadata; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.InlineHints; -using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; using Roslyn.LanguageServer.Protocol; using Newtonsoft.Json.Linq; using Roslyn.Utilities; diff --git a/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs b/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs index 8a4c55e4ca7bb..af0a848d1d9ac 100644 --- a/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs +++ b/src/Features/LanguageServer/Protocol/Handler/LspErrorCodes.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Text; - namespace Microsoft.CodeAnalysis.LanguageServer.Handler; /// diff --git a/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs b/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs index c4fdd4c88e8d2..c9ca5e63f60ef 100644 --- a/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs +++ b/src/Features/LanguageServer/Protocol/Handler/PullHandlers/VersionedPullCache`1.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs index 8e24d9425f109..bdfdaf086746d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/References/FindAllReferencesHandler.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs index 80e447be3956f..343299bec39e4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/References/FindImplementationsHandler.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler diff --git a/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs b/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs index 9e57963e23ef3..c562f50912455 100644 --- a/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs +++ b/src/Features/LanguageServer/Protocol/Handler/References/FindUsagesLSPContext.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs index f0610290edbde..a9426705cc246 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs index 899203510b20d..4ee36deb369e7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs index 541ffc8888b7b..b294d4998aa67 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SpellCheck/DocumentSpellCheckHandler.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using Roslyn.LanguageServer.Protocol; diff --git a/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs b/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs index 641e7f1953f1d..e833c82c7047f 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/ExportCSharpVisualBasicLspServiceFactoryAttribute.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Composition; -using System.Linq; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs b/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs index 28e824ca250fd..4319937d232ab 100644 --- a/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs +++ b/src/Features/LanguageServer/Protocol/LspServices/RoslynLspServiceProvider.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Composition; -using System.Text; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs index 7e4930168f914..2ede34011caaf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditParams.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from a server to a client for the workspace/applyEdit request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ApplyWorkspaceEditParams { /// /// Gets or sets the label associated with this edit. /// - [DataMember(Name = "label")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("label")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Label { get; @@ -29,7 +27,7 @@ public string? Label /// /// Gets or sets the edit to be applied to the workspace. /// - [DataMember(Name = "edit")] + [JsonPropertyName("edit")] public WorkspaceEdit Edit { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs index 080ce8850873f..0fb22929337ba 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ApplyWorkspaceEditResponse.cs @@ -4,21 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response sent for a workspace/applyEdit request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] + internal class ApplyWorkspaceEditResponse { /// /// Gets or sets a value indicating whether edits were applied or not. /// - [DataMember(Name = "applied")] + [JsonPropertyName("applied")] public bool Applied { get; @@ -28,8 +27,8 @@ public bool Applied /// /// Gets or sets a string with textual description for why the edit was not applied. /// - [DataMember(Name = "failureReason")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("failureReason")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FailureReason { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs index aa135aefed881..ba9367aea66dd 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ClientCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents client capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ClientCapabilities { /// /// Gets or sets the workspace capabilities. /// - [DataMember(Name = "workspace")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("workspace")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceClientCapabilities? Workspace { get; @@ -29,8 +27,8 @@ public WorkspaceClientCapabilities? Workspace /// /// Gets or sets the text document capabilities. /// - [DataMember(Name = "textDocument")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textDocument")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextDocumentClientCapabilities? TextDocument { get; @@ -40,8 +38,8 @@ public TextDocumentClientCapabilities? TextDocument /// /// Gets or sets the experimental capabilities. /// - [DataMember(Name = "experimental")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("experimental")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Experimental { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs index 86df1605432f0..9ac8ab11410e9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeAction.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A class representing a change that can be performed in code. A CodeAction must either set @@ -14,13 +13,12 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeAction { /// /// Gets or sets the human readable title for this code action. /// - [DataMember(Name = "title")] + [JsonPropertyName("title")] public string Title { get; @@ -30,8 +28,8 @@ public string Title /// /// Gets or sets the kind of code action this instance represents. /// - [DataMember(Name = "kind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("kind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionKind? Kind { get; @@ -41,8 +39,8 @@ public CodeActionKind? Kind /// /// Gets or sets the diagnostics that this code action resolves. /// - [DataMember(Name = "diagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Diagnostic[]? Diagnostics { get; @@ -52,8 +50,8 @@ public Diagnostic[]? Diagnostics /// /// Gets or sets the workspace edit that this code action performs. /// - [DataMember(Name = "edit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("edit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceEdit? Edit { get; @@ -63,8 +61,8 @@ public WorkspaceEdit? Edit /// /// Gets or sets the command that this code action executes. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; @@ -74,8 +72,8 @@ public Command? Command /// /// Gets or sets the data that will be resend to the server if the code action is selected to be resolved. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs index 2ccf570a49f2d..e40c24c7a3bd6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing diagnostic information about the context of a code action /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionContext { /// /// Gets or sets an array of diagnostics relevant to a code action. /// - [DataMember(Name = "diagnostics")] + [JsonPropertyName("diagnostics")] public Diagnostic[] Diagnostics { get; @@ -28,8 +26,8 @@ public Diagnostic[] Diagnostics /// /// Gets or sets an array of code action kinds to filter for. /// - [DataMember(Name = "only")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("only")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionKind[]? Only { get; @@ -39,8 +37,8 @@ public CodeActionKind[]? Only /// /// Gets or sets the indicating how the code action was triggered.. /// - [DataMember(Name = "triggerKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionTriggerKind? TriggerKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs index 264756a446c2c..c6c8663e284b2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the kind of a code action. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct CodeActionKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs index 1ec354679a037..03806a7c4c696 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionKindSetting.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class containing the set of code action kinds that are supported. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionKindSetting { /// /// Gets or sets the code actions kinds the client supports. /// - [DataMember(Name = "valueSet")] + [JsonPropertyName("valueSet")] public CodeActionKind[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs index 92e1cc4a4f9b7..adc2cf397db90 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionLiteralSetting.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing support for code action literals. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionLiteralSetting { /// /// Gets or sets a value indicating what code action kinds are supported. /// - [DataMember(Name = "codeActionKind")] + [JsonPropertyName("codeActionKind")] public CodeActionKindSetting CodeActionKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs index 3b32c4f3ec6bf..e63a4852b80e8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionOptions.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the registration options for code actions support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionOptions : IWorkDoneProgressOptions { /// @@ -22,8 +20,8 @@ internal class CodeActionOptions : IWorkDoneProgressOptions /// The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server /// may list out every specific kind they provide. /// - [DataMember(Name = "codeActionKinds")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeActionKinds")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionKind[]? CodeActionKinds { get; @@ -33,16 +31,16 @@ public CodeActionKind[]? CodeActionKinds /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets a value indicating whether the server provides support to resolve /// additional information for a code action. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs index 67f546f31ba48..70e9f70d93c4a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for the textDocument/codeAction request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionParams : ITextDocumentParams { /// /// Gets or sets the document identifier indicating where the command was invoked. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range in the document for which the command was invoked. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -37,7 +36,7 @@ public Range Range /// /// Gets or sets the additional diagnostic information about the code action context. /// - [DataMember(Name = "context")] + [JsonPropertyName("context")] public CodeActionContext Context { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs index 6649a9adf92c9..1d1280be4d074 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionResolveSupportSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing settings for codeAction/resolve support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionResolveSupportSetting { /// /// Gets or sets a value indicating the properties that a client can resolve lazily. /// - [DataMember(Name = "properties")] + [JsonPropertyName("properties")] public string[] Properties { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs index 4af0f9f18dd66..eba4e7a023559 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing settings for code action support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeActionSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating the client supports code action literals. /// - [DataMember(Name = "codeActionLiteralSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeActionLiteralSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionLiteralSetting? CodeActionLiteralSupport { get; @@ -31,8 +29,8 @@ public CodeActionLiteralSetting? CodeActionLiteralSupport /// additional code action properties via a separate `codeAction/resolve` /// request. /// - [DataMember(Name = "resolveSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resolveSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionResolveSupportSetting? ResolveSupport { get; @@ -44,8 +42,8 @@ public CodeActionResolveSupportSetting? ResolveSupport /// property which is preserved between a `textDocument/codeAction` and a /// `codeAction/resolve` request. /// - [DataMember(Name = "dataSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("dataSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DataSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs index 14413d38f3ead..ed48b55932d33 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeActionTriggerKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various reason why code actions were requested. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum CodeActionTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs index b91705db37fb0..d7c414285753e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeDescription.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a description for an error code. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeDescription : IEquatable { /// /// Gets or sets URI to open with more information about the diagnostic error. /// - [DataMember(Name = "href")] + [JsonPropertyName("href")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Href { diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs index c88d783e26953..4bec557ca0a10 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLens.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A class representing a code lens command that should be shown alongside source code. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLens { /// /// Gets or sets the range that the code lens applies to. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -28,8 +26,8 @@ public Range Range /// /// Gets or sets the command associated with this code lens. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; @@ -39,8 +37,8 @@ public Command? Command /// /// Gets or sets the data that should be preserved between a textDocument/codeLens request and a codeLens/resolve request. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs index eddb78e41bf39..22ac4b4cbfb1c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLensOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for code lens support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLensOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether or not the code lens support has a resolve provider. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; @@ -29,8 +27,8 @@ public bool ResolveProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs index d58fea7b19dec..a2ee3f149fbc2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLensParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for a textDocument/codeLens request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLensParams : ITextDocumentParams { /// /// Gets or sets the document identifier to fetch code lens results for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs index 76543e6b1a334..444bf3390dabe 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CodeLensWorkspaceSetting.cs @@ -2,8 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Newtonsoft.Json; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol { @@ -12,14 +11,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CodeLensWorkspaceSetting { /// /// Gets or sets a value indicating whether the client supports a refresh request sent from the server to the client. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Color.cs b/src/Features/LanguageServer/Protocol/Protocol/Color.cs index bda5111332f02..319d60cf17462 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Color.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Color.cs @@ -4,14 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a color. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Color { /// @@ -20,7 +19,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "red")] + [JsonPropertyName("red")] public decimal Red { get; set; } /// @@ -29,7 +28,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "green")] + [JsonPropertyName("green")] public decimal Green { get; set; } /// @@ -38,7 +37,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "blue")] + [JsonPropertyName("blue")] public decimal Blue { get; set; } /// @@ -47,7 +46,7 @@ internal class Color /// /// Value should be clamped to [0,1]. /// - [DataMember(Name = "alpha")] + [JsonPropertyName("alpha")] public decimal Alpha { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs index e2cf2f0433fad..a55e45f655455 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ColorInformation.cs @@ -4,26 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents color information. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ColorInformation { /// /// Gets or sets the text range representing the color. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; set; } /// /// Gets or sets the color. /// - [DataMember(Name = "color")] + [JsonPropertyName("color")] public Color Color { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Command.cs b/src/Features/LanguageServer/Protocol/Protocol/Command.cs index d3effcdb1b3ae..eefac1539a6f7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Command.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Command.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a reference to a command /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Command { /// /// Gets or sets the title of the command. /// - [DataMember(Name = "title")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("title")] + [JsonRequired] public string Title { get; @@ -29,8 +27,8 @@ public string Title /// /// Gets or sets the identifier associated with the command. /// - [DataMember(Name = "command")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("command")] + [JsonRequired] public string CommandIdentifier { get; @@ -40,8 +38,8 @@ public string CommandIdentifier /// /// Gets or sets the arguments that the command should be invoked with. /// - [DataMember(Name = "arguments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("arguments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object[]? Arguments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs index 609242748d6b3..6c674e6b35b65 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing additional information about the content in which a completion request is triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionContext { /// /// Gets or sets the indicating how the completion was triggered. /// - [DataMember(Name = "triggerKind")] + [JsonPropertyName("triggerKind")] public CompletionTriggerKind TriggerKind { get; @@ -28,8 +26,8 @@ public CompletionTriggerKind TriggerKind /// /// Gets or sets the character that triggered code completion. /// - [DataMember(Name = "triggerCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TriggerCharacter { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs index f416ae767d3d1..d795c27940feb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItem.cs @@ -6,21 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents an IntelliSense completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItem { /// /// Gets or sets the label value, i.e. display text to users. /// - [DataMember(Name = "label", IsRequired = true)] + [JsonPropertyName("label")] + [JsonRequired] public string Label { get; @@ -30,8 +29,8 @@ public string Label /// /// Gets or sets additional details for the label. /// - [DataMember(Name = "labelDetails")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("labelDetails")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemLabelDetails? LabelDetails { get; @@ -41,11 +40,11 @@ public CompletionItemLabelDetails? LabelDetails /// /// Gets or sets the completion kind. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] [DefaultValue(CompletionItemKind.None)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public CompletionItemKind Kind { get; @@ -55,8 +54,8 @@ public CompletionItemKind Kind /// /// Tags for this completion item. /// - [DataMember(Name = "tags")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("tags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public CompletionItemTag[]? Tags { get; @@ -66,8 +65,8 @@ public CompletionItemTag[]? Tags /// /// Gets or sets the completion detail. /// - [DataMember(Name = "detail")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("detail")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; @@ -77,8 +76,8 @@ public string? Detail /// /// Gets or sets the documentation comment. /// - [DataMember(Name = "documentation")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("documentation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public SumType? Documentation { get; @@ -88,8 +87,8 @@ public SumType? Documentation /// /// Gets or sets a value indicating whether this should be the selected item when showing. /// - [DataMember(Name = "preselect")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("preselect")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Preselect { get; @@ -99,8 +98,8 @@ public bool Preselect /// /// Gets or sets the custom sort text. /// - [DataMember(Name = "sortText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("sortText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? SortText { get; @@ -110,8 +109,8 @@ public string? SortText /// /// Gets or sets the custom filter text. /// - [DataMember(Name = "filterText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("filterText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? FilterText { get; @@ -121,8 +120,8 @@ public string? FilterText /// /// Gets or sets the insert text. /// - [DataMember(Name = "insertText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? InsertText { get; @@ -132,10 +131,10 @@ public string? InsertText /// /// Gets or sets the insert text format. /// - [DataMember(Name = "insertTextFormat")] + [JsonPropertyName("insertTextFormat")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [DefaultValue(InsertTextFormat.Plaintext)] public InsertTextFormat InsertTextFormat { @@ -146,8 +145,8 @@ public InsertTextFormat InsertTextFormat /// /// Gets or sets the text edit. /// - [DataMember(Name = "textEdit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? TextEdit { get; @@ -157,8 +156,8 @@ public SumType? TextEdit /// /// Gets or sets the text edit text. /// - [DataMember(Name = "textEditText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textEditText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TextEditText { get; @@ -171,8 +170,8 @@ public string? TextEditText /// /// Additional text edits must not interfere with the main text edit. /// - [DataMember(Name = "additionalTextEdits")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("additionalTextEdits")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextEdit[]? AdditionalTextEdits { get; @@ -184,8 +183,8 @@ public TextEdit[]? AdditionalTextEdits /// If present, this will override . /// If absent, will be used instead. /// - [DataMember(Name = "commitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("commitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? CommitCharacters { get; @@ -198,8 +197,8 @@ public string[]? CommitCharacters /// /// This feature is not supported in VS. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; @@ -209,8 +208,8 @@ public Command? Command /// /// Gets or sets any additional data that links the unresolve completion item and the resolved completion item. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs index 2322270f54e5d..b80c3219ce3fa 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemKindSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the initialization setting for completion item kind /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemKindSetting { /// /// Gets or sets the values that the client supports. /// - [DataMember(Name = "valueSet")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("valueSet")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemKind[]? ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs index 43b290c77361b..9bac8f809f335 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemLabelDetails.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Additional details for a completion item label. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemLabelDetails { /// /// Gets or sets an optional string which is rendered less prominently directly after label, without any spacing. /// - [DataMember(Name = "detail")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("detail")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; @@ -29,8 +27,8 @@ public string? Detail /// /// Gets or sets an optional string which is rendered less prominently after detail. /// - [DataMember(Name = "description")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Description { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs index 9686561c69c7b..11d0c8cd77ad1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents completion item capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemOptions { /// /// Gets or sets a value indicating The server has support for completion item label details /// - [DataMember(Name = "labelDetailsSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("labelDetailsSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LabelDetailsSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs index 58c655a5256c5..e5ca68bae6da4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemSetting { /// /// Gets or sets a value indicating whether completion items can contain snippets. /// - [DataMember(Name = "snippetSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("snippetSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SnippetSupport { get; @@ -29,8 +27,8 @@ public bool SnippetSupport /// /// Gets or sets a value indicating whether the client supports commit characters. /// - [DataMember(Name = "commitCharactersSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("commitCharactersSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool CommitCharactersSupport { get; @@ -40,8 +38,8 @@ public bool CommitCharactersSupport /// /// Gets or sets the content formats supported for documentation. /// - [DataMember(Name = "documentationFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentationFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MarkupKind[]? DocumentationFormat { get; @@ -51,8 +49,8 @@ public MarkupKind[]? DocumentationFormat /// /// Gets or sets the a value indicating whether the client supports the deprecated property on a completion item. /// - [DataMember(Name = "deprecatedSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("deprecatedSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DeprecatedSupport { get; @@ -62,8 +60,8 @@ public bool DeprecatedSupport /// /// Gets or sets the a value indicating whether the client supports the preselect property on a completion item. /// - [DataMember(Name = "preselectSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("preselectSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PreselectSupport { get; @@ -73,8 +71,8 @@ public bool PreselectSupport /// /// Gets or sets the a value indicating whether the client supports the tag property on a completion item. /// - [DataMember(Name = "tagSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tagSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemTagSupportSetting? TagSupport { get; @@ -84,8 +82,8 @@ public CompletionItemTagSupportSetting? TagSupport /// /// Gets or sets the a value indicating whether the client supports insert replace edit. /// - [DataMember(Name = "insertReplaceSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("insertReplaceSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool InsertReplaceSupport { get; @@ -95,8 +93,8 @@ public bool InsertReplaceSupport /// /// Gets or sets the a value indicating which properties a client can resolve lazily on a completion item. /// - [DataMember(Name = "resolveSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resolveSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ResolveSupportSetting? ResolveSupport { get; @@ -106,8 +104,8 @@ public ResolveSupportSetting? ResolveSupport /// /// Gets or sets the a value indicating whether the client supports the `insertTextMode` property on a completion item to override the whitespace handling mode as defined by the client. /// - [DataMember(Name = "insertTextModeSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextModeSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextModeSupportSetting? InsertTextModeSupport { get; @@ -117,8 +115,8 @@ public InsertTextModeSupportSetting? InsertTextModeSupport /// /// Gets or sets the a value indicating whether the client supports completion item label details. /// - [DataMember(Name = "labelDetailsSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("labelDetailsSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LabelDetailsSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs index 94b64986cff53..0b79975a7393a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionItemTagSupportSetting.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for the tag property on a completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionItemTagSupportSetting { /// /// Gets or sets a value indicating the tags supported by the client. /// - [DataMember(Name = "valueSet", IsRequired = true)] + [JsonPropertyName("valueSet")] + [JsonRequired] public CompletionItemTag[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs index 6189603e856f0..122fcb04d4e64 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionList.cs @@ -4,23 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a completion list. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionList { /// /// Gets or sets a value indicating whether Items is the complete list of items or not. If incomplete is true, then /// filtering should ask the server again for completion item. /// - [DataMember(Name = "isIncomplete")] + [JsonPropertyName("isIncomplete")] public bool IsIncomplete { get; @@ -30,7 +27,7 @@ public bool IsIncomplete /// /// Gets or sets the list of completion items. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public CompletionItem[] Items { get; @@ -40,8 +37,8 @@ public CompletionItem[] Items /// /// Gets or sets the completion list item defaults. /// - [DataMember(Name = "itemDefaults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("itemDefaults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionListItemDefaults? ItemDefaults { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs index 9c30582f3790a..4487a31a0f843 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionListItemDefaults.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents default properties associated with the entire completion list. /// - [DataContract] internal class CompletionListItemDefaults { /// /// Gets or sets the default commit character set. /// - [DataMember(Name = "commitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("commitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? CommitCharacters { get; @@ -27,8 +25,8 @@ public string[]? CommitCharacters /// /// Gets or sets the default edit range. /// - [DataMember(Name = "editRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("editRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? EditRange { get; @@ -38,8 +36,8 @@ public SumType? EditRange /// /// Gets or sets the default . /// - [DataMember(Name = "insertTextFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextFormat? InsertTextFormat { get; @@ -49,8 +47,8 @@ public InsertTextFormat? InsertTextFormat /// /// Gets or sets the default . /// - [DataMember(Name = "insertTextMode")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextMode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextMode? InsertTextMode { get; @@ -60,8 +58,8 @@ public InsertTextMode? InsertTextMode /// /// Gets or sets the default completion item data. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs index 6ae332b4c314b..ee4a5d56d6e8e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionListSetting.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents capabilites for the completion list type. /// - [DataContract] internal class CompletionListSetting { /// /// Gets or sets a value containing the supported property names of the object. /// If omitted, no properties are supported. /// - [DataMember(Name = "itemDefaults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("itemDefaults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? ItemDefaults { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs index 95c31bb7af59b..bdfa14e9507da 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents completion capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionOptions : IWorkDoneProgressOptions { /// /// Gets or sets the trigger characters. /// - [DataMember(Name = "triggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? TriggerCharacters { get; @@ -29,8 +27,8 @@ public string[]? TriggerCharacters /// /// Gets or sets a value indicating all the possible commit characters associated with the language server. /// - [DataMember(Name = "allCommitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("allCommitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? AllCommitCharacters { get; @@ -40,8 +38,8 @@ public string[]? AllCommitCharacters /// /// Gets or sets a value indicating whether server provides completion item resolve capabilities. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; @@ -51,15 +49,15 @@ public bool ResolveProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets completion item setting. /// - [DataMember(Name = "completionItem")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionItem")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemOptions? CompletionItemOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs index 0756a75604049..0c1626b6c3395 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionParams.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the textDocument/completion request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionParams : TextDocumentPositionParams, IPartialResultParams?> { /// /// Gets or sets the completion context. /// - [DataMember(Name = "context")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("context")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionContext? Context { get; @@ -30,8 +28,8 @@ public CompletionContext? Context /// /// Gets or sets the value of the PartialResultToken instance. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress?>? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs index d05a2bcac679e..30a5228668a92 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for completion. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CompletionSetting : DynamicRegistrationSetting { /// /// Gets or sets completion item setting. /// - [DataMember(Name = "completionItem")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionItem")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemSetting? CompletionItem { get; @@ -29,8 +27,8 @@ public CompletionItemSetting? CompletionItem /// /// Gets or sets specific settings. /// - [DataMember(Name = "completionItemKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionItemKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionItemKindSetting? CompletionItemKind { get; @@ -40,8 +38,8 @@ public CompletionItemKindSetting? CompletionItemKind /// /// Gets or sets a value indicating whether the client supports sending additional context. /// - [DataMember(Name = "contextSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("contextSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ContextSupport { get; @@ -51,8 +49,8 @@ public bool ContextSupport /// /// Gets or sets a value indicating client's default when the completion item doesn't provide an `insertTextMode` property. /// - [DataMember(Name = "insertTextMode")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("insertTextMode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InsertTextMode? InsertTextMode { get; @@ -62,8 +60,8 @@ public InsertTextMode? InsertTextMode /// /// Gets or sets a value indicating whether the client supports capabilities on the completion list. /// - [DataMember(Name = "completionList")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionList")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionListSetting? CompletionListSetting { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs index c6e0289f2196f..4154beeb6fa7f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CompletionTriggerKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various ways in which completion can be triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum CompletionTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs index 28bf61c08039e..51c7596a161c8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationItem.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents an configuration item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ConfigurationItem { /// /// Gets or sets the scope to get the configuration section for. /// - [DataMember(Name = "scopeUri")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("scopeUri")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(DocumentUriConverter))] public Uri? ScopeUri { @@ -31,8 +29,8 @@ public Uri? ScopeUri /// /// Gets or sets the requested configuration section. /// - [DataMember(Name = "section")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("section")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Section { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs index c4547e2f08587..3cfc4fd4a19cf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ConfigurationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the workspace/configuration request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ConfigurationParams { /// /// Gets or sets the ConfigurationItems being requested. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public ConfigurationItem[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs index 1da728f7de61d..4a91a4cacbde4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/DocumentUriConverter.cs @@ -2,59 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Globalization; - using Microsoft.CodeAnalysis.LanguageServer; - using Microsoft.CommonLanguageServerProtocol.Framework; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// TODO: document. - /// - internal class DocumentUriConverter : JsonConverter - { - /// - public override bool CanConvert(Type objectType) - { - return true; - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - if (reader.TokenType == JsonToken.String) - { - var token = JToken.ReadFrom(reader); - var uri = new Uri(token.ToObject()); +using System; +using System.Text.Json; +using System.Text.Json.Serialization; - return uri; - } - else if (reader.TokenType == JsonToken.Null) - { - return null; - } +namespace Roslyn.LanguageServer.Protocol; - throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.DocumentUriSerializationError, reader.Value)); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); +/// +/// TODO: document. +/// +internal class DocumentUriConverter : JsonConverter +{ + public override Uri Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => new(reader.GetString()); - if (value is Uri uri) - { - var token = JToken.FromObject(uri.AbsoluteUri); - token.WriteTo(writer); - } - else - { - throw new ArgumentException($"{nameof(value)} must be of type {nameof(Uri)}"); - } - } - } + public override void Write(Utf8JsonWriter writer, Uri value, JsonSerializerOptions options) + => writer.WriteStringValue(value.AbsoluteUri); } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs deleted file mode 100644 index 207ed168f25e6..0000000000000 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; - - /// - /// Class containing extension method to thread-safely manage operations. - /// - internal static class JsonConverterCollectionUtilities - { - /// - /// Lock used for modifications to Converters collection. - /// - public static readonly object ConvertersLock = new object(); - } -} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.cs new file mode 100644 index 0000000000000..c69b5f8bfbeb8 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/NaturalObjectConverter.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +// copied from https://github.com/dotnet/runtime/issues/98038 to match newtonsoft behavior +internal class NaturalObjectConverter : JsonConverter +{ + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => ReadObjectCore(ref reader); + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + var runtimeType = value.GetType(); + if (runtimeType == typeof(object)) + { + writer.WriteStartObject(); + writer.WriteEndObject(); + } + else + { + JsonSerializer.Serialize(writer, value, runtimeType, options); + } + } + + private static object? ReadObjectCore(ref Utf8JsonReader reader) + { + switch (reader.TokenType) + { + case JsonTokenType.Null: + return null; + + case JsonTokenType.False or JsonTokenType.True: + return reader.GetBoolean(); + + case JsonTokenType.Number: + if (reader.TryGetInt32(out var intValue)) + { + return intValue; + } + if (reader.TryGetInt64(out var longValue)) + { + return longValue; + } + + return reader.GetDouble(); + + case JsonTokenType.String: + return reader.GetString(); + + case JsonTokenType.StartArray: + var list = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + var element = ReadObjectCore(ref reader); + list.Add(element); + } + return list; + + case JsonTokenType.StartObject: + return JsonSerializer.Deserialize(ref reader); + + default: + throw new JsonException(); + } + } +} \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs index ca7ba65725eb1..ff7e382b22a64 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/ParameterInformationConverter.cs @@ -2,64 +2,56 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; - /// - /// JsonConverter to correctly deserialize int arrays in the Label param of ParameterInformation. - /// - internal class ParameterInformationConverter : JsonConverter +/// +/// JsonConverter to correctly deserialize int arrays in the Label param of ParameterInformation. +/// +internal class ParameterInformationConverter : JsonConverter +{ + public override ParameterInformation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - /// - public override bool CanWrite => false; + using var document = JsonDocument.ParseValue(ref reader); + var root = document.RootElement; - /// - public override bool CanConvert(Type objectType) - { - return true; - } + var parameter = new ParameterInformation(); - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + if (root.TryGetProperty("label", out var labelElement)) { - var token = JToken.Load(reader); - - var label = ((JObject)token).Property("label", StringComparison.Ordinal); - var documentation = ((JObject)token).Property("documentation", StringComparison.Ordinal); - - var parameter = new ParameterInformation(); - - if (label != null) + if (labelElement.ValueKind == JsonValueKind.Array) { - var value = label.Value; - if (value is JArray arr) - { - var tuple = new Tuple(arr[0].Value(), arr[1].Value()); - parameter.Label = tuple; - } - else - { - // If label is not an array we can serialize it normally - parameter.Label = value.ToObject>>(); - } + parameter.Label = new Tuple(labelElement[0].GetInt32(), labelElement[1].GetInt32()); } - - if (documentation != null) + else { - var value = documentation.Value; - parameter.Documentation = value.ToObject?>(); + parameter.Label = labelElement.Deserialize>>(options); } + } - return parameter; + if (root.TryGetProperty("documentation", out var documentationElement)) + { + parameter.Documentation = documentationElement.Deserialize>(options); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + return parameter; + } + + public override void Write(Utf8JsonWriter writer, ParameterInformation value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("label"); + JsonSerializer.Serialize(writer, value.Label, options); + + if (value.Documentation != null) { - throw new NotImplementedException(); + writer.WritePropertyName("documentation"); + JsonSerializer.Serialize(writer, value.Documentation, options); } + + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs index 64d2ae3d53d6e..0fab79be3e263 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/StringEnumConverter.cs @@ -2,22 +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. -namespace Roslyn.LanguageServer.Protocol; - using System; using System.ComponentModel; using System.Globalization; using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer; -using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; +namespace Roslyn.LanguageServer.Protocol; /// /// JsonConverter for serializing and deserializing string-based enums. /// /// The actual type implementing . internal class StringEnumConverter - : JsonConverter + : JsonConverter where TStringEnumType : IStringEnum { private static readonly Func CreateEnum; @@ -36,46 +35,30 @@ static StringEnumConverter() CreateEnum = Expression.Lambda>(body, param).Compile(); } - /// - public override bool CanConvert(Type objectType) => objectType == typeof(TStringEnumType); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override TStringEnumType? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - - if (reader.TokenType == JsonToken.String) + if (reader.TokenType == JsonTokenType.String) { - return CreateEnum((string)reader.Value!); + return CreateEnum(reader.GetString()!); } - else if (reader.TokenType == JsonToken.Null) + else if (reader.TokenType == JsonTokenType.Null) { - return default(TStringEnumType); + return default; } - throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.StringEnumSerializationError, reader.Value)); + throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.StringEnumSerializationError, reader.GetString())); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, TStringEnumType value, JsonSerializerOptions options) { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - if (value is TStringEnumType kind) - { - writer.WriteValue(kind.Value); - } - else - { - throw new ArgumentException($"{nameof(value)} must be of type {typeof(TStringEnumType).FullName}"); - } + writer.WriteStringValue(value.Value); } /// /// Type converter from to . /// This is required to support . /// - internal class TypeConverter + public class TypeConverter : System.ComponentModel.TypeConverter { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs index 303bc1801deca..d8fb1d10670a9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/SumConverter.cs @@ -2,269 +2,296 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Roslyn.LanguageServer.Protocol; +internal class SumConverter : JsonConverterFactory { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Microsoft.CodeAnalysis.LanguageServer; - using Microsoft.CommonLanguageServerProtocol.Framework; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// Converter to translate to and from SumTypes. - /// - internal class SumConverter : JsonConverter + public override bool CanConvert(Type typeToConvert) { - private static readonly ConcurrentDictionary SumTypeCache = new ConcurrentDictionary(); + return typeof(ISumType).IsAssignableFrom(typeToConvert); + } - /// - public override bool CanConvert(Type objectType) - { - return typeof(ISumType).IsAssignableFrom(objectType); - } + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var converterType = typeof(SumConverter<>).MakeGenericType(typeToConvert); + return (JsonConverter)Activator.CreateInstance(converterType)!; + } - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + internal class SumTypeInfoCache + { + // netstandard1.0 doesn't support Array.Empty +#pragma warning disable CA1825 // Avoid zero-length array allocations. + private static readonly IReadOnlyList EmptyUnionInfos = new UnionTypeInfo[0]; +#pragma warning restore CA1825 // Avoid zero-length array allocations. - // Even if CanConvert only returns true for ISumType, ReadJson is invoked for Nullable> as well - if (reader.TokenType == JsonToken.Null) - { - if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - return null; - } - else - { - // We shouldn't really ever have a non-Nullable SumType being received as null but, if we do, return an "empty" SumType - return Activator.CreateInstance(objectType); - } - } + private readonly IReadOnlyList allUnionTypeInfos; - // objectType will be one of the various SumType variants. In order for this converter to work with all SumTypes it has to use reflection. - // This method works by attempting to deserialize the json into each of the type parameters to a SumType and stops at the first success. - var sumTypeInfoCache = SumTypeCache.GetOrAdd(objectType, (t) => new SumTypeInfoCache(t)); + private readonly IReadOnlyList primitiveUnionTypeInfos; - JToken? token = null; - var applicableUnionTypeInfos = sumTypeInfoCache.GetApplicableInfos(reader.TokenType); + private readonly IReadOnlyList arrayUnionTypeInfos; - for (var i = 0; i < applicableUnionTypeInfos.Count; i++) - { - var unionTypeInfo = applicableUnionTypeInfos[i]; + private readonly IReadOnlyList objectUnionTypeInfos; - if (!IsTokenCompatibleWithType(reader, unionTypeInfo)) - { - continue; - } + public SumTypeInfoCache(Type sumTypeType) + { + var allUnionTypeInfosSet = new List(); + List? primitiveUnionTypeInfosSet = null; + List? arrayUnionTypeInfosSet = null; + List? objectUnionTypeInfosSet = null; - try - { - object? sumValue; - if (token == null && i + 1 == applicableUnionTypeInfos.Count) - { - // We're at the very last entry, we don't need to maintain the JsonReader, can read directly from the JsonReader to avoid the inbetween JObject type. - sumValue = serializer.Deserialize(reader, unionTypeInfo.Type); - } - else - { - if (token == null) - { - token = JToken.ReadFrom(reader); - } + // If the SumType is a nullable extract the underlying type and re-assign + sumTypeType = NormalizeToNonNullable(sumTypeType); - if (unionTypeInfo.KindAttribute is not null && - (token is not JObject jObject || jObject[unionTypeInfo.KindAttribute.KindPropertyName]?.ToString() != unionTypeInfo.KindAttribute.Kind)) - { - continue; - } + var typeInfo = sumTypeType.GetTypeInfo(); + var parameterTypes = typeInfo.GenericTypeArguments; + foreach (var parameterType in parameterTypes) + { + var parameterTypeInfo = NormalizeToNonNullable(parameterType).GetTypeInfo(); + var declaredConstructor = typeInfo.GetConstructor(new Type[] { parameterType }) ?? + throw new ArgumentException(nameof(sumTypeType), "All constructor parameter types must be represented in the generic type arguments of the SumType"); - sumValue = token.ToObject(unionTypeInfo.Type, serializer); - } + var kindAttribute = parameterType.GetCustomAttribute(); + var unionTypeInfo = new UnionTypeInfo(parameterType, declaredConstructor, kindAttribute); + allUnionTypeInfosSet.Add(unionTypeInfo); - object?[] args = [sumValue]; - var sum = unionTypeInfo.Constructor.Invoke(args); - return sum; + if (parameterTypeInfo.IsPrimitive || + parameterTypeInfo == typeof(string) || + typeof(IStringEnum).IsAssignableFrom(parameterTypeInfo)) + { + primitiveUnionTypeInfosSet ??= new List(); + primitiveUnionTypeInfosSet.Add(unionTypeInfo); } - catch + else if (parameterTypeInfo.IsArray) { - continue; + arrayUnionTypeInfosSet ??= new List(); + arrayUnionTypeInfosSet.Add(unionTypeInfo); + } + else + { + objectUnionTypeInfosSet ??= new List(); + objectUnionTypeInfosSet.Add(unionTypeInfo); } } - throw new JsonSerializationException(LanguageServerProtocolResources.NoSumTypeMatch); - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - if (value is null) + this.allUnionTypeInfos = allUnionTypeInfosSet; + this.primitiveUnionTypeInfos = primitiveUnionTypeInfosSet ?? EmptyUnionInfos; + this.arrayUnionTypeInfos = arrayUnionTypeInfosSet ?? EmptyUnionInfos; + if ((objectUnionTypeInfosSet?.Count ?? 0) > 1) { - writer.WriteNull(); + // If some types are tagged with a KindAttribute, make sure they are first in the list in order to avoid the wrong type being deserialized + this.objectUnionTypeInfos = objectUnionTypeInfosSet.Where(t => t.KindAttribute is not null).Concat( + objectUnionTypeInfosSet.Where(t => t.KindAttribute is null)).ToList(); } else { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); - - var sumValue = ((ISumType)value!).Value; - - if (sumValue != null) - { - var token = JToken.FromObject(sumValue); - token.WriteTo(writer); - } + this.objectUnionTypeInfos = objectUnionTypeInfosSet ?? EmptyUnionInfos; } } - private static bool IsTokenCompatibleWithType(JsonReader reader, SumTypeInfoCache.UnionTypeInfo unionTypeInfo) + public IReadOnlyList GetApplicableInfos(JsonTokenType startingTokenType) { - var isCompatible = true; - switch (reader.TokenType) + return startingTokenType switch { - case JsonToken.Float: - isCompatible = unionTypeInfo.Type == typeof(double) || - unionTypeInfo.Type == typeof(float); - break; - case JsonToken.Boolean: - isCompatible = unionTypeInfo.Type == typeof(bool); - break; - case JsonToken.Integer: - isCompatible = unionTypeInfo.Type == typeof(int) || - unionTypeInfo.Type == typeof(uint) || - unionTypeInfo.Type == typeof(long) || - unionTypeInfo.Type == typeof(ulong) || - unionTypeInfo.Type == typeof(short) || - unionTypeInfo.Type == typeof(ushort) || - unionTypeInfo.Type == typeof(byte) || - unionTypeInfo.Type == typeof(sbyte) || - unionTypeInfo.Type == typeof(double) || - unionTypeInfo.Type == typeof(float); - break; - case JsonToken.String: - isCompatible = unionTypeInfo.Type == typeof(string) || - typeof(IStringEnum).IsAssignableFrom(unionTypeInfo.Type); - break; - } - - return isCompatible; + JsonTokenType.StartArray + => this.arrayUnionTypeInfos, + JsonTokenType.Number or + JsonTokenType.String or + JsonTokenType.True or + JsonTokenType.False + => this.primitiveUnionTypeInfos, + JsonTokenType.StartObject + => this.objectUnionTypeInfos, + _ => this.allUnionTypeInfos, + }; } - private class SumTypeInfoCache + private static Type NormalizeToNonNullable(Type sumTypeType) { - // netstandard1.0 doesn't support Array.Empty -#pragma warning disable CA1825 // Avoid zero-length array allocations. - private static readonly IReadOnlyList EmptyUnionInfos = new UnionTypeInfo[0]; -#pragma warning restore CA1825 // Avoid zero-length array allocations. + return Nullable.GetUnderlyingType(sumTypeType) ?? sumTypeType; + } - private readonly IReadOnlyList allUnionTypeInfos; + public class UnionTypeInfo + { + // System.Text.Json can pre-compile the generic SumType<> constructor call so we don't need to do it through reflection every time. + internal delegate T StjReader(ref Utf8JsonReader reader, JsonSerializerOptions options); + + private static readonly Type[] expressionLambdaMethodTypes = new[] { typeof(Type), typeof(Expression), typeof(ParameterExpression[]) }; + private static readonly MethodInfo expressionLambdaMethod = typeof(Expression) + .GetMethods() + .Where(mi => + !mi.IsGenericMethod && + mi.Name == "Lambda" && + mi.GetParameters() + .Select(p => p.ParameterType) + .SequenceEqual(expressionLambdaMethodTypes)) + .Single(); + + private static readonly Type[] jsonSerializerDeserializeMethodTypes = new[] { typeof(Utf8JsonReader).MakeByRefType(), typeof(JsonSerializerOptions) }; + private static readonly MethodInfo jsonSerializerDeserializeMethod = typeof(JsonSerializer) + .GetMethods() + .Where(mi => + mi.IsGenericMethod && + mi.Name == "Deserialize" && + mi.GetParameters() + .Select(p => p.ParameterType) + .SequenceEqual(jsonSerializerDeserializeMethodTypes)) + .Single(); + + public UnionTypeInfo(Type type, ConstructorInfo constructor, KindAttribute? kindAttribute) + { + this.Type = type ?? throw new ArgumentNullException(nameof(type)); + this.Constructor = constructor ?? throw new ArgumentNullException(nameof(constructor)); + this.KindAttribute = kindAttribute; + + var param1 = Expression.Parameter(typeof(Utf8JsonReader).MakeByRefType(), "reader"); + var param2 = Expression.Parameter(typeof(JsonSerializerOptions), "options"); + var body = Expression.New( + constructor, + Expression.Call( + jsonSerializerDeserializeMethod.MakeGenericMethod(type), + param1, + param2)); + var expression = (LambdaExpression)expressionLambdaMethod.Invoke(null, new object[] { typeof(StjReader<>).MakeGenericType(constructor.DeclaringType), body, new[] { param1, param2 } })!; + + StjReaderFunction = expression.Compile(); + } - private readonly IReadOnlyList primitiveUnionTypeInfos; + public Type Type { get; } - private readonly IReadOnlyList arrayUnionTypeInfos; + public ConstructorInfo Constructor { get; } - private readonly IReadOnlyList objectUnionTypeInfos; + public KindAttribute? KindAttribute { get; } - public SumTypeInfoCache(Type sumTypeType) - { - var allUnionTypeInfosSet = new List(); - List? primitiveUnionTypeInfosSet = null; - List? arrayUnionTypeInfosSet = null; - List? objectUnionTypeInfosSet = null; + public object StjReaderFunction { get; } + } + } +} - // If the SumType is a nullable extract the underlying type and re-assign - sumTypeType = NormalizeToNonNullable(sumTypeType); +internal class SumConverter : JsonConverter + where T : struct, ISumType +{ + private static readonly ConcurrentDictionary SumTypeCache = new ConcurrentDictionary(); - var typeInfo = sumTypeType.GetTypeInfo(); - var parameterTypes = typeInfo.GenericTypeArguments; - foreach (var parameterType in parameterTypes) - { - var parameterTypeInfo = NormalizeToNonNullable(parameterType).GetTypeInfo(); - var declaredConstructor = typeInfo.GetConstructor([parameterType]) ?? - throw new ArgumentException(nameof(sumTypeType), "All constructor parameter types must be represented in the generic type arguments of the SumType"); + /// + public override T Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + // We shouldn't really ever have a non-Nullable SumType being received as null but, if we do, return an "empty" SumType + return (T)Activator.CreateInstance(objectType)!; + } - var kindAttribute = parameterType.GetCustomAttribute(); - var unionTypeInfo = new UnionTypeInfo(parameterType, declaredConstructor, kindAttribute); - allUnionTypeInfosSet.Add(unionTypeInfo); + // objectType will be one of the various SumType variants. In order for this converter to work with all SumTypes it has to use reflection. + // This method works by attempting to deserialize the json into each of the type parameters to a SumType and stops at the first success. + var sumTypeInfoCache = SumTypeCache.GetOrAdd(objectType, (t) => new SumConverter.SumTypeInfoCache(t)); - if (parameterTypeInfo.IsPrimitive || - parameterTypeInfo == typeof(string) || - typeof(IStringEnum).IsAssignableFrom(parameterTypeInfo)) - { - primitiveUnionTypeInfosSet ??= []; - primitiveUnionTypeInfosSet.Add(unionTypeInfo); - } - else if (parameterTypeInfo.IsArray) - { - arrayUnionTypeInfosSet ??= []; - arrayUnionTypeInfosSet.Add(unionTypeInfo); - } - else + var applicableUnionTypeInfos = sumTypeInfoCache.GetApplicableInfos(reader.TokenType); + if (applicableUnionTypeInfos.Count > 0) + { + var backupReader = reader; + if (applicableUnionTypeInfos[0].KindAttribute is { } kindAttribute) + { + using var document = JsonDocument.ParseValue(ref reader); + reader = backupReader; + if (document.RootElement.TryGetProperty(kindAttribute.KindPropertyName, out var value)) + { + var kind = value.GetString(); + for (var i = 0; i < applicableUnionTypeInfos.Count; i++) { - objectUnionTypeInfosSet ??= []; - objectUnionTypeInfosSet.Add(unionTypeInfo); + var unionTypeInfo = applicableUnionTypeInfos[i]; + if (unionTypeInfo.KindAttribute == null) + { + throw new JsonException(LanguageServerProtocolResources.NoSumTypeMatch); + } + + if (unionTypeInfo.KindAttribute.Kind == kind) + { + var result = ((SumConverter.SumTypeInfoCache.UnionTypeInfo.StjReader)unionTypeInfo.StjReaderFunction).Invoke(ref reader, options); + return result; + } } } + } + + for (var i = 0; i < applicableUnionTypeInfos.Count; i++) + { + var unionTypeInfo = applicableUnionTypeInfos[i]; - this.allUnionTypeInfos = allUnionTypeInfosSet; - this.primitiveUnionTypeInfos = primitiveUnionTypeInfosSet ?? EmptyUnionInfos; - this.arrayUnionTypeInfos = arrayUnionTypeInfosSet ?? EmptyUnionInfos; - if ((objectUnionTypeInfosSet?.Count ?? 0) > 1) + if (!IsTokenCompatibleWithType(ref reader, unionTypeInfo)) { - // If some types are tagged with a KindAttribute, make sure they are first in the list in order to avoid the wrong type being deserialized - this.objectUnionTypeInfos = objectUnionTypeInfosSet.Where(t => t.KindAttribute is not null).Concat( - objectUnionTypeInfosSet.Where(t => t.KindAttribute is null)).ToList(); + continue; } - else + + if (unionTypeInfo.KindAttribute != null) { - this.objectUnionTypeInfos = objectUnionTypeInfosSet ?? EmptyUnionInfos; + continue; } - } - public IReadOnlyList GetApplicableInfos(JsonToken startingTokenType) - { - return startingTokenType switch + try { - JsonToken.StartArray - => this.arrayUnionTypeInfos, - JsonToken.Integer or - JsonToken.Float or - JsonToken.Bytes or - JsonToken.String or - JsonToken.Boolean - => this.primitiveUnionTypeInfos, - JsonToken.StartObject - => this.objectUnionTypeInfos, - _ => this.allUnionTypeInfos, - }; + var result = ((SumConverter.SumTypeInfoCache.UnionTypeInfo.StjReader)unionTypeInfo.StjReaderFunction).Invoke(ref reader, options); + return result; + } + catch + { + reader = backupReader; + continue; + } } + } - private static Type NormalizeToNonNullable(Type sumTypeType) - { - return Nullable.GetUnderlyingType(sumTypeType) ?? sumTypeType; - } + throw new JsonException(LanguageServerProtocolResources.NoSumTypeMatch); + } - internal class UnionTypeInfo - { - public UnionTypeInfo(Type type, ConstructorInfo constructor, KindAttribute? kindAttribute) - { - this.Type = type ?? throw new ArgumentNullException(nameof(type)); - this.Constructor = constructor ?? throw new ArgumentNullException(nameof(constructor)); - this.KindAttribute = kindAttribute; - } + /// + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer = writer ?? throw new ArgumentNullException(nameof(writer)); - public Type Type { get; } + var sumValue = value.Value; - public ConstructorInfo Constructor { get; } + if (sumValue != null) + { + JsonSerializer.Serialize(writer, sumValue, options); + } + } - public KindAttribute? KindAttribute { get; } - } + private static bool IsTokenCompatibleWithType(ref Utf8JsonReader reader, SumConverter.SumTypeInfoCache.UnionTypeInfo unionTypeInfo) + { + var isCompatible = true; + switch (reader.TokenType) + { + case JsonTokenType.True: + case JsonTokenType.False: + isCompatible = unionTypeInfo.Type == typeof(bool); + break; + case JsonTokenType.Number: + isCompatible = unionTypeInfo.Type == typeof(int) || + unionTypeInfo.Type == typeof(uint) || + unionTypeInfo.Type == typeof(long) || + unionTypeInfo.Type == typeof(ulong) || + unionTypeInfo.Type == typeof(short) || + unionTypeInfo.Type == typeof(ushort) || + unionTypeInfo.Type == typeof(byte) || + unionTypeInfo.Type == typeof(sbyte) || + unionTypeInfo.Type == typeof(double) || + unionTypeInfo.Type == typeof(float); + break; + case JsonTokenType.String: + isCompatible = unionTypeInfo.Type == typeof(string) || + typeof(IStringEnum).IsAssignableFrom(unionTypeInfo.Type); + break; } + + return isCompatible; } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs index 68bb6efadad73..64e236febb743 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/TextDocumentSyncConverter.cs @@ -2,99 +2,48 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Roslyn.LanguageServer.Protocol; +internal class TextDocumentSyncConverter : JsonConverter { - using System; - using System.Globalization; - using Microsoft.CodeAnalysis.LanguageServer; - using Microsoft.CommonLanguageServerProtocol.Framework; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - /// - /// Converter which offers custom serialization for enum to a object. - /// - /// - /// This is to support backwards compatibility for the protocol. - /// - internal class TextDocumentSyncConverter : JsonConverter + public override TextDocumentSyncOptions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - /// - public override bool CanConvert(Type objectType) + if (reader.TokenType == JsonTokenType.Number) { - return true; - } - - /// - /// Deserializes a json value to a object. - /// - /// Reader from which to read json value. - /// Type of the json value. - /// Existing value. - /// Default serializer. - /// A which matches the json value. - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - reader = reader ?? throw new ArgumentNullException(nameof(reader)); - if (reader.TokenType is JsonToken.Float or JsonToken.Integer) - { - // This conversion is modeled after what VS Code does, see https://github.com/Microsoft/vscode-languageserver-node/blob/master/client/src/client.ts#L1234 - var textDocSync = new TextDocumentSyncOptions - { - OpenClose = true, - Change = (TextDocumentSyncKind)int.Parse(reader.Value!.ToString(), NumberStyles.Integer, CultureInfo.CurrentCulture), - Save = new SaveOptions - { - IncludeText = false, - }, - }; - - return textDocSync; - } - else if (reader.TokenType == JsonToken.String) - { - return JsonConvert.DeserializeObject(reader.Value!.ToString()); - } - else if (reader.TokenType == JsonToken.StartObject) + // This conversion is modeled after what VS Code does, see https://github.com/microsoft/vscode-languageserver-node/blob/21f4f0af6bf1623483c3e65f36e550bfdb6245a6/client/src/common/client.ts#L1248 + var textDocSync = new TextDocumentSyncOptions { - var token = JToken.ReadFrom(reader); - return token.ToObject(); - } - else if (reader.TokenType == JsonToken.Null) - { - // This conversion is modeled after what VS Code does, see https://github.com/Microsoft/vscode-languageserver-node/blob/master/client/src/client.ts#L1234 - var textDocSync = new TextDocumentSyncOptions + OpenClose = true, + Change = (TextDocumentSyncKind)reader.GetInt32(), + Save = new SaveOptions { - OpenClose = true, - Change = TextDocumentSyncKind.None, - Save = new SaveOptions - { - IncludeText = false, - }, - }; - - return textDocSync; - } + IncludeText = false, + }, + }; - throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.TextDocumentSyncSerializationError, reader.Value)); + return textDocSync; } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + else if (reader.TokenType == JsonTokenType.String) + { + var value = reader.GetString()!; + return JsonSerializer.Deserialize(value)!; + } + else if (reader.TokenType == JsonTokenType.StartObject) { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); + return JsonSerializer.Deserialize(ref reader, options)!; + } - if (value is null) - { - writer.WriteNull(); - } - else - { - writer = writer ?? throw new ArgumentNullException(nameof(writer)); + throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.TextDocumentSyncSerializationError, reader.GetString())); + } - var token = JToken.FromObject(value); - token.WriteTo(writer); - } - } + /// + public override void Write(Utf8JsonWriter writer, TextDocumentSyncOptions value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs b/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs index 8cc16ae3379ff..723850708e18b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CreateFile.cs @@ -5,29 +5,28 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [Kind("create")] internal class CreateFile { /// /// Gets the kind value. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Member can't be static since it's part of the protocol")] public string Kind => "create"; /// /// Gets or sets the resource to create. /// - [DataMember(Name = "uri", IsRequired = true)] + [JsonPropertyName("uri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -38,8 +37,8 @@ public Uri Uri /// /// Gets or sets the additional options. /// - [DataMember(Name = "options")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CreateFileOptions? Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs index 68fcf7c8e30c5..c8215d3a14223 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/CreateFileOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class CreateFileOptions { /// /// Gets or sets a value indicating whether the creation should overwrite the file if it already exists. (Overwrite wins over ignoreIfExists). /// - [DataMember(Name = "overwrite")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("overwrite")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Overwrite { get; @@ -29,8 +27,8 @@ public bool Overwrite /// /// Gets or sets a value indicating whether the action should be ignored if the file already exists. /// - [DataMember(Name = "ignoreIfExists")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("ignoreIfExists")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IgnoreIfExists { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs b/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs index abcd66aa84d61..d221861d3f169 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DefaultBehaviorPrepareRename.cs @@ -4,23 +4,21 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a possible result value of the 'textDocument/prepareRename' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DefaultBehaviorPrepareRename { /// /// Gets or sets a value indicating whether the rename position is valid and the client should use its /// default behavior to compute the rename range. /// - [DataMember(Name = "defaultBehavior")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("defaultBehavior")] + [JsonRequired] public bool DefaultBehavior { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs index 2469f7a97db8e..b906658d84875 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DefinitionOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DefinitionOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs b/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs index dcc6eed78e854..919093f482981 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DeleteFile.cs @@ -5,29 +5,28 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a delete file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [Kind("delete")] internal class DeleteFile { /// /// Gets the kind value. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Member can't be static since it's part of the protocol")] public string Kind => "delete"; /// /// Gets or sets the file to delete. /// - [DataMember(Name = "uri", IsRequired = true)] + [JsonPropertyName("uri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -38,8 +37,8 @@ public Uri Uri /// /// Gets or sets the additional options. /// - [DataMember(Name = "options")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DeleteFileOptions? Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs index 5e1c8edd9e884..8764e43d01657 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DeleteFileOptions.cs @@ -4,23 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DeleteFileOptions { /// /// Gets or sets a value indicating whether the delete operation should be applied recursively if a folder is denoted. (Overwrite wins over ignoreIfNotExists). /// - [DataMember(Name = "recursive")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("recursive")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Recursive { get; @@ -30,8 +27,8 @@ public bool Recursive /// /// Gets or sets a value indicating whether the action should be ignored if the file doesn't exists. /// - [DataMember(Name = "ignoreIfNotExists")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("ignoreIfNotExists")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IgnoreIfNotExists { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs b/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs index d664f263d5a7b..57def31e7cee4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Diagnostic.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Linq; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a source code diagnostic message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Diagnostic : IEquatable { /// /// Gets or sets the source code range. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -30,8 +28,8 @@ public Range Range /// /// Gets or sets the diagnostic severity. /// - [DataMember(Name = "severity")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("severity")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticSeverity? Severity { get; @@ -44,8 +42,8 @@ public DiagnosticSeverity? Severity /// /// The value can be an , . /// - [DataMember(Name = "code")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("code")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Code { get; @@ -55,8 +53,8 @@ public SumType? Code /// /// Gets or sets an optional value that describes the error code. /// - [DataMember(Name = "codeDescription")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeDescription")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeDescription? CodeDescription { get; @@ -67,8 +65,8 @@ public CodeDescription? CodeDescription /// Gets or sets a human-readable string describing the source of this /// diagnostic, e.g. 'typescript' or 'super lint'. It usually appears in the user interface. /// - [DataMember(Name = "source")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("source")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Source { get; @@ -78,7 +76,7 @@ public string? Source /// /// Gets or sets the diagnostic's message. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; @@ -88,8 +86,8 @@ public string Message /// /// Gets or sets the diagnostic's tags. /// - [DataMember(Name = "tags")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticTag[]? Tags { get; @@ -99,8 +97,8 @@ public DiagnosticTag[]? Tags /// /// Gets or sets the diagnostic related information /// - [DataMember(Name = "relatedInformation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("relatedInformation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticRelatedInformation[]? RelatedInformation { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs index e464c6eb9a8a8..4d51c1b96d201 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticOptions.cs @@ -4,29 +4,27 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Server capabilities for pull diagnostics. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets the identifier in which the diagnostics are bucketed by the client. /// - [DataMember(Name = "identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; @@ -36,7 +34,7 @@ public string? Identifier /// /// Gets or sets a value indicating whether the language has inter file dependencies. /// - [DataMember(Name = "interFileDependencies")] + [JsonPropertyName("interFileDependencies")] public bool InterFileDependencies { get; @@ -46,7 +44,7 @@ public bool InterFileDependencies /// /// Gets or sets a value indicating whether the server provides support for workspace diagnostics as well. /// - [DataMember(Name = "workspaceDiagnostics")] + [JsonPropertyName("workspaceDiagnostics")] public bool WorkspaceDiagnostics { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs index 549fe02ed56cf..710b99742f84e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRegistrationOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json; +using System.Text.Json.Serialization; /// /// Diagnostic registration options. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticRegistrationOptions : DiagnosticOptions, IStaticRegistrationOptions, ITextDocumentRegistrationOptions { /// /// Gets or sets the document filters for this registration option. /// - [DataMember(Name = "documentSelector")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("documentSelector")] public DocumentFilter[]? DocumentSelector { get; @@ -29,8 +27,8 @@ public DocumentFilter[]? DocumentSelector /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "id")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Id { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs index e62e8058f8cf7..cc3099afe6976 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticRelatedInformation.cs @@ -4,7 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a related message and source code location for a diagnostic. @@ -13,19 +13,18 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DiagnosticRelatedInformation { /// /// Gets or sets the location for the related information. /// - [DataMember(Name = "location")] + [JsonPropertyName("location")] public Location Location { get; set; } /// /// Gets or sets the message for the related information. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs index 9840373f3f4c5..0ee406d772ae8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticServerCancellationData.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; /// /// Class representing the cancellation data returned from a diagnostic request. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticServerCancellationData { /// /// Gets or sets a value indicating whether the client should re-trigger the request. /// - [DataMember(Name = "retriggerRequest")] + [JsonPropertyName("retriggerRequest")] public bool RetriggerRequest { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs index 75c0827e25bf6..b5d623d11f2c9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Client settings for pull diagnostics. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether the client supports related documents for document diagnostic pulls. /// - [DataMember(Name = "relatedDocumentSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("relatedDocumentSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RelatedDocumentSupport { get; set; } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs index 1c1640598d134..5aee040d2afc3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DiagnosticWorkspaceSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing the workspace diagnostic client capabilities. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DiagnosticWorkspaceSetting { /// /// Gets or sets a value indicating whether the client supports a refresh request sent from the server to the client. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs index a9816d3926583..102c94551b080 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidChangeConfigurationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter sent with workspace/didChangeConfiguration requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidChangeConfigurationParams { /// /// Gets or sets the settings that are applicable to the language server. /// - [DataMember(Name = "settings")] + [JsonPropertyName("settings")] public object Settings { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs index 0a4f9c3934a02..78ac42a0491d8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidChangeTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/didChange message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidChangeTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the document that changed. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public VersionedTextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public VersionedTextDocumentIdentifier TextDocument /// /// Gets or sets the content changes. /// - [DataMember(Name = "contentChanges")] + [JsonPropertyName("contentChanges")] public TextDocumentContentChangeEvent[] ContentChanges { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs index 75e21d2e8de99..e090da167da4e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidChangeWatchedFilesParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with workspace/didChangeWatchedFiles message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidChangeWatchedFilesParams { /// /// Gets or sets of the collection of file change events. /// - [DataMember(Name = "changes")] + [JsonPropertyName("changes")] public FileEvent[] Changes { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs index f2e1dccc784a4..15a1ccb176d6e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidCloseTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/didClose message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidCloseTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the text document identifier. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs index 2d32742a3c94f..ff6ecfacc3ccb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidOpenTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/didOpen message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidOpenTextDocumentParams { /// /// Gets or sets the which represents the text document that was opened. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentItem TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs index aab858ce354e8..b0c24f6e168c2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DidSaveTextDocumentParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with a textDocument/didSave message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DidSaveTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the which represents the text document that was saved. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -28,8 +26,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the which represents the content of the text document when it was saved. /// - [DataMember(Name = "text")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("text")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs index dbad3759bf15b..96cf410caca2e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentColorOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs index 7f364f077b7cb..6e5b7506a718b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentColorParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/documentColor request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentColorParams : ITextDocumentParams { /// /// Gets or sets the to provide links for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs index 55cd0c4f38eb7..348274675e055 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticParams.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing the document diagnostic request parameters /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DocumentDiagnosticParams : ITextDocumentParams, IPartialResultParams> { /// @@ -23,8 +21,8 @@ internal class DocumentDiagnosticParams : ITextDocumentParams, IPartialResultPar /// Note that the first literal send needs to be either the or /// followed by n literals. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress>? PartialResultToken { get; @@ -34,7 +32,7 @@ public IProgress /// Gets or sets the to provide diagnostics for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -44,8 +42,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the identifier for which the client is requesting diagnostics for. /// - [DataMember(Name = "identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; @@ -55,8 +53,8 @@ public string? Identifier /// /// Gets or sets the result id of a previous diagnostics response if provided. /// - [DataMember(Name = "previousResultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("previousResultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PreviousResultId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs index 936ad48f1dbde..9e2c17cb63aef 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentDiagnosticReportPartialResult.cs @@ -6,20 +6,19 @@ namespace Roslyn.LanguageServer.Protocol; using System; using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; /// /// Class representing a partial document diagnostic report for a set of related documents. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class DocumentDiagnosticReportPartialResult { /// /// Gets or sets the map of related document diagnostic reports. /// - [DataMember(Name = "relatedDocuments")] + [JsonPropertyName("relatedDocuments")] public Dictionary> RelatedDocuments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs index d06ad65ed2e9a..da8ed8d6512a1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentFilter.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a filter over certain types of documents /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentFilter { /// /// Gets or sets a language id for the filter (e.g. 'typescript'). /// - [DataMember(Name = "language")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("language")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Language { get; @@ -29,8 +27,8 @@ public string? Language /// /// Gets or sets a Uri scheme (e.g. 'file' or 'untitled'). /// - [DataMember(Name = "scheme")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("scheme")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Scheme { get; @@ -40,8 +38,8 @@ public string? Scheme /// /// Gets or sets a glob pattern (e.g. '*.cs'). /// - [DataMember(Name = "pattern")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("pattern")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Pattern { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs index 24f1e6f2008e9..144e53f17087c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the document formatting options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentFormattingOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs index 766dc19e1843e..35f814d3ff9b9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentFormattingParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/formatting message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentFormattingParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be formatted. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the formatting options. /// - [DataMember(Name = "options")] + [JsonPropertyName("options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs index 692bc31ab9b6f..ee7ab022fea0f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlight.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response from a textDocument/documentHighlight request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentHighlight { /// /// Gets or sets the range that the highlight applies to. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -30,11 +28,11 @@ public Range Range /// /// Gets or sets the kind of highlight. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] [DefaultValue(DocumentHighlightKind.Text)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public DocumentHighlightKind Kind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs index 60375fb94cb92..7ec465f1fc335 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum representing the different types of document highlight. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum DocumentHighlightKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs index 40333c450d29b..c01c7eb95adb7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentHighlightOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs index 65107dad7c797..9fed8bdcc35fe 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentHighlightParams.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/documentHighlight request. @@ -20,8 +19,8 @@ internal class DocumentHighlightParams /// /// Gets or sets the value of the PartialResultToken instance. /// - [DataMember(Name = "partialResultToken")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("partialResultToken")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs index 91e3d65073174..a4fe991aef507 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentLink.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response of a textDocument/documentLink request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentLink { /// /// Gets or sets the range the link applies to. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -29,9 +27,9 @@ public Range Range /// /// Gets or sets the uri that the link points to. /// - [DataMember(Name = "target")] + [JsonPropertyName("target")] [JsonConverter(typeof(DocumentUriConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Uri? Target { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs index 42625d49b51a9..c01515f8ae949 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the document link options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentLinkOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether or not the server supports resolve providers. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; @@ -29,8 +27,8 @@ public bool ResolveProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs index 58aaf921ffbc4..780a15fceded5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentLinkParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/documentLink request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentLinkParams : ITextDocumentParams { /// /// Gets or sets the to provide links for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs index c358c3dc1b7be..13105e53003d7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingOptions.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for on type formatting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentOnTypeFormattingOptions { /// /// Gets or sets the first trigger character. /// - [DataMember(Name = "firstTriggerCharacter")] + [JsonPropertyName("firstTriggerCharacter")] public string FirstTriggerCharacter { get; @@ -28,8 +26,8 @@ public string FirstTriggerCharacter /// /// Gets or sets additional trigger characters. /// - [DataMember(Name = "moreTriggerCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("moreTriggerCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? MoreTriggerCharacter { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs index eda0db4a326e2..919c36c1275ae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentOnTypeFormattingParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/onTypeFormatting request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentOnTypeFormattingParams : TextDocumentPositionParams { /// /// Gets or sets the character that was typed. /// - [DataMember(Name = "ch")] + [JsonPropertyName("ch")] public string Character { get; @@ -27,7 +26,7 @@ public string Character /// /// Gets or sets the for the request. /// - [DataMember(Name = "options")] + [JsonPropertyName("options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs index 8dc450883122a..1c4cbe2ce2c7c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the document range formatting options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentRangeFormattingOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs index 103881f93ef17..104830b737a1b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentRangeFormattingParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that is sent with textDocument/rangeFormatting message. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentRangeFormattingParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be formatted. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the selection range to be formatted. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -37,7 +36,7 @@ public Range Range /// /// Gets or sets the formatting options. /// - [DataMember(Name = "options")] + [JsonPropertyName("options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs index bb1f719958c7b..e9110e2f82bc0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbol.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be @@ -14,13 +13,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbol { /// /// Gets or sets the name of this symbol. /// - [DataMember(IsRequired = true, Name = "name")] + [JsonPropertyName("name")] + [JsonRequired] public string Name { get; @@ -30,8 +29,8 @@ public string Name /// /// Gets or sets more detail for this symbol, e.g the signature of a function. /// - [DataMember(Name = "detail")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("detail")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Detail { get; @@ -41,7 +40,7 @@ public string? Detail /// /// Gets or sets the of this symbol. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public SymbolKind Kind { get; @@ -51,8 +50,8 @@ public SymbolKind Kind /// /// Gets or sets a value indicating whether this symbol is deprecated. /// - [DataMember(Name = "deprecated")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("deprecated")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Deprecated { get; @@ -64,7 +63,8 @@ public bool Deprecated /// like comments.This information is typically used to determine if the clients cursor is /// inside the symbol to reveal in the symbol in the UI. /// - [DataMember(IsRequired = true, Name = "range")] + [JsonPropertyName("range")] + [JsonRequired] public Range Range { get; @@ -75,7 +75,8 @@ public Range Range /// Gets or sets the range that should be selected and revealed when this symbol is being picked, e.g the name of a function. /// Must be contained by the `range`. /// - [DataMember(IsRequired = true, Name = "selectionRange")] + [JsonPropertyName("selectionRange")] + [JsonRequired] public Range SelectionRange { get; @@ -85,8 +86,8 @@ public Range SelectionRange /// /// Gets or sets the children of this symbol, e.g. properties of a class. /// - [DataMember(Name = "children")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("children")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentSymbol[]? Children { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs index 2ea84c8ca9ca6..9298ac3d94632 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbolOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs index 3ba1eaa41230b..b12f359aec536 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the parameter sent with textDocument/documentSymbol requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbolParams : ITextDocumentParams { /// /// Gets or sets the text document. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs index f1df03c9304ef..6b4274a7d9fdc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DocumentSymbolSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the initialization setting for document symbols. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class DocumentSymbolSetting : DynamicRegistrationSetting { /// /// Gets or sets the capabilities. /// - [DataMember(Name = "symbolKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("symbolKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolKindSetting? SymbolKind { get; @@ -29,8 +27,8 @@ public SymbolKindSetting? SymbolKind /// /// Gets or sets a value indicating whether the document has hierarchical symbol support. /// - [DataMember(Name = "hierarchicalDocumentSymbolSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("hierarchicalDocumentSymbolSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool HierarchicalDocumentSymbolSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs index 095b552f61176..9bc210064c78f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/DynamicRegistrationSetting.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a setting that can be dynamically registered. /// - [DataContract] internal class DynamicRegistrationSetting { /// @@ -32,8 +30,8 @@ public DynamicRegistrationSetting(bool value) /// /// Gets or sets a value indicating whether setting can be dynamically registered. /// - [DataMember(Name = "dynamicRegistration")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("dynamicRegistration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DynamicRegistration { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs index 6cdae0d11cafc..d79e56516f134 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandOptions.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for execute command support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ExecuteCommandOptions : IWorkDoneProgressOptions { /// /// Gets or sets the commands that are to be executed on the server. /// - [DataMember(Name = "commands")] + [JsonPropertyName("commands")] public string[] Commands { get; @@ -28,8 +26,8 @@ public string[] Commands /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs index d9c934063e7ba..6269066a2ad07 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ExecuteCommandParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from client to server for the workspace/executeCommand request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ExecuteCommandParams { /// /// Gets or sets the command identifier associated with the command handler. /// - [DataMember(Name = "command")] + [JsonPropertyName("command")] public string Command { get; @@ -28,8 +26,8 @@ public string Command /// /// Gets or sets the arguments that the command should be invoked with. /// - [DataMember(Name = "arguments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("arguments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object[]? Arguments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs index 70f3ed31e8acb..dd53e04c4e9f6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionConverter.cs @@ -5,7 +5,8 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Converter used to serialize and deserialize classes extending types defined in the @@ -14,28 +15,46 @@ namespace Roslyn.LanguageServer.Protocol /// Base class that is specified in the /// Microsoft.VisualStudio.LanguageServer.Protocol package. /// Extension class that extends TBase. - internal class VSExtensionConverter : JsonConverter - where TExtension : TBase + internal class VSExtensionConverter : JsonConverter + where TExtension : TBase { - /// - public override bool CanWrite => false; + private JsonSerializerOptions? _trimmedOptions; - /// - public override bool CanConvert(Type objectType) + public override TBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return objectType == typeof(TBase); + return JsonSerializer.Deserialize(ref reader, options); } - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, TBase value, JsonSerializerOptions options) { - return serializer.Deserialize(reader); + // System.Text.Json doesn't serialize properties from derived classes by default, and there's no 'readonly' converters + // like Newtonsoft has. + if (value is TExtension extension) + { + JsonSerializer.Serialize(writer, extension, options); + } + else + { + // There's no ability to fallback to a 'default' serialization, so we clone our options + // and exclude this converter from it to prevent a stack overflow. + JsonSerializer.Serialize(writer, (object)value!, DropConverter(options)); + } } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + private JsonSerializerOptions DropConverter(JsonSerializerOptions options) { - throw new NotImplementedException(); + if (_trimmedOptions != null) + { + return _trimmedOptions; + } + + lock (this) + { + options = new System.Text.Json.JsonSerializerOptions(options); + options.Converters.Remove(this); + _trimmedOptions = options; + return options; + } } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs index 51ce11b3bf2bc..0cd191528ca56 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/Converters/VSExtensionUtilities.cs @@ -2,56 +2,56 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; +/// +/// Utility functions to simplify working with the Visual Studio extensions to the Language Server Protocol. +/// +internal static class VSExtensionUtilities +{ /// - /// Utility functions to simplify working with the Visual Studio extensions to the Language Server Protocol. + /// Adds to the allowing to + /// deserialize the JSON stream into objects which include Visual Studio specific extensions. + /// + /// For example, it allows to correctly deserialize the entries of a + /// 'codeAction/resolve' request into objects even if + /// is defined as an array of . /// - internal static class VSExtensionUtilities + internal static void AddVSExtensionConverters(this JsonSerializerOptions options) { - /// - /// Adds to the allowing to - /// deserialize the JSON stream into objects which include Visual Studio specific extensions. - /// - /// For example, it allows to correctly deserialize the entries of a - /// 'codeAction/resolve' request into objects even if - /// is defined as an array of . - /// - /// - /// If is used in parallel to the execution of this method, - /// its access needs to be synchronized with this method call, to guarantee that the - /// collection is not modified when is in use. - /// - /// Instance of to be configured. - public static void AddVSExtensionConverters(this JsonSerializer serializer) - { - // Reading the number of converters before we start adding new ones - var existingConvertersCount = serializer.Converters.Count; + AddConverters(options.Converters); + } - TryAddConverter(); - TryAddConverter(); - TryAddConverter(); - TryAddConverter(); - TryAddConverter(); + private static void AddConverters(IList converters) + { + // Reading the number of converters before we start adding new ones + var existingConvertersCount = converters.Count; + + TryAddConverter(); + TryAddConverter(); + TryAddConverter(); + TryAddConverter(); + TryAddConverter(); - void TryAddConverter() - where TExtension : TBase + void TryAddConverter() + where TExtension : TBase + { + for (var i = 0; i < existingConvertersCount; i++) { - for (var i = 0; i < existingConvertersCount; i++) + var existingConverterType = converters[i].GetType(); + if (existingConverterType.IsGenericType && + (existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) || existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>)) && + existingConverterType.GenericTypeArguments[0] == typeof(TBase)) { - var existingConverterType = serializer.Converters[i].GetType(); - if (existingConverterType.IsGenericType && - existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) && - existingConverterType.GenericTypeArguments[0] == typeof(TBase)) - { - return; - } + return; } - - serializer.Converters.Add(new VSExtensionConverter()); } + + converters.Add(new VSExtensionConverter()); } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs index 4ac6e47c0a682..aa14da1ed5762 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnostic.cs @@ -4,65 +4,63 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSDiagnostic : Diagnostic { /// /// Gets or sets the project and context (e.g. Win32, MacOS, etc.) in which the diagnostic was generated. /// - [DataMember(Name = "_vs_projects")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projects")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSDiagnosticProjectInformation[]? Projects { get; set; } /// /// Gets or sets an expanded description of the diagnostic. /// - [DataMember(Name = "_vs_expandedMessage")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_expandedMessage")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ExpandedMessage { get; set; } /// /// Gets or sets a message shown when the user hovers over an error. If , then /// is used (use to prevent a tool tip from being shown). /// - [DataMember(Name = "_vs_toolTip")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_toolTip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ToolTip { get; set; } /// /// Gets or sets a non-human-readable identier allowing consolidation of multiple equivalent diagnostics /// (e.g. the same syntax error from builds targeting different platforms). /// - [DataMember(Name = "_vs_identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; set; } /// /// Gets or sets a string describing the diagnostic types (e.g. Security, Performance, Style, etc.). /// - [DataMember(Name = "_vs_diagnosticType")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticType")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DiagnosticType { get; set; } /// /// Gets or sets a rank associated with this diagnostic, used for the default sort. /// will be used if no rank is specified. /// - [DataMember(Name = "_vs_diagnosticRank")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticRank")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSDiagnosticRank? DiagnosticRank { get; set; } /// /// Gets or sets an ID used to associate this diagnostic with a corresponding line in the output window. /// - [DataMember(Name = "_vs_outputId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_outputId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? OutputId { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs index c2a84f512d6eb..7cfc6d2346dc2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSDiagnosticProjectInformation.cs @@ -4,35 +4,33 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// represents the project and context in which the is generated. /// - [DataContract] internal class VSDiagnosticProjectInformation { /// /// Gets or sets a human-readable identifier for the project in which the diagnostic was generated. /// - [DataMember(Name = "_vs_projectName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectName { get; set; } /// /// Gets or sets a human-readable identifier for the build context (e.g. Win32 or MacOS) /// in which the diagnostic was generated. /// - [DataMember(Name = "_vs_context")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_context")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Context { get; set; } /// /// Gets or sets the unique identifier for the project in which the diagnostic was generated. /// - [DataMember(Name = "_vs_projectIdentifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectIdentifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectIdentifier { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs index f02ebbc2bf112..e357fe57b13ae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSGetProjectContextsParams.cs @@ -4,19 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents the parameter that is sent /// with the 'textDocument/_vs_getProjectContexts' request. /// - [DataContract] internal class VSGetProjectContextsParams { /// /// Gets or sets the document for which project contexts are queried. /// - [DataMember(Name = "_vs_textDocument")] + [JsonPropertyName("_vs_textDocument")] public TextDocumentItem TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs index ed16c06838842..87a5115add07d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSImageId.cs @@ -5,7 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents the unique identifier for a Visual Studio image asset. @@ -13,13 +13,12 @@ namespace Roslyn.LanguageServer.Protocol /// A list of valid image ids can be retrieved from the KnownMonikers class /// from the Visual Studio SDK. /// - [DataContract] internal class VSImageId : IEquatable { /// /// Gets or sets the component of the unique identifier. /// - [DataMember(Name = "_vs_guid")] + [JsonPropertyName("_vs_guid")] public Guid Guid { get; @@ -29,7 +28,7 @@ public Guid Guid /// /// Gets or sets the integer component of the unique identifier. /// - [DataMember(Name = "_vs_id")] + [JsonPropertyName("_vs_id")] public int Id { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs index a34ea77a25296..1a68fa04259ca 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSLocation.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSLocation : Location { /// /// Gets or sets the project name to be displayed to user. /// - [DataMember(Name = "_vs_projectName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectName { get; set; } /// @@ -25,8 +23,8 @@ internal class VSLocation : Location /// In case the actual path on disk would be confusing for users, this should be a friendly display name. /// This doesn't have to correspond to a real file path, but must be parsable by the method. /// - [DataMember(Name = "_vs_displayPath")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_displayPath")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DisplayPath { get; set; } } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs index de37989753381..528a49257dea7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContext.cs @@ -5,18 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents a project context. /// - [DataContract] internal class VSProjectContext : IEquatable { /// /// Gets or sets the label for the project context. /// - [DataMember(Name = "_vs_label", IsRequired = true)] + [JsonPropertyName("_vs_label")] + [JsonRequired] public string Label { get; @@ -26,7 +26,8 @@ public string Label /// /// Gets or sets the unique identifier of the project context. /// - [DataMember(Name = "_vs_id", IsRequired = true)] + [JsonPropertyName("_vs_id")] + [JsonRequired] public string Id { get; @@ -36,7 +37,7 @@ public string Id /// /// Gets or sets the context kind of the project context which is used to determine its associated icon. /// - [DataMember(Name = "_vs_kind")] + [JsonPropertyName("_vs_kind")] public VSProjectKind Kind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs index 4c1ee69a0bbff..2cca38acf08ac 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectContextList.cs @@ -4,19 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// represents the response to the /// 'textDocument/_vs_getProjectContexts' request. /// - [DataContract] internal class VSProjectContextList { /// /// Gets or sets the document contexts associated with a text document. /// - [DataMember(Name = "_vs_projectContexts")] + [JsonPropertyName("_vs_projectContexts")] public VSProjectContext[] ProjectContexts { get; @@ -26,7 +25,7 @@ public VSProjectContext[] ProjectContexts /// /// Gets or sets the index of the default entry of the array. /// - [DataMember(Name = "_vs_defaultIndex")] + [JsonPropertyName("_vs_defaultIndex")] public int DefaultIndex { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs index 6a541c5bce361..467374d98fdce 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSProjectKind.cs @@ -4,12 +4,9 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// represents the various kinds of contexts. /// - [DataContract] internal enum VSProjectKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs index 3cd00971b4a28..0605c8afbba7f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSServerCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends allowing to provide /// additional capabilities supported by Visual Studio. /// - [DataContract] internal class VSServerCapabilities : ServerCapabilities { /// /// Gets or sets a value indicating whether the server supports the /// 'textDocument/_vs_getProjectContexts' request. /// - [DataMember(Name = "_vs_projectContextProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_projectContextProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ProjectContextProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs index 62b8b120be35e..84d77d40b6219 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSSymbolInformation.cs @@ -4,34 +4,32 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSSymbolInformation : SymbolInformation { /// /// Gets or sets the icon associated with the symbol. If specified, this icon is used instead of . /// - [DataMember(Name = "_vs_icon")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_icon")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSImageId? Icon { get; set; } /// /// Gets or sets the description of the symbol. /// - [DataMember(Name = "_vs_description")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Description { get; set; } /// /// Gets or sets the hint text for the symbol. /// - [DataMember(Name = "_vs_hintText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_hintText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? HintText { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs index 2cffba1c8d1d8..519e178848056 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Extensions/VSTextDocumentIdentifier.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// extends providing additional properties used by Visual Studio. /// - [DataContract] internal class VSTextDocumentIdentifier : TextDocumentIdentifier, IEquatable { /// /// Gets or sets the project context of the text document. /// - [DataMember(Name = "_vs_projectContext")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectContext")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSProjectContext? ProjectContext { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs b/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs index 7d437e7bd1acb..6133abc68ec98 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FileEvent.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a file change event. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FileEvent { /// /// Gets or sets the URI of the file. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the file change type. /// - [DataMember(Name = "type")] + [JsonPropertyName("type")] public FileChangeType FileChangeType { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs index a45d49f3f9d5f..78f0c975a205d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRange.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a folding range in a document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRange { /// /// Gets or sets the start line value. /// - [DataMember(Name = "startLine")] + [JsonPropertyName("startLine")] public int StartLine { get; @@ -28,8 +26,8 @@ public int StartLine /// /// Gets or sets the start character value. /// - [DataMember(Name = "startCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("startCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? StartCharacter { get; @@ -39,7 +37,7 @@ public int? StartCharacter /// /// Gets or sets the end line value. /// - [DataMember(Name = "endLine")] + [JsonPropertyName("endLine")] public int EndLine { get; @@ -49,8 +47,8 @@ public int EndLine /// /// Gets or sets the end character value. /// - [DataMember(Name = "endCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("endCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? EndCharacter { get; @@ -60,8 +58,8 @@ public int? EndCharacter /// /// Gets or sets the folding range kind. /// - [DataMember(Name = "kind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("kind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public FoldingRangeKind? Kind { get; @@ -71,8 +69,8 @@ public FoldingRangeKind? Kind /// /// Gets or sets the collapsedText. /// - [DataMember(Name = "collapsedText")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("collapsedText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? CollapsedText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs index 05e12df4907f0..bf807c451becf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing various code action kinds. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct FoldingRangeKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs index c68fcaaa0a074..273d431e6da95 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the folding range provider options for initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs index ac57b23d4f24c..fed7123c0cb67 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the folding range request parameter. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeParams : ITextDocumentParams { /// /// Gets or sets the text document associated with the folding range request. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs index f4c10c48e1f26..e57fde0b348aa 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the folding range setting for initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeSetting : DynamicRegistrationSetting { /// /// Gets or sets the range limit for folding ranges. /// - [DataMember(Name = "rangeLimit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rangeLimit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? RangeLimit { get; @@ -29,8 +27,8 @@ public int? RangeLimit /// /// Gets or sets a value indicating whether if client only supports entire line folding only. /// - [DataMember(Name = "lineFoldingOnly")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("lineFoldingOnly")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LineFoldingOnly { get; @@ -40,8 +38,8 @@ public bool LineFoldingOnly /// /// Gets or sets a value indicating the specific options for the folding range. /// - [DataMember(Name = "foldingRange")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("foldingRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public FoldingRangeSettingOptions? FoldingRange { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs index f1a1fe75e4584..35b524fd67379 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FoldingRangeSettingOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the specific options for the folding range. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FoldingRangeSettingOptions : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether if client supports collapsedText on folding ranges. /// - [DataMember(Name = "collapsedText")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("collapsedText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool CollapsedText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs index e1787620e30f5..c7385acedce78 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FormattingOptions.cs @@ -5,22 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents formatting options. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class FormattingOptions { /// /// Gets or sets the number of spaces to be inserted per tab. /// - [DataMember(Name = "tabSize")] + [JsonPropertyName("tabSize")] public int TabSize { get; @@ -30,7 +27,7 @@ public int TabSize /// /// Gets or sets a value indicating whether tabs should be spaces. /// - [DataMember(Name = "insertSpaces")] + [JsonPropertyName("insertSpaces")] public bool InsertSpaces { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs index ab55018017cb3..17eaadd82514a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/FullDocumentDiagnosticReport.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a diagnostic report with a full set of problems. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Full)] internal class FullDocumentDiagnosticReport { /// /// Gets the kind of this report. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] #pragma warning disable CA1822 // Mark members as static public string Kind => DocumentDiagnosticReportKind.Full; #pragma warning restore CA1822 // Mark members as static @@ -27,8 +25,8 @@ internal class FullDocumentDiagnosticReport /// /// Gets or sets the optional result id. /// - [DataMember(Name = "resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; @@ -38,7 +36,7 @@ public string? ResultId /// /// Gets or sets the diagnostics in this report. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public Diagnostic[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Hover.cs b/src/Features/LanguageServer/Protocol/Protocol/Hover.cs index 339ca873bcc6d..27c8399906956 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Hover.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Hover.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the data returned by a textDocument/hover request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Hover { /// @@ -21,7 +19,7 @@ internal class Hover /// If the object is not an array it can be of type , , or . /// // This is nullable because in VS we allow null when VSInternalHover.RawContent is specified instead of Contents - [DataMember(Name = "contents")] + [JsonPropertyName("contents")] public SumType[], MarkupContent>? Contents { get; @@ -31,8 +29,8 @@ public SumType[], MarkupCont /// /// Gets or sets the range over which the hover applies. /// - [DataMember(Name = "range")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Range? Range { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs index 0d5001c8105d4..51c8a83658f0d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/HoverOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the server hover support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class HoverOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs index f1d195fb0a6d8..e19b54f371fe0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/HoverSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the initialization setting for hover. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class HoverSetting : DynamicRegistrationSetting { /// /// Gets or sets the values supported. /// - [DataMember(Name = "contentFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("contentFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MarkupKind[]? ContentFormat { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs index 8a4cc0363a9bd..afd33c2190764 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ImplementationOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ImplementationOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs index 756906f6d62c7..070add9aecfd7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeError.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the error type sent when the initialize request fails. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InitializeError { /// /// Gets or sets a value indicating whether or not to retry. /// - [DataMember(Name = "retry")] + [JsonPropertyName("retry")] public bool Retry { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs index 3f412b17040a5..63a3ed253409c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeErrorCode.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum representing the possible reasons for an initialization error. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum InitializeErrorCode { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs index a3f76104ae979..c0f68e4f7ad23 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeParams.cs @@ -6,22 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter sent with an initialize method request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InitializeParams { /// /// Gets or sets the ID of the process which launched the language server. /// - [DataMember(Name = "processId")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("processId")] public int? ProcessId { get; @@ -35,8 +33,8 @@ public int? ProcessId /// Uses IETF language tags as the value's syntax. /// (See https://en.wikipedia.org/wiki/IETF_language_tag) /// - [DataMember(Name = "locale")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("locale")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Locale { get; @@ -46,8 +44,8 @@ public string? Locale /// /// Gets or sets the workspace root path. /// - [DataMember(Name = "rootPath")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rootPath")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [Obsolete("Deprecated in favour of RootUri")] public string? RootPath { @@ -61,8 +59,7 @@ public string? RootPath /// /// This should be a string representation of an URI. /// - [DataMember(Name = "rootUri")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("rootUri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri? RootUri { @@ -73,8 +70,8 @@ public Uri? RootUri /// /// Gets or sets the initialization options as specified by the client. /// - [DataMember(Name = "initializationOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("initializationOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? InitializationOptions { get; @@ -84,7 +81,7 @@ public object? InitializationOptions /// /// Gets or sets the capabilities supported by the client. /// - [DataMember(Name = "capabilities")] + [JsonPropertyName("capabilities")] public ClientCapabilities Capabilities { get; @@ -94,9 +91,9 @@ public ClientCapabilities Capabilities /// /// Gets or sets the initial trace setting. /// - [DataMember(Name = "trace")] + [JsonPropertyName("trace")] [DefaultValue(typeof(TraceSetting), "off")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public TraceSetting Trace { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs b/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs index 8970f738ee2b2..b3cf86842bdfd 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InitializeResult.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents the result returned by the initialize request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InitializeResult { /// /// Gets or sets the server capabilities. /// - [DataMember(Name = "capabilities")] + [JsonPropertyName("capabilities")] public ServerCapabilities Capabilities { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs index e710324261479..3b934f479d901 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHint.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A class representing inlay hints that appear next to parameters or types. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHint { /// /// Gets or sets the position that the inlay hint applies to. /// - [DataMember(Name = "position")] + [JsonPropertyName("position")] public Position Position { get; @@ -28,7 +26,7 @@ public Position Position /// /// Gets or sets the label associated with this inlay hint. /// - [DataMember(Name = "label")] + [JsonPropertyName("label")] public SumType Label { get; @@ -38,8 +36,8 @@ public SumType Label /// /// Gets or sets the InlayHintKind associated with this inlay hint. /// - [DataMember(Name = "kind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("kind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintKind? Kind { get; @@ -49,8 +47,8 @@ public InlayHintKind? Kind /// /// Gets or sets the TextEdits associated with this inlay hint. /// - [DataMember(Name = "textEdits")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("textEdits")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextEdit[]? TextEdits { get; @@ -60,8 +58,8 @@ public TextEdit[]? TextEdits /// /// Gets or sets the tooltip of this inlay hint. /// - [DataMember(Name = "tooltip")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tooltip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? ToolTip { get; @@ -71,8 +69,8 @@ public SumType? ToolTip /// /// Gets or sets the padding before this inlay hint. /// - [DataMember(Name = "paddingLeft")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("paddingLeft")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PaddingLeft { get; @@ -82,8 +80,8 @@ public bool PaddingLeft /// /// Gets or sets the padding after this inlay hint. /// - [DataMember(Name = "paddingRight")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("paddingRight")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PaddingRight { get; @@ -93,8 +91,8 @@ public bool PaddingRight /// /// Gets or sets the data that should be preserved between a textDocument/inlayHint request and a inlayHint/resolve request. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs index 26b3d742d0007..9e8ccb1548903 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum values for inlay hint kinds. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum InlayHintKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs index f3ae678a2f27f..1946a23322ced 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintLabelPart.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// A class representing inlay hint label parts. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintLabelPart { /// /// Gets or sets the value associated with this label part. /// - [DataMember(Name = "value")] + [JsonPropertyName("value")] public string Value { get; @@ -28,8 +26,8 @@ public string Value /// /// Gets or sets the tooltip of this label part. /// - [DataMember(Name = "tooltip")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("tooltip")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public SumType? ToolTip { get; @@ -39,8 +37,8 @@ public SumType? ToolTip /// /// Gets or sets the location of this label part. /// - [DataMember(Name = "location")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("location")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Location? Location { get; @@ -50,8 +48,8 @@ public Location? Location /// /// Gets or sets the command of this label part. /// - [DataMember(Name = "command")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Command? Command { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs index b86910b8786b0..ce9e1c5aef33a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintOptions.cs @@ -4,29 +4,27 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Server capabilities for inlay hints. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } /// /// Gets or sets a value indicating whether or not the inlay hints support has a resolve provider. /// - [DataMember(Name = "resolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("resolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ResolveProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs index 84556a6ec5a3f..9761b7c09c83d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for a textDocument/inlayHint request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintParams : ITextDocumentParams { /// /// Gets or sets the document identifier to fetch inlay hints results for. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range to fetch inlay hints results for. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs index 5825a4c023ac5..9df07a8275017 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintRegistrationOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; - using System.Runtime.Serialization; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Inlay hint registration options. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintRegistrationOptions : InlayHintOptions, ITextDocumentRegistrationOptions, IStaticRegistrationOptions { /// /// Gets or sets the document filters for this registration option. /// - [DataMember(Name = "documentSelector")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("documentSelector")] public DocumentFilter[]? DocumentSelector { get; @@ -29,8 +27,8 @@ public DocumentFilter[]? DocumentSelector /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "id")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("id")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Id { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs index 560985840651c..25d1c6a154cc3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintResolveSupportSetting.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing settings for inlayHint/resolve support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintResolveSupportSetting { /// /// Gets or sets a value indicating the properties that a client can resolve lazily. /// - [DataMember(Name = "properties")] + [JsonPropertyName("properties")] public string[] Properties { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs index edce450da136b..d6f8008ba47b6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintSetting.cs @@ -4,23 +4,21 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing settings for inlay hint support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether the client supports /// resolving lazily on an inlay hint. /// - [DataMember(Name = "resolveSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resolveSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintResolveSupportSetting? ResolveSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs index 5060f0e640602..7308f9b3d2ec5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InlayHintWorkspaceSetting.cs @@ -2,8 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Newtonsoft.Json; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol { @@ -12,14 +11,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InlayHintWorkspaceSetting { /// /// Gets or sets a value indicating whether the client supports a refresh request sent from the server to the client. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs index 9f34cd35362cd..44c06ee7f3aef 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceEdit.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// A special text edit to provide an insert and a replace operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InsertReplaceEdit { /// /// Gets or sets the string to be inserted. /// - [DataMember(Name = "newText", IsRequired = true)] + [JsonPropertyName("newText")] + [JsonRequired] public string NewText { get; @@ -27,7 +27,8 @@ public string NewText /// /// Gets or sets the range range if the insert is requested /// - [DataMember(Name = "insert", IsRequired = true)] + [JsonPropertyName("insert")] + [JsonRequired] public Range Insert { get; @@ -37,7 +38,8 @@ public Range Insert /// /// Gets or sets the range range if the replace is requested /// - [DataMember(Name = "replace", IsRequired = true)] + [JsonPropertyName("replace")] + [JsonRequired] public Range Replace { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs index 2ffbf04d520c6..26fc9e2c6efaf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InsertReplaceRange.cs @@ -4,18 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents default range of InsertReplaceEdit for the entire completion list /// - [DataContract] internal class InsertReplaceRange { /// /// Gets or sets the insert range. /// - [DataMember(Name = "insert", IsRequired = true)] + [JsonPropertyName("insert")] + [JsonRequired] public Range Insert { get; @@ -25,7 +25,8 @@ public Range Insert /// /// Gets or sets the replace edit range. /// - [DataMember(Name = "replace", IsRequired = true)] + [JsonPropertyName("replace")] + [JsonRequired] public Range Replace { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs index 3de5c8aa688cb..7b7e24c261a79 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/InsertTextModeSupportSetting.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for the tag property on a completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class InsertTextModeSupportSetting { /// /// Gets or sets a value indicating the client supports the `insertTextMode` property on a completion item to override the whitespace handling mode as defined by the client. /// - [DataMember(Name = "valueSet", IsRequired = true)] + [JsonPropertyName("valueSet")] + [JsonRequired] public InsertTextMode[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs index c7a88f7485e0f..13545c7af9359 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextElementConverter.cs @@ -2,83 +2,71 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; -using System.Linq; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ClassifiedTextElementConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ClassifiedTextElementConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ClassifiedTextElementConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ClassifiedTextElement); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ClassifiedTextElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + List objects = new(); + + while (reader.Read()) { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ClassifiedTextElement)) + if (reader.TokenType == JsonTokenType.EndObject) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextElement)}"); + return new ClassifiedTextElement(objects); } - var runTokens = data[nameof(ClassifiedTextElement.Runs)]?.ToArray() ?? - throw new JsonSerializationException($"Missing {nameof(ClassifiedTextElement.Runs)} property"); - var runs = new ClassifiedTextRun[runTokens.Length]; - for (var i = 0; i < runTokens.Length; i++) + if (reader.TokenType == JsonTokenType.PropertyName) { - var runTokenReader = runTokens[i].CreateReader(); - runTokenReader.Read(); - runs[i] = (ClassifiedTextRun)ClassifiedTextRunConverter.Instance.ReadJson(runTokenReader, typeof(ClassifiedTextRun), null, serializer)!; - } + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case nameof(ClassifiedTextElement.Runs): + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + break; - return new ClassifiedTextElement(runs); - } - else - { - throw new JsonSerializationException("Expected start object or null tokens"); + objects.Add(ClassifiedTextRunConverter.Instance.Read(ref reader, typeof(ClassifiedTextRun), options)!); + } + + break; + case ObjectContentConverter.TypeProperty: + if (reader.GetString() != nameof(ClassifiedTextElement)) + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextElement)}"); + break; + default: + reader.Skip(); + break; + } + } } + + throw new JsonException(); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ClassifiedTextElement value, JsonSerializerOptions options) { - if (value is not ClassifiedTextElement classifiedTextElement) + writer.WriteStartObject(); + writer.WritePropertyName(nameof(ClassifiedTextElement.Runs)); + writer.WriteStartArray(); + foreach (var run in value.Runs) { - writer.WriteNull(); + ClassifiedTextRunConverter.Instance.Write(writer, run, options); } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ClassifiedTextElement.Runs)); - writer.WriteStartArray(); - foreach (var run in classifiedTextElement.Runs) - { - ClassifiedTextRunConverter.Instance.WriteJson(writer, run, serializer); - } - writer.WriteEndArray(); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ClassifiedTextElement)); - writer.WriteEndObject(); - } + writer.WriteEndArray(); + writer.WritePropertyName(ObjectContentConverter.TypeProperty); + writer.WriteStringValue(nameof(ClassifiedTextElement)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs index b4becb32c4bef..1b7fe24c3cce7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ClassifiedTextRunConverter.cs @@ -2,91 +2,58 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ClassifiedTextRunConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ClassifiedTextRunConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ClassifiedTextRunConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) - => objectType == typeof(ClassifiedTextRun); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ClassifiedTextRun? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) + if (reader.TokenType == JsonTokenType.StartObject) { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) - { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ClassifiedTextRun)) + var data = JsonDocument.ParseValue(ref reader).RootElement; + if (data.TryGetProperty(ObjectContentConverter.TypeProperty, out var typeProperty) && typeProperty.GetString() != nameof(ClassifiedTextRun)) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextRun)}"); + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ClassifiedTextRun)}"); } - var classificationTypeName = data[nameof(ClassifiedTextRun.ClassificationTypeName)]?.Value(); - var text = data[nameof(ClassifiedTextRun.Text)]?.Value(); - var markerTagType = data[nameof(ClassifiedTextRun.MarkerTagType)]?.Value(); - var style = (ClassifiedTextRunStyle)(data[nameof(ClassifiedTextRun.Style)]?.Value() ?? 0); - return new ClassifiedTextRun(classificationTypeName!, text!, style, markerTagType); + var classificationTypeName = data.GetProperty(nameof(ClassifiedTextRun.ClassificationTypeName)).GetString(); + var text = data.GetProperty(nameof(ClassifiedTextRun.Text)).GetString(); + var markerTagType = data.GetProperty(nameof(ClassifiedTextRun.MarkerTagType)).GetString(); + var style = (ClassifiedTextRunStyle)(data.GetProperty(nameof(ClassifiedTextRun.Style)).GetInt32()); + return new ClassifiedTextRun(classificationTypeName, text, style, markerTagType); } else { - throw new JsonSerializationException("Expected start object or null tokens"); + throw new JsonException("Expected start object or null tokens"); } } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ClassifiedTextRun value, JsonSerializerOptions options) { - if (value is not ClassifiedTextRun classifiedTextRun) + writer.WriteStartObject(); + writer.WriteString(nameof(ClassifiedTextRun.ClassificationTypeName), value.ClassificationTypeName); + writer.WriteString(nameof(ClassifiedTextRun.Text), value.Text); + writer.WriteString(nameof(ClassifiedTextRun.MarkerTagType), value.MarkerTagType); + writer.WriteNumber(nameof(ClassifiedTextRun.Style), (int)value.Style); + writer.WriteNull(nameof(ClassifiedTextRun.Tooltip)); + if (value.Tooltip is not null) { - writer.WriteNull(); + throw new JsonException(); } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ClassifiedTextRun.ClassificationTypeName)); - writer.WriteValue(classifiedTextRun.ClassificationTypeName); - writer.WritePropertyName(nameof(ClassifiedTextRun.Text)); - writer.WriteValue(classifiedTextRun.Text); - writer.WritePropertyName(nameof(ClassifiedTextRun.MarkerTagType)); - writer.WriteValue(classifiedTextRun.MarkerTagType); - writer.WritePropertyName(nameof(ClassifiedTextRun.Style)); - writer.WriteValue(classifiedTextRun.Style); - writer.WritePropertyName(nameof(ClassifiedTextRun.Tooltip)); - writer.WriteNull(); - if (classifiedTextRun.Tooltip is not null) - { - throw new JsonSerializationException(); - } - writer.WritePropertyName(nameof(ClassifiedTextRun.NavigationAction)); - writer.WriteNull(); - if (classifiedTextRun.NavigationAction is not null) - { - throw new JsonSerializationException(); - } - - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ClassifiedTextRun)); - writer.WriteEndObject(); + writer.WriteNull(nameof(ClassifiedTextRun.NavigationAction)); + if (value.NavigationAction is not null) + { + throw new JsonException(); } + + writer.WriteString(ObjectContentConverter.TypeProperty, nameof(ClassifiedTextRun)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs index d1778d40c9251..4d501e5f6c53b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs @@ -2,86 +2,81 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; -using System.Linq; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +namespace Roslyn.LanguageServer.Protocol; /// -/// JsonConverter for serializing and deserializing . +/// System.Text.Json.JsonConverter for serializing and deserializing . /// -internal class ContainerElementConverter : JsonConverter +internal class ContainerElementConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ContainerElementConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ContainerElement); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ContainerElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + if (reader.TokenType == JsonTokenType.StartObject) { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ContainerElement)) - { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ContainerElement)}"); - } + ContainerElementStyle? style = default; + List objects = new(); - var elementTokens = data[nameof(ContainerElement.Elements)]?.ToArray() ?? - throw new JsonSerializationException($"Missing {nameof(ContainerElement.Elements)} property"); - var elements = new object?[elementTokens.Length]; - for (var i = 0; i < elementTokens.Length; i++) + while (reader.Read()) { - var elementTokenReader = elementTokens[i].CreateReader(); - elementTokenReader.Read(); - elements[i] = ObjectContentConverter.Instance.ReadJson(elementTokenReader, typeof(object), null, serializer); - } + if (reader.TokenType == JsonTokenType.EndObject) + { + return new ContainerElement(style ?? throw new JsonException(), objects); + } - var style = (ContainerElementStyle)(data[nameof(ContainerElement.Style)]?.Value() ?? throw new JsonSerializationException()); - return new ContainerElement(style, elements); - } - else - { - throw new JsonSerializationException("Expected start object or null tokens"); + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case nameof(ContainerElement.Elements): + while (reader.Read()) + { + if (reader.TokenType == JsonTokenType.EndArray) + break; + + objects.Add(ObjectContentConverter.Instance.Read(ref reader, typeof(object), options)!); + } + + break; + case nameof(ContainerElement.Style): + style = (ContainerElementStyle)reader.GetInt32(); + break; + case ObjectContentConverter.TypeProperty: + if (reader.GetString() != nameof(ContainerElement)) + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ContainerElement)}"); + break; + default: + reader.Skip(); + break; + } + } + } } + + throw new JsonException(); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ContainerElement value, JsonSerializerOptions options) { - if (value is not ContainerElement containerElement) + writer.WriteStartObject(); + writer.WritePropertyName(nameof(ContainerElement.Elements)); + writer.WriteStartArray(); + foreach (var run in value.Elements) { - writer.WriteNull(); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ContainerElement.Elements)); - writer.WriteStartArray(); - foreach (var run in containerElement.Elements) - { - ObjectContentConverter.Instance.WriteJson(writer, run, serializer); - } - - writer.WriteEndArray(); - writer.WritePropertyName(nameof(ContainerElement.Style)); - writer.WriteValue(containerElement.Style); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ContainerElement)); - writer.WriteEndObject(); + ObjectContentConverter.Instance.Write(writer, run, options); } + writer.WriteEndArray(); + writer.WriteNumber(nameof(ContainerElement.Style), (int)value.Style); + writer.WritePropertyName(ObjectContentConverter.TypeProperty); + writer.WriteStringValue(nameof(ContainerElement)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs deleted file mode 100644 index 868de33bddf28..0000000000000 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Linq; - using Newtonsoft.Json; - - /// - /// Converter used to deserialize objects dropping any property. - /// - internal class DropProgressConverter : JsonConverter - { - /// - public override bool CanWrite => true; - - /// - /// Static method to get a containing a . - /// - /// object containing a . - public static JsonSerializer CreateSerializer() - { - var serializer = new JsonSerializer(); - serializer.Converters.Add(new DropProgressConverter()); - return serializer; - } - - /// - public override bool CanConvert(Type objectType) - { - var isIProgressOfT = objectType.IsConstructedGenericType && objectType.GetGenericTypeDefinition().Equals(typeof(IProgress<>)); - var implementsIProgressOfT = objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(IProgress<>))); - - return isIProgressOfT || implementsIProgressOfT; - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - // We deserialize all IProgress objects as null. - return null; - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer.WriteNull(); - } - } -} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs index cb8d549fe6f65..38c46fcb547a0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs @@ -2,73 +2,66 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Core.Imaging; using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ImageElementConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ImageElementConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ImageElementConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ImageElement); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ImageElement Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) + if (reader.TokenType == JsonTokenType.StartObject) { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) - { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ImageElement)) + ImageId? imageId = default; + string? automationName = default; + + while (reader.Read()) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageElement)}"); - } + if (reader.TokenType == JsonTokenType.EndObject) + { + imageId ??= imageId ?? throw new JsonException(); + return automationName is null ? new ImageElement(imageId.Value) : new ImageElement(imageId.Value, automationName); + } - var imageTokenReader = data[nameof(ImageElement.ImageId)]?.CreateReader() ?? throw new JsonSerializationException(); - imageTokenReader.Read(); - var imageId = (ImageId)ImageIdConverter.Instance.ReadJson(imageTokenReader, typeof(ImageId), null, serializer)!; - var automationName = data[nameof(ImageElement.AutomationName)]?.Value(); - return automationName is null ? new ImageElement(imageId) : new ImageElement(imageId, automationName); - } - else - { - throw new JsonSerializationException("Expected start object or null tokens"); + if (reader.TokenType == JsonTokenType.PropertyName) + { + var propertyName = reader.GetString(); + reader.Read(); + switch (propertyName) + { + case nameof(ImageElement.ImageId): + imageId = ImageIdConverter.Instance.Read(ref reader, typeof(ImageId), options); + break; + case nameof(ImageElement.AutomationName): + automationName = reader.GetString(); + break; + case ObjectContentConverter.TypeProperty: + if (reader.GetString() != nameof(ImageElement)) + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageElement)}"); + break; + default: + reader.Skip(); + break; + } + } + } } + + throw new JsonException("Expected start object or null tokens"); } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ImageElement value, JsonSerializerOptions options) { - if (value is not ImageElement imageElement) - { - writer.WriteNull(); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ImageElement.ImageId)); - ImageIdConverter.Instance.WriteJson(writer, imageElement.ImageId, serializer); - writer.WritePropertyName(nameof(ImageElement.AutomationName)); - writer.WriteValue(imageElement.AutomationName); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ImageElement)); - writer.WriteEndObject(); - } + writer.WriteStartObject(); + writer.WritePropertyName(nameof(ImageElement.ImageId)); + ImageIdConverter.Instance.Write(writer, value.ImageId, options); + writer.WriteString(nameof(ImageElement.AutomationName), value.AutomationName); + writer.WriteString(ObjectContentConverter.TypeProperty, nameof(ImageElement)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs index 825d98d413ea5..80fba1a12ca49 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageIdConverter.cs @@ -2,71 +2,43 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol; - using System; +using System.Text.Json; +using System.Text.Json.Serialization; using Roslyn.Core.Imaging; -using Roslyn.Text.Adornments; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -/// -/// JsonConverter for serializing and deserializing . -/// -internal class ImageIdConverter : JsonConverter +namespace Roslyn.LanguageServer.Protocol; +internal class ImageIdConverter : JsonConverter { - /// - /// A reusable instance of the . - /// public static readonly ImageIdConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(ImageId); - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + public override ImageId Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + if (reader.TokenType == JsonTokenType.StartObject) { - var data = JObject.Load(reader); - var typeProperty = data[ObjectContentConverter.TypeProperty]; - if (typeProperty is not null && typeProperty.ToString() != nameof(ImageId)) + using var document = JsonDocument.ParseValue(ref reader); + var root = document.RootElement; + if (root.TryGetProperty(ObjectContentConverter.TypeProperty, out var typeProperty) && typeProperty.GetString() != nameof(ImageId)) { - throw new JsonSerializationException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageId)}"); + throw new JsonException($"Expected {ObjectContentConverter.TypeProperty} property value {nameof(ImageId)}"); } - var guid = data[nameof(ImageId.Guid)]?.Value() ?? throw new JsonSerializationException(); - var id = data[nameof(ImageId.Id)]?.Value() ?? throw new JsonSerializationException(); + var guid = root.GetProperty(nameof(ImageId.Guid)).GetString() ?? throw new JsonException(); + var id = root.GetProperty(nameof(ImageId.Id)).GetInt32(); return new ImageId(new Guid(guid), id); } else { - throw new JsonSerializationException("Expected start object or null tokens"); + throw new JsonException("Expected start object or null tokens"); } } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, ImageId value, JsonSerializerOptions options) { - if (value is not ImageId imageId) - { - writer.WriteNull(); - } - else - { - writer.WriteStartObject(); - writer.WritePropertyName(nameof(ImageId.Guid)); - writer.WriteValue(imageId.Guid); - writer.WritePropertyName(nameof(ImageId.Id)); - writer.WriteValue(imageId.Id); - writer.WritePropertyName(ObjectContentConverter.TypeProperty); - writer.WriteValue(nameof(ImageId)); - writer.WriteEndObject(); - } + writer.WriteStartObject(); + writer.WriteString(nameof(ImageId.Guid), value.Guid.ToString()); + writer.WriteNumber(nameof(ImageId.Id), value.Id); + writer.WriteString(ObjectContentConverter.TypeProperty, nameof(ImageId)); + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs index 26e0886a0ee51..428088de62230 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ObjectContentConverter.cs @@ -2,115 +2,117 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using Roslyn.Core.Imaging; - using Roslyn.Text.Adornments; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Roslyn.Core.Imaging; +using Roslyn.Text.Adornments; + +namespace Roslyn.LanguageServer.Protocol; +/// +/// Object Content converter used to serialize and deserialize Text and Adornements from VS. +/// +/// This converts the following types: +/// +/// , +/// , +/// , +/// , +/// . +/// +/// Every other type is serialized as a string using the method. +/// +internal class ObjectContentConverter : JsonConverter +{ /// - /// Object Content converter used to serialize and deserialize Text and Adornements from VS. - /// - /// This converts the following types: - /// - /// , - /// , - /// , - /// , - /// . - /// - /// Every other type is serialized as a string using the method. + /// The property name used to save the .NET Type name of the serialized object. /// - internal class ObjectContentConverter : JsonConverter - { - /// - /// The property name used to save the .NET Type name of the serialized object. - /// - public const string TypeProperty = "_vs_type"; + public const string TypeProperty = "_vs_type"; - /// - /// A reusable instance of the . - /// - public static readonly ObjectContentConverter Instance = new ObjectContentConverter(); + /// + /// A reusable instance of the . + /// + public static readonly ObjectContentConverter Instance = new(); - /// - public override bool CanConvert(Type objectType) + public override object? Read(ref Utf8JsonReader reader, Type objectType, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) { - return objectType == typeof(object); + reader.Read(); + return null; } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + else if (reader.TokenType == JsonTokenType.StartObject) { - if (reader.TokenType == JsonToken.Null) - { - reader.Read(); - return null; - } - else if (reader.TokenType == JsonToken.StartObject) + var clonedReader = reader; + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) { - var data = JObject.Load(reader); - var type = data[TypeProperty]?.ToString() ?? throw new JsonSerializationException(); + var data = jsonDocument.RootElement; + var type = data.GetProperty(TypeProperty).GetString() ?? throw new JsonException(); - var tokenReader = data.CreateReader(); - tokenReader.Read(); switch (type) { case nameof(ImageId): - return ImageIdConverter.Instance.ReadJson(tokenReader, typeof(ImageId), null, serializer); + return ImageIdConverter.Instance.Read(ref clonedReader, typeof(ImageId), options); case nameof(ImageElement): - return ImageElementConverter.Instance.ReadJson(tokenReader, typeof(ImageElement), null, serializer); + return ImageElementConverter.Instance.Read(ref clonedReader, typeof(ImageElement), options); case nameof(ContainerElement): - return ContainerElementConverter.Instance.ReadJson(tokenReader, typeof(ContainerElementConverter), null, serializer); + return ContainerElementConverter.Instance.Read(ref clonedReader, typeof(ContainerElementConverter), options); case nameof(ClassifiedTextElement): - return ClassifiedTextElementConverter.Instance.ReadJson(tokenReader, typeof(ClassifiedTextElementConverter), null, serializer); + return ClassifiedTextElementConverter.Instance.Read(ref clonedReader, typeof(ClassifiedTextElementConverter), options); case nameof(ClassifiedTextRun): - return ClassifiedTextRunConverter.Instance.ReadJson(tokenReader, typeof(ClassifiedTextRunConverter), null, serializer); + return ClassifiedTextRunConverter.Instance.Read(ref clonedReader, typeof(ClassifiedTextRunConverter), options); default: return data; } } - else - { - return serializer.Deserialize(reader); - } } + else if (reader.TokenType == JsonTokenType.String) + { + return reader.GetString(); + } + else if (reader.TokenType == JsonTokenType.Number) + { + return reader.GetInt32(); + } + else + { + return JsonSerializer.Deserialize(ref reader, objectType, options); + } + } - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + /// + public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) + { + if (value is null) { - if (value is null) - { - writer.WriteNull(); - return; - } + writer.WriteNullValue(); + return; + } - switch (value) - { - case ImageId: - ImageIdConverter.Instance.WriteJson(writer, value, serializer); - break; - case ImageElement: - ImageElementConverter.Instance.WriteJson(writer, value, serializer); - break; - case ContainerElement: - ContainerElementConverter.Instance.WriteJson(writer, value, serializer); - break; - case ClassifiedTextElement: - ClassifiedTextElementConverter.Instance.WriteJson(writer, value, serializer); - break; - case ClassifiedTextRun: - ClassifiedTextRunConverter.Instance.WriteJson(writer, value, serializer); - break; - default: - // According to the docs of ContainerElement point to https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.adornments.iviewelementfactoryservice - // which states that Editor supports ClassifiedTextElement, ContainerElement, ImageElement and that other objects would be presented using ToString unless an extender - // exports a IViewElementFactory for that type. So I will simply serialize unknown objects as strings. - writer.WriteValue(value.ToString()); - break; - } + switch (value) + { + case ImageId: + ImageIdConverter.Instance.Write(writer, (ImageId)value, options); + break; + case ImageElement: + ImageElementConverter.Instance.Write(writer, (ImageElement)value, options); + break; + case ContainerElement: + ContainerElementConverter.Instance.Write(writer, (ContainerElement)value, options); + break; + case ClassifiedTextElement: + ClassifiedTextElementConverter.Instance.Write(writer, (ClassifiedTextElement)value, options); + break; + case ClassifiedTextRun: + ClassifiedTextRunConverter.Instance.Write(writer, (ClassifiedTextRun)value, options); + break; + default: + // According to the docs of ContainerElement point to https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.adornments.iviewelementfactoryservice + // which states that Editor supports ClassifiedTextElement, ContainerElement, ImageElement and that other objects would be presented using ToString unless an extender + // exports a IViewElementFactory for that type. So I will simply serialize unknown objects as strings. + writer.WriteStringValue(value.ToString()); + break; } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs index 1bd71cd566617..881fa953b1fc1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/RegexConverter.cs @@ -2,48 +2,26 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Text.RegularExpressions; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; - using Newtonsoft.Json; +namespace Roslyn.LanguageServer.Protocol; - /// - /// Similar to https://devdiv.visualstudio.com/DevDiv/_git/VS-Platform?path=/src/Productivity/TextMate/Core/LanguageConfiguration/Impl/FastRegexConverter.cs - /// to allow us to only compile the regex option once. - /// - internal class RegexConverter : JsonConverter +/// +/// Similar to https://devdiv.visualstudio.com/DevDiv/_git/VS-Platform?path=/src/Productivity/TextMate/Core/LanguageConfiguration/Impl/FastRegexConverter.cs +/// to allow us to only compile the regex option once. +/// +internal class RegexConverter : JsonConverter +{ + public override Regex? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override bool CanConvert(Type objectType) - { - // nameof is faster than typeof, so below is a fast path. - return objectType.Name == nameof(Regex) && objectType == typeof(Regex); - } - - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - // Create a custom deserializer for regex as the default provided by newtonsoft doesn't - // specify the Compiled option. - var regexText = reader.Value as string; - if (string.IsNullOrEmpty(regexText)) - { - return null; - } - - return new Regex(regexText, RegexOptions.Compiled | RegexOptions.ECMAScript, matchTimeout: TimeSpan.FromMilliseconds(1000)); - } + return new Regex(reader.GetString(), RegexOptions.Compiled | RegexOptions.ECMAScript, matchTimeout: TimeSpan.FromMilliseconds(1000)); + } - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - if (value is Regex valueAsRegex) - { - writer.WriteValue(valueAsRegex.ToString()); - } - else - { - throw new ArgumentException($"{nameof(value)} must be of type {nameof(Regex)}"); - } - } + public override void Write(Utf8JsonWriter writer, Regex value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString()); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs index a1d482e7a480a..23dc024eca0ee 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSCodeInternalExtensionUtilities.cs @@ -4,7 +4,9 @@ namespace Roslyn.LanguageServer.Protocol { - using Newtonsoft.Json; + using System.Collections.Generic; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Utilities to aid work with VS Code LSP Extensions. @@ -15,37 +17,31 @@ internal static class VSCodeInternalExtensionUtilities /// Adds necessary to deserialize /// JSON stream into objects which include VS Code-specific extensions. /// - /// - /// If is used in parallel to execution of this method, - /// its access needs to be synchronized with this method call, to guarantee that - /// collection is not modified when in use. - /// - /// Instance of which is guaranteed to not work in parallel to this method call. - public static void AddVSCodeInternalExtensionConverters(this JsonSerializer serializer) + public static void AddVSCodeInternalExtensionConverters(this JsonSerializerOptions options) { // Reading the number of converters before we start adding new ones - var existingConvertersCount = serializer.Converters.Count; + var existingConvertersCount = options.Converters.Count; - AddOrReplaceConverter(); - AddOrReplaceConverter(); + AddOrReplaceConverter(options.Converters); + AddOrReplaceConverter(options.Converters); - void AddOrReplaceConverter() - where TExtension : TBase + void AddOrReplaceConverter(IList converters) + where TExtension : TBase { for (var i = 0; i < existingConvertersCount; i++) { - var existingConverterType = serializer.Converters[i].GetType(); + var existingConverterType = converters[i].GetType(); if (existingConverterType.IsGenericType && - existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) && + (existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) || existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>)) && existingConverterType.GenericTypeArguments[0] == typeof(TBase)) { - serializer.Converters.RemoveAt(i); + converters.RemoveAt(i); existingConvertersCount--; break; } } - serializer.Converters.Add(new VSExtensionConverter()); + converters.Add(new VSExtensionConverter()); } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs index dea7f8218f125..15e162c6c13eb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/VSInternalExtensionUtilities.cs @@ -2,72 +2,69 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; +/// +/// Utilities to aid work with the LSP Extensions. +/// +internal static class VSInternalExtensionUtilities +{ /// - /// Utilities to aid work with the LSP Extensions. + /// Adds necessary to deserialize + /// JSON stream into objects which include VS-specific extensions. /// - internal static class VSInternalExtensionUtilities + internal static void AddVSInternalExtensionConverters(this JsonSerializerOptions options) { - /// - /// Adds necessary to deserialize - /// JSON stream into objects which include VS-specific extensions. - /// - /// - /// If is used in parallel to execution of this method, - /// its access needs to be synchronized with this method call, to guarantee that - /// collection is not modified when in use. - /// - /// Instance of which is guaranteed to not work in parallel to this method call. - public static void AddVSInternalExtensionConverters(this JsonSerializer serializer) - { - VSExtensionUtilities.AddVSExtensionConverters(serializer); + VSExtensionUtilities.AddVSExtensionConverters(options); + AddConverters(options.Converters); + } - // Reading the number of converters before we start adding new ones - var existingConvertersCount = serializer.Converters.Count; + private static void AddConverters(IList converters) + { + // Reading the number of converters before we start adding new ones + var existingConvertersCount = converters.Count; - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); - AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); + AddOrReplaceConverter(); - void AddOrReplaceConverter() - where TExtension : TBase + void AddOrReplaceConverter() + where TExtension : TBase + { + for (var i = 0; i < existingConvertersCount; i++) { - for (var i = 0; i < existingConvertersCount; i++) + var existingConverterType = converters[i].GetType(); + if (existingConverterType.IsGenericType && + (existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) || existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>)) && + existingConverterType.GenericTypeArguments[0] == typeof(TBase)) { - var existingConverterType = serializer.Converters[i].GetType(); - if (existingConverterType.IsGenericType && - existingConverterType.GetGenericTypeDefinition() == typeof(VSExtensionConverter<,>) && - existingConverterType.GenericTypeArguments[0] == typeof(TBase)) - { - serializer.Converters.RemoveAt(i); - existingConvertersCount--; - break; - } + converters.RemoveAt(i); + existingConvertersCount--; + break; } - - serializer.Converters.Add(new VSExtensionConverter()); } + + converters.Add(new VSExtensionConverter()); } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs index 2e2756573aa8e..3ac8099a356ec 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticKind.cs @@ -5,14 +5,11 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the kind of a diagnostic. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct VSInternalDiagnosticKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs index 5ae9280872f06..1bf9a1d635dc5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticOptions.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Server provided options for pull diagnostic requests. /// - [DataContract] internal record class VSInternalDiagnosticOptions : IWorkDoneProgressOptions { /// @@ -20,29 +18,29 @@ internal record class VSInternalDiagnosticOptions : IWorkDoneProgressOptions /// VS client will then use the information to do any merging logic in the Error List. /// Maps to . /// - [DataMember(Name = "_vs_buildOnlyDiagnosticIds")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_buildOnlyDiagnosticIds")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? BuildOnlyDiagnosticIds { get; init; } /// /// Gets or sets a list of diagnostic kinds used to query diagnostics in each context. /// - [DataMember(Name = "_vs_diagnosticKinds")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticKinds")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticKind[]? DiagnosticKinds { get; init; } /// /// Gets or sets a value indicating whether the server provides support for sending diagnostics requests for all project contexts. /// - [DataMember(Name = "_vs_supportsMultipleContextDiagnostics")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsMultipleContextDiagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsMultipleContextsDiagnostics { get; init; } /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "_vs_workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs index 40e808078218a..d45d258d1091c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticParams.cs @@ -4,27 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request parameter used. /// - [DataContract] internal class VSInternalDiagnosticParams { /// /// Gets or sets the document for which diagnostics are desired. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier? TextDocument { get; set; } /// /// Gets or sets a value indicating what kind of diagnostic this request is querying for. /// - [DataMember(Name = "_vs_queryingDiagnosticKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_queryingDiagnosticKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticKind? QueryingDiagnosticKind { get; set; } /// @@ -46,8 +44,8 @@ internal class VSInternalDiagnosticParams /// document, then all reports are expected to have the same /// previousResultId. /// - [DataMember(Name = "_vs_previousResultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PreviousResultId { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs index 29b96efd3f442..71421912abc11 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDiagnosticReport.cs @@ -5,13 +5,11 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request report. /// - [DataContract] internal class VSInternalDiagnosticReport { /// @@ -21,8 +19,8 @@ internal class VSInternalDiagnosticReport /// diagnostics.The server can use this result ID to avoid resending /// diagnostics that had previously been sent. /// - [DataMember(Name = "_vs_resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// @@ -32,8 +30,8 @@ internal class VSInternalDiagnosticReport /// /// Is null if no changes in the diagnostics. Is empty if there is no diagnostic. /// - [DataMember(Name = "_vs_diagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Diagnostic[]? Diagnostics { get; set; } /// @@ -43,8 +41,8 @@ internal class VSInternalDiagnosticReport /// entries tagged with will /// be hidden in the editor. /// - [DataMember(Name = "_vs_identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Identifier { get; set; } /// @@ -53,8 +51,8 @@ internal class VSInternalDiagnosticReport /// /// Diagnostics in a superseded report will be hidden if they have the PotentialDuplicate VSDiagnosticTag. /// - [DataMember(Name = "_vs_supersedes")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_supersedes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Supersedes { get; set; } /// @@ -63,15 +61,15 @@ internal class VSInternalDiagnosticReport /// outputId and the (outputKey, outputId) uniquely identify /// a line of text in the output window). /// - [DataMember(Name = "_vs_outputKey")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_outputKey")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Guid? OutputKey { get; set; } /// /// Gets or sets the document version. /// - [DataMember(Name = "_vs_version")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_version")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? Version { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs index 8b61f2fbf6438..2a136bb3fab29 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalDocumentDiagnosticsParams.cs @@ -5,27 +5,25 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request for a specific document. /// - [DataContract] internal class VSInternalDocumentDiagnosticsParams : VSInternalDiagnosticParams, IPartialResultParams { /// /// Gets or sets an optional token that a server can use to report work done progress. /// - [DataMember(Name = Methods.WorkDoneTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.WorkDoneTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? WorkDoneToken { get; set; } /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs index b4841e7c41664..a14f42cf55b4e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticReport.cs @@ -4,18 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request result for all documents. /// - [DataContract] internal class VSInternalWorkspaceDiagnosticReport : VSInternalDiagnosticReport { /// /// Gets or sets the document for which diagnostics is returned. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier? TextDocument { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs index 1ddf9bd496914..61d145c73c8bc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Diagnostics/VSInternalWorkspaceDiagnosticsParams.cs @@ -5,41 +5,39 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a diagnostic pull request for all documents. /// - [DataContract] internal class VSInternalWorkspaceDiagnosticsParams : IPartialResultParams { /// /// Gets or sets the current state of the documents the client already has received. /// - [DataMember(Name = "_vs_previousResults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticParams[]? PreviousResults { get; set; } /// /// Gets or sets an optional token that a server can use to report work done progress. /// - [DataMember(Name = Methods.WorkDoneTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.WorkDoneTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? WorkDoneToken { get; set; } /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } /// /// Gets or sets a value indicating what kind of diagnostic this request is querying for. /// - [DataMember(Name = "_vs_queryingDiagnosticKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_queryingDiagnosticKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticKind? QueryingDiagnosticKind { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs index f99efeba0bbea..2c08c682897e0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionList.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A subclass of the VS LSP protocol extension that has a fast serialization path. /// - [DataContract] [JsonConverter(typeof(OptimizedVSCompletionListJsonConverter))] internal sealed class OptimizedVSCompletionList : VSInternalCompletionList { diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs index 2dbe0839d6d76..5288dde922493 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Efficiency/OptimizedVSCompletionListJsonConverter.cs @@ -2,272 +2,250 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using Roslyn.Core.Imaging; - using Newtonsoft.Json; - - internal class OptimizedVSCompletionListJsonConverter : JsonConverter - { - public static readonly OptimizedVSCompletionListJsonConverter Instance = new OptimizedVSCompletionListJsonConverter(); - private static readonly ConcurrentDictionary IconRawJson = new ConcurrentDictionary(); +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using Roslyn.Core.Imaging; - public override bool CanRead => false; +namespace Roslyn.LanguageServer.Protocol; - public override bool CanConvert(Type objectType) => typeof(OptimizedVSCompletionList) == objectType; +internal class OptimizedVSCompletionListJsonConverter : JsonConverter +{ + public static readonly OptimizedVSCompletionListJsonConverter Instance = new(); + private static readonly ConcurrentDictionary IconRawJson = new ConcurrentDictionary(); - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) => throw new NotImplementedException(); + public override OptimizedVSCompletionList Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + public override void Write(Utf8JsonWriter writer, OptimizedVSCompletionList value, JsonSerializerOptions options) + { + if (value is null) { - if (value is null) - { - writer.WriteNull(); - return; - } + writer.WriteNullValue(); + return; + } - var completionList = (VSInternalCompletionList)value; + var completionList = (VSInternalCompletionList)value; - writer.WriteStartObject(); + writer.WriteStartObject(); - if (completionList.SuggestionMode) - { - writer.WritePropertyName(VSInternalCompletionList.SuggestionModeSerializedName); - writer.WriteValue(completionList.SuggestionMode); - } - else - { - // Default is "false" so no need to serialize - } + if (completionList.SuggestionMode) + { + writer.WriteBoolean(VSInternalCompletionList.SuggestionModeSerializedName, completionList.SuggestionMode); + } + else + { + // Default is "false" so no need to serialize + } - if (completionList.ContinueCharacters != null && completionList.ContinueCharacters.Length > 0) - { - writer.WritePropertyName(VSInternalCompletionList.ContinueCharactersSerializedName); - serializer.Serialize(writer, completionList.ContinueCharacters); - } + if (completionList.ContinueCharacters != null && completionList.ContinueCharacters.Length > 0) + { + writer.WritePropertyName(VSInternalCompletionList.ContinueCharactersSerializedName); + JsonSerializer.Serialize(writer, completionList.ContinueCharacters, options); + } - if (completionList.Data != null) - { - writer.WritePropertyName(VSInternalCompletionList.DataSerializedName); - serializer.Serialize(writer, completionList.Data); - } + if (completionList.Data != null) + { + writer.WritePropertyName(VSInternalCompletionList.DataSerializedName); + JsonSerializer.Serialize(writer, completionList.Data, options); + } - if (completionList.CommitCharacters != null) - { - writer.WritePropertyName(VSInternalCompletionList.CommitCharactersSerializedName); - serializer.Serialize(writer, completionList.CommitCharacters); - } + if (completionList.CommitCharacters != null) + { + writer.WritePropertyName(VSInternalCompletionList.CommitCharactersSerializedName); + JsonSerializer.Serialize(writer, completionList.CommitCharacters, options); + } - if (completionList.IsIncomplete) - { - writer.WritePropertyName("isIncomplete"); - writer.WriteValue(completionList.IsIncomplete); - } - else - { - // Default is "false" so no need to serialize - } + if (completionList.IsIncomplete) + { + writer.WriteBoolean("isIncomplete", completionList.IsIncomplete); + } + else + { + // Default is "false" so no need to serialize + } - writer.WritePropertyName("items"); - if (completionList.Items == null || completionList.Items.Length == 0) - { - writer.WriteRawValue("[]"); - } - else - { - writer.WriteStartArray(); + writer.WritePropertyName("items"); + if (completionList.Items == null || completionList.Items.Length == 0) + { + writer.WriteRawValue("[]"); + } + else + { + writer.WriteStartArray(); - var itemRawJsonCache = new Dictionary(capacity: 1); + var itemRawJsonCache = new Dictionary(capacity: 1); - foreach (var completionItem in completionList.Items) + foreach (var completionItem in completionList.Items) + { + if (completionItem == null) { - if (completionItem == null) - { - continue; - } - - WriteCompletionItem(writer, completionItem, serializer, itemRawJsonCache); + continue; } - writer.WriteEndArray(); - } - - if (completionList.ItemDefaults != null) - { - writer.WritePropertyName("itemDefaults"); - serializer.Serialize(writer, completionList.ItemDefaults); + WriteCompletionItem(writer, completionItem, options, itemRawJsonCache); } - writer.WriteEndObject(); + writer.WriteEndArray(); } - private static void WriteCompletionItem(JsonWriter writer, CompletionItem completionItem, JsonSerializer serializer, Dictionary itemRawJsonCache) + if (completionList.ItemDefaults != null) { - writer.WriteStartObject(); + writer.WritePropertyName("itemDefaults"); + JsonSerializer.Serialize(writer, completionList.ItemDefaults, options); + } + + writer.WriteEndObject(); + } + + private static void WriteCompletionItem(Utf8JsonWriter writer, CompletionItem completionItem, JsonSerializerOptions options, Dictionary itemRawJsonCache) + { + writer.WriteStartObject(); - if (completionItem is VSInternalCompletionItem vsCompletionItem) + if (completionItem is VSInternalCompletionItem vsCompletionItem) + { + if (vsCompletionItem.Icon != null) { - if (vsCompletionItem.Icon != null) + if (!IconRawJson.TryGetValue(vsCompletionItem.Icon.ImageId, out var jsonString)) { - if (!IconRawJson.TryGetValue(vsCompletionItem.Icon.ImageId, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(vsCompletionItem.Icon, Formatting.None, ImageElementConverter.Instance); - IconRawJson.TryAdd(vsCompletionItem.Icon.ImageId, jsonString); - } - - writer.WritePropertyName(VSInternalCompletionItem.IconSerializedName); - writer.WriteRawValue(jsonString); + jsonString = JsonSerializer.Serialize(vsCompletionItem.Icon, options); + IconRawJson.TryAdd(vsCompletionItem.Icon.ImageId, jsonString); } + writer.WritePropertyName(VSInternalCompletionItem.IconSerializedName); + writer.WriteRawValue(jsonString); + } - if (vsCompletionItem.Description != null) - { - writer.WritePropertyName(VSInternalCompletionItem.DescriptionSerializedName); - ClassifiedTextElementConverter.Instance.WriteJson(writer, vsCompletionItem.Description, serializer); - } + if (vsCompletionItem.Description != null) + { + writer.WritePropertyName(VSInternalCompletionItem.DescriptionSerializedName); + JsonSerializer.Serialize(writer, vsCompletionItem.Description, options); + } - if (vsCompletionItem.VsCommitCharacters?.Value is string[] basicCommitCharacters - && basicCommitCharacters.Length > 0) - { - if (!itemRawJsonCache.TryGetValue(basicCommitCharacters, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(basicCommitCharacters); - itemRawJsonCache.Add(basicCommitCharacters, jsonString); - } - - writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); - writer.WriteRawValue(jsonString); - } - else if (vsCompletionItem.VsCommitCharacters?.Value is VSInternalCommitCharacter[] augmentedCommitCharacters - && augmentedCommitCharacters.Length > 0) + if (vsCompletionItem.VsCommitCharacters?.Value is string[] basicCommitCharacters + && basicCommitCharacters.Length > 0) + { + if (!itemRawJsonCache.TryGetValue(basicCommitCharacters, out var jsonString)) { - if (!itemRawJsonCache.TryGetValue(augmentedCommitCharacters, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(augmentedCommitCharacters); - itemRawJsonCache.Add(augmentedCommitCharacters, jsonString); - } - - writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); - writer.WriteRawValue(jsonString); + jsonString = JsonSerializer.Serialize(basicCommitCharacters, options); + itemRawJsonCache.Add(basicCommitCharacters, jsonString); } - if (vsCompletionItem.VsResolveTextEditOnCommit) + writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); + writer.WriteRawValue(jsonString); + } + else if (vsCompletionItem.VsCommitCharacters?.Value is VSInternalCommitCharacter[] augmentedCommitCharacters + && augmentedCommitCharacters.Length > 0) + { + if (!itemRawJsonCache.TryGetValue(augmentedCommitCharacters, out var jsonString)) { - writer.WritePropertyName(VSInternalCompletionItem.VsResolveTextEditOnCommitName); - writer.WriteValue(vsCompletionItem.VsResolveTextEditOnCommit); + jsonString = JsonSerializer.Serialize(augmentedCommitCharacters, options); + itemRawJsonCache.Add(augmentedCommitCharacters, jsonString); } - } - var label = completionItem.Label; - if (label != null) - { - writer.WritePropertyName("label"); - writer.WriteValue(label); + writer.WritePropertyName(VSInternalCompletionItem.VsCommitCharactersSerializedName); + writer.WriteRawValue(jsonString); } - if (completionItem.LabelDetails != null) + if (vsCompletionItem.VsResolveTextEditOnCommit) { - writer.WritePropertyName("labelDetails"); - serializer.Serialize(writer, completionItem.LabelDetails); + writer.WriteBoolean(VSInternalCompletionItem.VsResolveTextEditOnCommitName, vsCompletionItem.VsResolveTextEditOnCommit); } + } - writer.WritePropertyName("kind"); - writer.WriteValue(completionItem.Kind); - - if (completionItem.Tags != null) - { - writer.WritePropertyName("tags"); - serializer.Serialize(writer, completionItem.Tags); - } + var label = completionItem.Label; + if (label != null) + { + writer.WriteString("label", label); + } - if (completionItem.Detail != null) - { - writer.WritePropertyName("detail"); - writer.WriteValue(completionItem.Detail); - } + if (completionItem.LabelDetails != null) + { + writer.WritePropertyName("labelDetails"); + JsonSerializer.Serialize(writer, completionItem.LabelDetails, options); + } - if (completionItem.Documentation != null) - { - writer.WritePropertyName("documentation"); - serializer.Serialize(writer, completionItem.Documentation); - } + writer.WriteNumber("kind", (int)completionItem.Kind); - // Only render preselect if it's "true" - if (completionItem.Preselect) - { - writer.WritePropertyName("preselect"); - writer.WriteValue(completionItem.Preselect); - } + if (completionItem.Detail != null) + { + writer.WriteString("detail", completionItem.Detail); + } - if (completionItem.SortText != null && !string.Equals(completionItem.SortText, label, StringComparison.Ordinal)) - { - writer.WritePropertyName("sortText"); - writer.WriteValue(completionItem.SortText); - } + if (completionItem.Documentation != null) + { + writer.WritePropertyName("documentation"); + JsonSerializer.Serialize(writer, completionItem.Documentation, options); + } - if (completionItem.FilterText != null && !string.Equals(completionItem.FilterText, label, StringComparison.Ordinal)) - { - writer.WritePropertyName("filterText"); - writer.WriteValue(completionItem.FilterText); - } + // Only render preselect if it's "true" + if (completionItem.Preselect) + { + writer.WriteBoolean("preselect", completionItem.Preselect); + } - if (completionItem.InsertText != null && !string.Equals(completionItem.InsertText, label, StringComparison.Ordinal)) - { - writer.WritePropertyName("insertText"); - writer.WriteValue(completionItem.InsertText); - } + if (completionItem.SortText != null && !string.Equals(completionItem.SortText, label, StringComparison.Ordinal)) + { + writer.WriteString("sortText", completionItem.SortText); + } - if (completionItem.InsertTextFormat != default && completionItem.InsertTextFormat != InsertTextFormat.Plaintext) - { - writer.WritePropertyName("insertTextFormat"); - writer.WriteValue(completionItem.InsertTextFormat); - } + if (completionItem.FilterText != null && !string.Equals(completionItem.FilterText, label, StringComparison.Ordinal)) + { + writer.WriteString("filterText", completionItem.FilterText); + } - if (completionItem.TextEdit != null) - { - writer.WritePropertyName("textEdit"); - serializer.Serialize(writer, completionItem.TextEdit); - } + if (completionItem.InsertText != null && !string.Equals(completionItem.InsertText, label, StringComparison.Ordinal)) + { + writer.WriteString("insertText", completionItem.InsertText); + } - if (completionItem.TextEditText != null) - { - writer.WritePropertyName("textEditText"); - serializer.Serialize(writer, completionItem.TextEditText); - } + if (completionItem.InsertTextFormat != default && completionItem.InsertTextFormat != InsertTextFormat.Plaintext) + { + writer.WriteNumber("insertTextFormat", (int)completionItem.InsertTextFormat); + } - if (completionItem.AdditionalTextEdits != null && completionItem.AdditionalTextEdits.Length > 0) - { - writer.WritePropertyName("additionalTextEdits"); - serializer.Serialize(writer, completionItem.AdditionalTextEdits); - } + if (completionItem.TextEdit != null) + { + writer.WritePropertyName("textEdit"); + JsonSerializer.Serialize(writer, completionItem.TextEdit, options); + } - if (completionItem.CommitCharacters != null && completionItem.CommitCharacters.Length > 0) - { - if (!itemRawJsonCache.TryGetValue(completionItem.CommitCharacters, out var jsonString)) - { - jsonString = JsonConvert.SerializeObject(completionItem.CommitCharacters); - itemRawJsonCache.Add(completionItem.CommitCharacters, jsonString); - } + if (completionItem.TextEditText != null) + { + writer.WritePropertyName("textEditText"); + JsonSerializer.Serialize(writer, completionItem.TextEditText, options); + } - writer.WritePropertyName("commitCharacters"); - writer.WriteRawValue(jsonString); - } + if (completionItem.AdditionalTextEdits != null && completionItem.AdditionalTextEdits.Length > 0) + { + writer.WritePropertyName("additionalTextEdits"); + JsonSerializer.Serialize(writer, completionItem.AdditionalTextEdits, options); + } - if (completionItem.Command != null) + if (completionItem.CommitCharacters != null && completionItem.CommitCharacters.Length > 0) + { + if (!itemRawJsonCache.TryGetValue(completionItem.CommitCharacters, out var jsonString)) { - writer.WritePropertyName("command"); - serializer.Serialize(writer, completionItem.Command); + jsonString = JsonSerializer.Serialize(completionItem.CommitCharacters, options); + itemRawJsonCache.Add(completionItem.CommitCharacters, jsonString); } - if (completionItem.Data != null) - { - writer.WritePropertyName("data"); - serializer.Serialize(writer, completionItem.Data); - } + writer.WritePropertyName("commitCharacters"); + writer.WriteRawValue(jsonString); + } + + if (completionItem.Command != null) + { + writer.WritePropertyName("command"); + JsonSerializer.Serialize(writer, completionItem.Command, options); + } - writer.WriteEndObject(); + if (completionItem.Data != null) + { + writer.WritePropertyName("data"); + JsonSerializer.Serialize(writer, completionItem.Data, options); } + + writer.WriteEndObject(); } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs index e472d496076d0..a96c5482e09cb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using Roslyn.Text.Adornments; namespace Roslyn.Text.Adornments { diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs index a7d46e257f83e..a2ad7eb9fae76 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSFoldingRangeSetting.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class used to extend to add internal capabilities. @@ -15,8 +14,8 @@ internal class VSFoldingRangeSetting : FoldingRangeSetting /// /// Gets or sets a value indicating whether if client only supports entire line folding only. /// - [DataMember(Name = "_vs_refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs index 476b1bbc3238e..ec9d0e7459dee 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClientCapabilities.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for ClientCapabilities with fields specific to Visual Studio. /// - [DataContract] internal class VSInternalClientCapabilities : ClientCapabilities { /// /// Gets or sets a value indicating whether client supports Visual Studio extensions. /// - [DataMember(Name = "_vs_supportsVisualStudioExtensions")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsVisualStudioExtensions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsVisualStudioExtensions { get; @@ -28,8 +26,8 @@ public bool SupportsVisualStudioExtensions /// Gets or sets a value indicating what level of snippet support is available from Visual Studio Client. /// v1.0 refers to only default tab stop support i.e. support for $0 which manipualtes the cursor position. /// - [DataMember(Name = "_vs_supportedSnippetVersion")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_supportedSnippetVersion")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalSnippetSupportLevel? SupportedSnippetVersion { get; @@ -39,8 +37,8 @@ public VSInternalSnippetSupportLevel? SupportedSnippetVersion /// /// Gets or sets a value indicating whether client supports omitting document text in textDocument/didOpen notifications. /// - [DataMember(Name = "_vs_supportsNotIncludingTextInTextDocumentDidOpen")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsNotIncludingTextInTextDocumentDidOpen")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsNotIncludingTextInTextDocumentDidOpen { get; @@ -51,8 +49,8 @@ public bool SupportsNotIncludingTextInTextDocumentDidOpen /// Gets or sets a value indicating whether the client supports string based response kinds /// instead of enum based response kinds. /// - [DataMember(Name = "_vs_supportsIconExtensions")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsIconExtensions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsIconExtensions { get; @@ -62,8 +60,8 @@ public bool SupportsIconExtensions /// /// Gets or sets a value indicating whether the client provides support for diagnostic pull requests. /// - [DataMember(Name = "_vs_supportsDiagnosticRequests")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsDiagnosticRequests")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsDiagnosticRequests { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs index eb25ceed3c90f..4fedc2a05ee70 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalClipboardContent.cs @@ -4,18 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents content to be sent to the clipboard. /// - [DataContract] internal class VSInternalClipboardContent { /// /// Gets or sets a string that describes clipboard format types, for example, "text/plain". /// - [DataMember(Name = "_vs_mime", IsRequired = true)] + [JsonPropertyName("_vs_mime")] + [JsonRequired] public string MimeType { get; @@ -25,7 +25,8 @@ public string MimeType /// /// Gets or sets the content of the clipboard. /// - [DataMember(Name = "_vs_content", IsRequired = true)] + [JsonPropertyName("_vs_content")] + [JsonRequired] public string Content { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs index 2daba780549e6..5b44a7388b215 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeAction.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class used to extend to add the data field for codeAction/_ms_resolve support. /// - [DataContract] internal class VSInternalCodeAction : CodeAction { /// /// Gets or sets the group this CodeAction belongs to. /// - [DataMember(Name = "_vs_group")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_group")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Group { get; @@ -28,8 +26,8 @@ public string? Group /// /// Gets or sets the priority level of the code action. /// - [DataMember(Name = "_vs_priority")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_priority")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalPriorityLevel? Priority { get; @@ -39,8 +37,8 @@ public VSInternalPriorityLevel? Priority /// /// Gets or sets the range of the span this action is applicable to. /// - [DataMember(Name = "_vs_applicableRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_applicableRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Range? ApplicableRange { get; @@ -50,8 +48,8 @@ public Range? ApplicableRange /// /// Gets or sets the children of this action. /// - [DataMember(Name = "_vs_children")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_children")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalCodeAction[]? Children { get; @@ -61,8 +59,8 @@ public VSInternalCodeAction[]? Children /// /// Gets or sets the telemetry id of this action. /// - [DataMember(Name = "_vs_telemetryId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_telemetryId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Guid? TelemetryId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs index 93a48ed25cbc6..a6384488e787f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent from the client to the server for the textDocument/codeAction request. /// - [DataContract] internal class VSInternalCodeActionContext : CodeActionContext { /// /// Gets or sets the range of the current selection in the document for which the command was invoked. /// If there is no selection this would be a Zero-length range for the caret position. /// - [DataMember(Name = "_vs_selectionRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_selectionRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Range? SelectionRange { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs index 8bd608dbd27fe..87cdca79cd594 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionGroupSetting.cs @@ -4,18 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class containing the set of code action default groups that are supported. /// - [DataContract] internal class VSInternalCodeActionGroupSetting { /// /// Gets or sets the code actions default group names the client supports. /// - [DataMember(Name = "_vs_valueSet")] + [JsonPropertyName("_vs_valueSet")] public string[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs index 58da67255fd31..86e2389eefff3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCodeActionLiteralSetting.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing support for code action literals. /// - [DataContract] internal class VSInternalCodeActionLiteralSetting : CodeActionLiteralSetting { /// /// Gets or sets a value indicating what code action default groups are supported. /// - [DataMember(Name = "_vs_codeActionGroup")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_codeActionGroup")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalCodeActionGroupSetting? CodeActionGroup { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs index 9e9b8ab8cebf2..1333ec15adfb5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCommitCharacter.cs @@ -4,24 +4,23 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Extension class for CompletionItem with fields specific to Visual Studio functionalities. /// - [DataContract] internal class VSInternalCommitCharacter { /// /// Gets or sets the commit character. /// - [DataMember(Name = "_vs_character")] + [JsonPropertyName("_vs_character")] public string Character { get; set; } /// /// Gets or sets a value indicating whether the commit character should be inserted or not. /// - [DataMember(Name = "_vs_insert")] + [JsonPropertyName("_vs_insert")] public bool Insert { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs index fc0b92a2fe216..3bcc6ea57ea39 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionContext.cs @@ -6,23 +6,21 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for with properties specific to Visual Studio. /// - [DataContract] internal class VSInternalCompletionContext : CompletionContext { /// /// Gets or sets the indicating how the completion was triggered. /// - [DataMember(Name = "_vs_invokeKind")] + [JsonPropertyName("_vs_invokeKind")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] [DefaultValue(VSInternalCompletionInvokeKind.Explicit)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public VSInternalCompletionInvokeKind InvokeKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs index a8bd492e3ebd2..9bd3560beb600 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionInvokeKind.cs @@ -4,13 +4,10 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Provides value for which specifies /// how completion was invoked. /// - [DataContract] internal enum VSInternalCompletionInvokeKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs index 7edcd8403e1ef..392f1a00bfac6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionItem.cs @@ -4,14 +4,12 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for CompletionItem with fields specific to Visual Studio functionalities. /// - [DataContract] internal class VSInternalCompletionItem : CompletionItem { internal const string IconSerializedName = "_vs_icon"; @@ -22,17 +20,17 @@ internal class VSInternalCompletionItem : CompletionItem /// /// Gets or sets the icon to show for the completion item. In VS, this is more extensive than the completion kind. /// - [DataMember(Name = IconSerializedName)] + [JsonPropertyName(IconSerializedName)] [JsonConverter(typeof(ImageElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ImageElement? Icon { get; set; } /// /// Gets or sets the description for a completion item. /// - [DataMember(Name = DescriptionSerializedName)] + [JsonPropertyName(DescriptionSerializedName)] [JsonConverter(typeof(ClassifiedTextElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ClassifiedTextElement? Description { get; set; } /// @@ -41,16 +39,16 @@ internal class VSInternalCompletionItem : CompletionItem /// If present, client will use this value instead of . /// If absent, client will default to . /// - [DataMember(Name = VsCommitCharactersSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(VsCommitCharactersSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? VsCommitCharacters { get; set; } /// /// Gets or sets a value indicating whether the client should call to /// get the value of the text edit to commit. /// - [DataMember(Name = VsResolveTextEditOnCommitName)] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName(VsResolveTextEditOnCommitName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool VsResolveTextEditOnCommit { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs index e6984d8584a40..a9063b038b456 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs @@ -4,13 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A subclass of the LSP protocol that contains extensions specific to Visual Studio. /// - [DataContract] internal class VSInternalCompletionList : CompletionList { internal const string SuggestionModeSerializedName = "_vs_suggestionMode"; @@ -21,8 +19,8 @@ internal class VSInternalCompletionList : CompletionList /// /// Gets or sets a value indicating whether the completion list should use suggestion mode. In suggestion mode items are "soft-selected" by default. /// - [DataMember(Name = SuggestionModeSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(SuggestionModeSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool SuggestionMode { get; @@ -32,8 +30,8 @@ public bool SuggestionMode /// /// Gets or sets the continue characters for the completion list. /// - [DataMember(Name = ContinueCharactersSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(ContinueCharactersSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType[]? ContinueCharacters { get; @@ -43,8 +41,8 @@ public SumType /// Gets or sets the default used for completion items. /// - [DataMember(Name = DataSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(DataSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Data { get; @@ -57,8 +55,8 @@ public object? Data /// /// If set, overrides . /// - [DataMember(Name = CommitCharactersSerializedName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(CommitCharactersSerializedName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? CommitCharacters { get; set; } // NOTE: Any changes that are added to this file may need to be reflected in its "optimized" counterparts JsonConverter (OptomizedVSCompletionListJsonConverter). diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs index 4f613cd4e478a..413695d0c9f74 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for completion list. /// - [DataContract] internal class VSInternalCompletionListSetting { /// /// Gets or sets a value indicating whether completion lists can have Data bags. These data bags get propagated /// onto underlying completion items unless they have their own data bags. /// - [DataMember(Name = "_vs_data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool Data { get; @@ -29,8 +27,8 @@ public bool Data /// Gets or sets a value indicating whether completion lists can have VSCommitCharacters. These commit characters get propagated /// onto underlying valid completion items unless they have their own commit characters. /// - [DataMember(Name = "_vs_commitCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_commitCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public bool CommitCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs index 41cd0d069d8cb..9496e3f1eecd2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionSetting.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for VS completion. /// - [DataContract] internal class VSInternalCompletionSetting : CompletionSetting { /// /// Gets or sets completion list setting. /// - [DataMember(Name = "_vs_completionList")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_completionList")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalCompletionListSetting? CompletionList { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs index dbfacba1f2cf3..e59831f19eeae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterClass.cs @@ -4,27 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a unicode character class for completion continuation. /// - [DataContract] internal class VSInternalContinueCharacterClass { /// /// Gets the type value. /// - [DataMember(Name = "_vs_type")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_type")] + [JsonRequired] public const string Type = "unicodeClass"; /// /// Gets or sets the unicode class. /// - [DataMember(Name = "_vs_unicodeClass")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_unicodeClass")] + [JsonRequired] public string UnicodeClass { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs index 46bc21e6ba34f..d464790256715 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterRange.cs @@ -4,34 +4,32 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing range of characters for completion continuation. /// - [DataContract] internal class VSInternalContinueCharacterRange { /// /// Gets the type value. /// - [DataMember(Name = "_vs_type")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_type")] + [JsonRequired] public const string Type = "charRange"; /// /// Gets or sets the first completion character of the range. /// - [DataMember(Name = "_vs_start")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_start")] + [JsonRequired] public string Start { get; set; } /// /// Gets or sets the last completion character of the range. /// - [DataMember(Name = "_vs_end")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_end")] + [JsonRequired] public string End { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs index 233e1e82129e2..cb2af1a924f5d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalContinueCharacterSingle.cs @@ -4,27 +4,25 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing single continue character for completion. /// - [DataContract] internal class VSInternalContinueCharacterSingle { /// /// Gets the type value. /// - [DataMember(Name = "_vs_type")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_type")] + [JsonRequired] public const string Type = "singleChar"; /// /// Gets or sets the completion character. /// - [DataMember(Name = "_vs_char")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_char")] + [JsonRequired] public string Character { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs index 785e96b9decad..378ff67e08581 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertOptions.cs @@ -4,18 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the options for on auto insert. /// - [DataContract] internal class VSInternalDocumentOnAutoInsertOptions { /// /// Gets or sets trigger characters for on auto insert. /// - [DataMember(Name = "_vs_triggerCharacters")] + [JsonPropertyName("_vs_triggerCharacters")] public string[] TriggerCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs index 4fe5d9c1897a7..0000038032286 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertParams.cs @@ -4,18 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/_ms_onAutoInsert request. /// - [DataContract] internal class VSInternalDocumentOnAutoInsertParams : ITextDocumentPositionParams { /// /// Gets or sets the representing the document to format. /// - [DataMember(Name = "_vs_textDocument")] + [JsonPropertyName("_vs_textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -25,7 +24,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the at which the request was sent. /// - [DataMember(Name = "_vs_position")] + [JsonPropertyName("_vs_position")] public Position Position { get; @@ -35,7 +34,7 @@ public Position Position /// /// Gets or sets the character that was typed. /// - [DataMember(Name = "_vs_ch")] + [JsonPropertyName("_vs_ch")] public string Character { get; @@ -45,7 +44,7 @@ public string Character /// /// Gets or sets the for the request. /// - [DataMember(Name = "_vs_options")] + [JsonPropertyName("_vs_options")] public FormattingOptions Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs index d9b52602383c3..f196732392f86 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentOnAutoInsertResponseItem.cs @@ -6,20 +6,18 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response of an AutoInsert response. /// - [DataContract] internal class VSInternalDocumentOnAutoInsertResponseItem { /// /// Gets or sets the insert text format of the primary text edit. for supported formats. /// - [DataMember(Name = "_vs_textEditFormat")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_textEditFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [DefaultValue(InsertTextFormat.Plaintext)] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] @@ -32,8 +30,8 @@ public InsertTextFormat TextEditFormat /// /// Gets or sets the text edit. /// - [DataMember(Name = "_vs_textEdit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_textEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextEdit TextEdit { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs index 2bee314d31fa6..e3634b57d5e98 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalDocumentSpellCheckableParams.cs @@ -5,22 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Collections.Generic; - using System.Runtime.Serialization; - using System.Text; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameter for tD/_vs_spellCheckableRanges. /// - [DataContract] internal class VSInternalDocumentSpellCheckableParams : VSInternalStreamingParams, IPartialResultParams { /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs index ba2abd3e80a02..168d365a49115 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalExecuteCommandClientCapabilities.cs @@ -4,12 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing settings for well-known Visual Studio's code action command. /// - [DataContract] internal class VSInternalExecuteCommandClientCapabilities : DynamicRegistrationSetting { /// @@ -31,7 +30,7 @@ public VSInternalExecuteCommandClientCapabilities(bool value) /// /// Gets or sets a set of well-known commands name the given VS-LSP client supports. /// - [DataMember(Name = "_vs_supportedCommands")] + [JsonPropertyName("_vs_supportedCommands")] public string[] SupportedCommands { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs index de87ad88193bc..2dcd51847ce4d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension to Hover which adds additional data for colorization. @@ -18,9 +17,9 @@ internal class VSInternalHover : Hover /// of objects from the Microsoft.VisualStudio.Text.Adornments namespace, /// such as ContainerElements, ClassifiedTextElements and ClassifiedTextRuns. /// - [DataMember(Name = "_vs_rawContent")] + [JsonPropertyName("_vs_rawContent")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? RawContent { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs index aaf0fe7a8517a..a46ad067f020a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalIconMapping.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Response class when asking server to resolve the rendering information of a string kind. /// - [DataContract] internal class VSInternalIconMapping : IEquatable { /// /// Gets or sets the ImageElements for a certain kind. /// - [DataMember(Name = "_vs_images")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_images")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSImageId[]? Images { get; @@ -28,8 +26,8 @@ public VSImageId[]? Images /// /// Gets or sets the tags for a certain kind. To be used in the absence of ImageIds. /// - [DataMember(Name = "_vs_tags")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_tags")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? Tags { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs index a3ab7943d4821..0c40cfcfc3b28 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs @@ -4,9 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Context for inline completion request. @@ -18,8 +16,8 @@ internal class VSInternalInlineCompletionContext /// /// Gets or sets how completion was triggered. /// - [DataMember(Name = "_vs_triggerKind")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_triggerKind")] + [JsonRequired] public VSInternalInlineCompletionTriggerKind TriggerKind { get; set; } = VSInternalInlineCompletionTriggerKind.Explicit; /// @@ -27,8 +25,8 @@ internal class VSInternalInlineCompletionContext /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L45. /// - [DataMember(Name = "_vs_selectedCompletionInfo")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_selectedCompletionInfo")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public VSInternalSelectedCompletionInfo? SelectedCompletionInfo { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs index 740c6799c26d9..1c2520f3a6b4b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs @@ -4,9 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// A single inline completion item response. @@ -19,8 +17,8 @@ internal class VSInternalInlineCompletionItem /// /// Gets or sets the text to replace the range with. /// - [DataMember(Name = "_vs_text")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_text")] + [JsonRequired] public string Text { get; set; } /// @@ -28,22 +26,22 @@ internal class VSInternalInlineCompletionItem /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L94. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public Range? Range { get; set; } /// /// Gets or sets the command that is executed after inserting this completion item. /// - [DataMember(Name = "_vs_command")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_command")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public Command? Command { get; set; } /// /// Gets or sets the format of the insert text. /// - [DataMember(Name = "_vs_insertTextFormat")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_insertTextFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public InsertTextFormat? TextFormat { get; set; } = InsertTextFormat.Plaintext; } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs index 57c7957df661b..8fc7113870e2a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Response for an inline completions request. @@ -18,8 +17,8 @@ internal class VSInternalInlineCompletionList /// /// Gets or sets the inline completion items. /// - [DataMember(Name = "_vs_items")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_items")] + [JsonRequired] public VSInternalInlineCompletionItem[] Items { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs index b73012d5900e8..113e1d17b6fdf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionOptions.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; using System.Text.RegularExpressions; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// The options for inline completion. /// - [DataContract] internal class VSInternalInlineCompletionOptions { /// /// Gets or sets a regex used by the client to determine when to ask the server for snippets. /// - [DataMember(Name = "_vs_pattern")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_pattern")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] [JsonConverter(typeof(RegexConverter))] public Regex Pattern { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs index a57c4f294d4db..3a7e067efbb49 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// The request data for an inline completions request. @@ -18,28 +17,28 @@ internal class VSInternalInlineCompletionRequest : ITextDocumentParams /// /// Gets or sets the text document. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets the position where inline completions are being requested. /// - [DataMember(Name = "_vs_position")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_position")] + [JsonRequired] public Position Position { get; set; } /// /// Gets or sets the context for the inline completions request. /// - [DataMember(Name = "_vs_context")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_context")] + [JsonRequired] public VSInternalInlineCompletionContext Context { get; set; } /// /// Gets or sets the for the request. /// - [DataMember(Name = "_vs_options")] + [JsonPropertyName("_vs_options")] public FormattingOptions Options { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs index 476a038b93d7f..0a0f4335811de 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionTriggerKind.cs @@ -4,13 +4,10 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// How the inline completion request was triggered. /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L58. /// - [DataContract] internal enum VSInternalInlineCompletionTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs index 6167f1ff419e3..9ad6c9c4e99ad 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalKindAndModifier.cs @@ -5,19 +5,17 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class that contains the base kind and modifiers used to describe a response item. /// - [DataContract] internal class VSInternalKindAndModifier : IEquatable { /// /// Gets or sets the ImageIds for a certain kind. /// - [DataMember(Name = "_vs_kind")] + [JsonPropertyName("_vs_kind")] public string Kind { get; @@ -27,8 +25,8 @@ public string Kind /// /// Gets or sets the modifier of the kind. /// - [DataMember(Name = "_vs_modifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_modifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? Modifier { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs index 4c62c6fb74ec5..915dc06853f81 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs @@ -5,14 +5,12 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for . Used to relay reference text information with colorization. /// - [DataContract] internal class VSInternalLocation : VSLocation { private object? textValue = null; @@ -20,9 +18,9 @@ internal class VSInternalLocation : VSLocation /// /// Gets or sets the text value for a location reference. Must be of type or or or . /// - [DataMember(Name = "_vs_text")] + [JsonPropertyName("_vs_text")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Text { get diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs index 26394166fef4a..ab32d5981107d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeMapping.cs @@ -4,17 +4,15 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; - [DataContract] internal class VSInternalMapCodeMapping { /// /// Gets or sets identifier for the document the contents are supposed to be mapped into. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_textDocument")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TextDocumentIdentifier? TextDocument { get; @@ -24,7 +22,7 @@ public TextDocumentIdentifier? TextDocument /// /// Gets or sets strings of code/text to map into TextDocument. /// - [DataMember(Name = "_vs_contents")] + [JsonPropertyName("_vs_contents")] public string[] Contents { get; @@ -36,8 +34,8 @@ public string[] Contents /// related classes (in other documents), viewport, etc. Earlier items should be considered /// higher priority. /// - [DataMember(Name = "_vs_focusLocations")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_focusLocations")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Location[][]? FocusLocations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs index 6932c4d998bb2..e1460443c6612 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalMapCodeParams.cs @@ -4,19 +4,17 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// LSP Params for textDocument/mapCode calls. /// - [DataContract] internal class VSInternalMapCodeParams { /// /// Set of code blocks, associated with documents and regions, to map. /// - [DataMember(Name = "_vs_mappings")] + [JsonPropertyName("_vs_mappings")] public VSInternalMapCodeMapping[] Mappings { get; @@ -27,8 +25,8 @@ public VSInternalMapCodeMapping[] Mappings /// Changes that should be applied to the workspace by the mapper before performing /// the mapping operation. /// - [DataMember(Name = "_vs_updates")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_updates")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceEdit? Updates { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs index 4334c1b2aff26..647eb28637172 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalProjectContext.cs @@ -5,20 +5,18 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class for a project context. /// - [DataContract] internal class VSInternalProjectContext : VSProjectContext, IEquatable { /// /// Gets or sets the string context kind of the project context. /// - [DataMember(Name = "_vs_vsKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_vsKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalKindAndModifier? VSKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs index cf551ec93172e..add3d0afb01b7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs @@ -5,14 +5,12 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents references information. /// - [DataContract] internal class VSInternalReferenceItem { private object? definitionTextValue = null; @@ -21,7 +19,8 @@ internal class VSInternalReferenceItem /// /// Gets or sets the reference id. /// - [DataMember(Name = "_vs_id", IsRequired = true)] + [JsonPropertyName("_vs_id")] + [JsonRequired] public int Id { get; @@ -31,7 +30,7 @@ public int Id /// /// Gets or sets the reference location. /// - [DataMember(Name = "_vs_location")] + [JsonPropertyName("_vs_location")] public Location Location { get; @@ -41,8 +40,8 @@ public Location Location /// /// Gets or sets the definition Id. /// - [DataMember(Name = "_vs_definitionId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_definitionId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? DefinitionId { get; @@ -57,9 +56,9 @@ public int? DefinitionId /// This element should colorize syntax, but should not contain highlighting, e.g. /// embedded within should not define . /// - [DataMember(Name = "_vs_definitionText")] + [JsonPropertyName("_vs_definitionText")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? DefinitionText { get @@ -84,7 +83,7 @@ public object? DefinitionText /// /// Gets or sets the resolution status. /// - [DataMember(Name = "_vs_resolutionStatus")] + [JsonPropertyName("_vs_resolutionStatus")] public VSInternalResolutionStatusKind ResolutionStatus { get; @@ -94,7 +93,7 @@ public VSInternalResolutionStatusKind ResolutionStatus /// /// Gets or sets the reference kind. /// - [DataMember(Name = "_vs_kind")] + [JsonPropertyName("_vs_kind")] public VSInternalReferenceKind[] Kind { get; @@ -104,29 +103,29 @@ public VSInternalReferenceKind[] Kind /// /// Gets or sets the document name to be displayed to user when needed.This can be used in cases where URI doesn't have a user friendly file name or it is a remote URI. /// - [DataMember(Name = "_vs_documentName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_documentName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DocumentName { get; set; } /// /// Gets or sets the project name. /// - [DataMember(Name = "_vs_projectName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_projectName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ProjectName { get; set; } /// /// Gets or sets the containing type. /// - [DataMember(Name = "_vs_containingType")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_containingType")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContainingType { get; set; } /// /// Gets or sets the containing member. /// - [DataMember(Name = "_vs_containingMember")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_containingMember")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContainingMember { get; set; } /// @@ -146,9 +145,9 @@ public VSInternalReferenceKind[] Kind /// "MarkerFormatDefinition/HighlightedDefinition" for definitions. /// /// - [DataMember(Name = "_vs_text")] + [JsonPropertyName("_vs_text")] [JsonConverter(typeof(ObjectContentConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Text { get @@ -173,23 +172,23 @@ public object? Text /// Gets or sets the text value for display path.This would be a friendly display name for scenarios where the actual path on disk may be confusing for users. /// This doesn't have to correspond to a real file path, but does need to be parsable by the various Path.GetFileName() methods. /// - [DataMember(Name = "_vs_displayPath")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_displayPath")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? DisplayPath { get; set; } /// /// Gets or sets the origin of the item.The origin is used to filter remote results. /// - [DataMember(Name = "_vs_origin")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_origin")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalItemOrigin? Origin { get; set; } /// /// Gets or sets the icon to show for the definition header. /// - [DataMember(Name = "_vs_definitionIcon")] + [JsonPropertyName("_vs_definitionIcon")] [JsonConverter(typeof(ImageElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ImageElement? DefinitionIcon { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs index b511340956f5c..28c2d6c76a120 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceKind.cs @@ -4,8 +4,6 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Diagnostics.CodeAnalysis; - /// /// Enum which represents the various reference kinds. /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs index ef0a031bbb6dd..b589fd7e9b218 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents extensions of passed as parameter of find reference requests. @@ -16,8 +15,8 @@ internal class VSInternalReferenceParams : ReferenceParams /// /// Gets or sets a value indicating the scope of returned items. /// - [DataMember(Name = "_vs_scope")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_scope")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalItemOrigin? Scope { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs index 4fe711ae387b5..6c7eeee8638bc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSelection.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the user configuration (as defined in ) for a rename request. /// - [DataContract] internal class VSInternalRenameOptionSelection { /// /// Gets or sets the name that identifies the option. /// - [DataMember(Name = "_vs_name")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_name")] + [JsonRequired] public string Name { get; @@ -27,8 +25,8 @@ public string Name /// /// Gets or sets a value indicating whether the user selected the option. /// - [DataMember(Name = "_vs_value")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_value")] + [JsonRequired] public bool Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs index 4614f862c3eb2..23e893e123907 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameOptionSupport.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a renaming option for customizing the edit in the 'textDocument/rename' request. /// - [DataContract] internal class VSInternalRenameOptionSupport { /// /// Gets or sets the name that identifies the option. /// - [DataMember(Name = "_vs_name")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_name")] + [JsonRequired] public string Name { get; @@ -27,8 +25,8 @@ public string Name /// /// Gets or sets the user-facing option label. /// - [DataMember(Name = "_vs_label")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_label")] + [JsonRequired] public string Label { get; @@ -38,8 +36,8 @@ public string Label /// /// Gets or sets a value indicating whether the option has a default value of true. /// - [DataMember(Name = "_vs_default")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_default")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Default { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs index 969228507d1b8..2281272ecd8b8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameParams.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters (together with extra VS-specific options) sent for the /// 'textDocument/rename' request. /// - [DataContract] internal class VSInternalRenameParams : RenameParams { /// /// Gets or sets the rename option values as selected by the user. /// - [DataMember(Name = "_vs_optionSelections")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_optionSelections")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalRenameOptionSelection[]? OptionSelections { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs index 092944a6dadae..4f144aaa2c8bd 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalRenameRange.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a possible result value of the 'textDocument/prepareRename' request, /// together with extra VS-specific options. /// - [DataContract] internal class VSInternalRenameRange : RenameRange { /// /// Gets or sets the supported options for the rename request. /// - [DataMember(Name = "_vs_supportedOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_supportedOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalRenameOptionSupport[]? SupportedOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs index 963cc264e0acf..85c150b74778f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Information about the selected completion item for . @@ -18,29 +17,29 @@ internal class VSInternalSelectedCompletionInfo /// /// Gets or sets the range of the selected completion item. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public Range Range { get; set; } /// /// Gets or sets the text of the selected completion item. /// - [DataMember(Name = "_vs_text")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_text")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public string Text { get; set; } /// /// Gets or sets the completion item kind of the selected completion item. /// - [DataMember(Name = "_vs_completionKind")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_completionKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public CompletionItemKind CompletionKind { get; set; } /// /// Gets or sets a value indicating whether the completion item is a snippet. /// - [DataMember(Name = "_vs_isSnippetText")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_isSnippetText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IsSnippetText { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs index b8758f0f60161..b0c441bcd1a93 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalServerCapabilities.cs @@ -4,15 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for ServerCapabilities with fields specific to Visual Studio. /// - [DataContract] internal class VSInternalServerCapabilities : VSServerCapabilities { /// @@ -24,8 +20,8 @@ internal class VSInternalServerCapabilities : VSServerCapabilities /// This is provided to facilitate transition from in-proc to OOP for teams that /// currently own both a Language Server for Ctrl+Q and a GoTo provider. /// - [DataMember(Name = "_vs_disableGoToWorkspaceSymbols")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_disableGoToWorkspaceSymbols")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DisableGoToWorkspaceSymbols { get; @@ -35,8 +31,8 @@ public bool DisableGoToWorkspaceSymbols /// /// Gets or sets a value indicating whether document/_ms_references is supported. /// - [DataMember(Name = "_vs_ReferencesProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_ReferencesProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MSReferencesProvider { get; @@ -46,8 +42,8 @@ public bool MSReferencesProvider /// /// Gets or sets a value indicating whether the server supports OnAutoInsert. /// - [DataMember(Name = "_vs_onAutoInsertProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_onAutoInsertProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDocumentOnAutoInsertOptions? OnAutoInsertProvider { get; @@ -58,8 +54,8 @@ public VSInternalDocumentOnAutoInsertOptions? OnAutoInsertProvider /// Gets or sets a value indicating whether the server requires document text to be included in textDocument/didOpen notifications. /// /// This capability is not intended to be included into the official LSP, hence _ms_ prefix. - [DataMember(Name = "_vs_doNotIncludeTextInTextDocumentDidOpen")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_doNotIncludeTextInTextDocumentDidOpen")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DoNotIncludeTextInTextDocumentDidOpen { get; @@ -69,8 +65,8 @@ public bool DoNotIncludeTextInTextDocumentDidOpen /// /// Gets or sets a value indicating whether the server provides support to resolve string based response kinds. /// - [DataMember(Name = "_vs_KindDescriptionResolveProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_KindDescriptionResolveProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool KindDescriptionResolveProvider { get; @@ -80,8 +76,8 @@ public bool KindDescriptionResolveProvider /// /// Gets or sets a value indicating whether the server provides support for diagnostic pull requests. /// - [DataMember(Name = "_vs_supportsDiagnosticRequests")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_supportsDiagnosticRequests")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SupportsDiagnosticRequests { get; @@ -91,8 +87,8 @@ public bool SupportsDiagnosticRequests /// /// Gets or sets server specified options for diagnostic pull requests. /// - [DataMember(Name = "_vs_diagnosticProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_diagnosticProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalDiagnosticOptions? DiagnosticProvider { get; @@ -102,8 +98,8 @@ public VSInternalDiagnosticOptions? DiagnosticProvider /// /// Gets or sets a value indicating whether the server provides support for inline completion requests. /// - [DataMember(Name = "_vs_inlineCompletionOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_inlineCompletionOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalInlineCompletionOptions? InlineCompletionOptions { get; @@ -113,8 +109,8 @@ public VSInternalInlineCompletionOptions? InlineCompletionOptions /// /// Gets or sets a value indicating whether the server provides support for spell checking. /// - [DataMember(Name = "_vs_spellCheckingProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_spellCheckingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SpellCheckingProvider { get; @@ -124,8 +120,8 @@ public bool SpellCheckingProvider /// /// Gets or sets a value indicating whether the server supports validating breakable ranges. /// - [DataMember(Name = "_vs_breakableRangeProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_breakableRangeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool BreakableRangeProvider { get; @@ -135,8 +131,8 @@ public bool BreakableRangeProvider /// /// Gets or sets a value indicating whether the server supports uri presentation. /// - [DataMember(Name = "_vs_uriPresentationProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_uriPresentationProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool UriPresentationProvider { get; @@ -146,8 +142,8 @@ public bool UriPresentationProvider /// /// Gets or sets a value indicating whether the server supports text presentation. /// - [DataMember(Name = "_vs_textPresentationProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_textPresentationProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool TextPresentationProvider { get; @@ -157,8 +153,8 @@ public bool TextPresentationProvider /// /// Gets or sets the value which indicates what support the server has for code mapping. /// - [DataMember(Name = "_vs_mapCodeProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("_vs_mapCodeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MapCodeProvider { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs index 74ca607565159..375585cc5c934 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSignatureInformation.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; using Roslyn.Text.Adornments; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for signature help information which contains colorized label information. /// - [DataContract] internal class VSInternalSignatureInformation : SignatureInformation { /// /// Gets or sets the value representing the colorized label. /// - [DataMember(Name = "_vs_colorizedLabel")] + [JsonPropertyName("_vs_colorizedLabel")] [JsonConverter(typeof(ClassifiedTextElementConverter))] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ClassifiedTextElement? ColorizedLabel { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs index ca3264b7e8c30..c0acf2fc98f8b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSpellCheckableRangeReport.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Report of spell checkable ranges. /// - [DataContract] internal class VSInternalSpellCheckableRangeReport { /// @@ -21,8 +18,8 @@ internal class VSInternalSpellCheckableRangeReport /// spell checkable ranges. The server can use this result ID to avoid resending /// spell checkable ranges that had previously been sent. /// - [DataMember(Name = "_vs_resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// @@ -56,8 +53,8 @@ internal class VSInternalSpellCheckableRangeReport /// 5 // Span length /// ] /// - [DataMember(Name = "_vs_ranges")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_ranges")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int[]? Ranges { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs index 3b0e082213ebe..173f983b6b293 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalStreamingParams.cs @@ -4,21 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a streaming pull request parameter used. /// /// TODO: Deprecate VSInternalDiagnosticParams.cs to use this merged param instead. /// - [DataContract] internal class VSInternalStreamingParams : ITextDocumentParams { /// /// Gets or sets the document for which the feature is being requested for. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; set; } /// @@ -40,8 +39,8 @@ internal class VSInternalStreamingParams : ITextDocumentParams /// document, then all reports are expected to have the same /// previousResultId. /// - [DataMember(Name = "_vs_previousResultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? PreviousResultId { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs index 7e241f9b91960..469c85e86b4a7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSymbolInformation.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Extension class for SymbolInformation with fields specific to Visual Studio functionalities. @@ -13,14 +12,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// This is a temporary protocol and should not be used. /// - [DataContract] internal class VSInternalSymbolInformation : VSSymbolInformation { /// /// Gets or sets the string kind used for icons. /// - [DataMember(Name = "_vs_vsKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_vsKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalKindAndModifier? VSKind { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs index abc8b2b3749ed..3a7d0af7a9725 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Text document capabilities specific to Visual Studio. @@ -16,8 +15,8 @@ internal class VSInternalTextDocumentClientCapabilities : TextDocumentClientCapa /// /// Gets or sets the setting which determines if on auto insert can be dynamically registered. /// - [DataMember(Name = "_vs_onAutoInsert")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_onAutoInsert")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? OnAutoInsert { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs index e73b246d01f2e..933950179ae0a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentRegistrationOptions.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// / Class representing the options for registering textDocument/_vs_OnAutoInsert support. /// - [DataContract] internal class VSInternalTextDocumentRegistrationOptions : TextDocumentRegistrationOptions { /// /// Gets or sets trigger characters for on auto insert. /// - [DataMember(Name = "_vs_triggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_triggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? TriggerCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs index 3ec280d525972..e2cd66192521e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextPresentationParams.cs @@ -4,20 +4,18 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/_vs_textPresentation request. /// - [DataContract] internal class VSInternalTextPresentationParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be operate on. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; @@ -27,8 +25,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_range")] + [JsonRequired] public Range Range { get; @@ -38,7 +36,7 @@ public Range Range /// /// Gets or sets the text. /// - [DataMember(Name = "_vs_text")] + [JsonPropertyName("_vs_text")] public string? Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs index 3736b48506e1e..99fa2ac931edf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalUriPresentationParams.cs @@ -5,20 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for a textDocument/_vs_uriPresentation request. /// - [DataContract] internal class VSInternalUriPresentationParams : ITextDocumentParams { /// /// Gets or sets the identifier for the text document to be operate on. /// - [DataMember(Name = "_vs_textDocument")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; @@ -28,8 +27,8 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the range. /// - [DataMember(Name = "_vs_range")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("_vs_range")] + [JsonRequired] public Range Range { get; @@ -39,8 +38,7 @@ public Range Range /// /// Gets or sets the URI values. Valid for DropKind.Uris. /// - [DataMember(Name = "_vs_uris")] - [JsonProperty(ItemConverterType = typeof(DocumentUriConverter))] + [JsonPropertyName("_vs_uris")] public Uri[]? Uris { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs index f9a4464b2e282..e216a5b4aed8f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalValidateBreakableRangeParams.cs @@ -4,24 +4,23 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for the textDocument/validateBreakableRange request. /// - [DataContract] internal class VSInternalValidateBreakableRangeParams : ITextDocumentParams { /// /// Gets or sets the for the request. /// - [DataMember(Name = "_vs_textDocument")] + [JsonPropertyName("_vs_textDocument")] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets the at which the request was sent. /// - [DataMember(Name = "_vs_range")] + [JsonPropertyName("_vs_range")] public Range Range { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs index 516eb1c8218ea..417638b7bd55d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableParams.cs @@ -5,29 +5,25 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Collections.Generic; - using System.Runtime.Serialization; - using System.Text; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameter for workspace/_vs_spellCheckableRanges. /// - [DataContract] internal class VSInternalWorkspaceSpellCheckableParams : IPartialResultParams { /// /// Gets or sets the current state of the documents the client already has received. /// - [DataMember(Name = "_vs_previousResults")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_previousResults")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public VSInternalStreamingParams[]? PreviousResults { get; set; } /// /// Gets or sets an optional token that a server can use to report partial results (e.g. streaming) to the client. /// - [DataMember(Name = "_vs_partialResultToken")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("_vs_partialResultToken")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs index a844892b092b1..5bfce67da8ca2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalWorkspaceSpellCheckableReport.cs @@ -2,23 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text.Json.Serialization; + namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Collections.Generic; - using System.Runtime.Serialization; - using System.Text; - /// /// Report for workspace spell checkable range request. /// - [DataContract] internal class VSInternalWorkspaceSpellCheckableReport : VSInternalSpellCheckableRangeReport, ITextDocumentParams { /// /// Gets or sets the document for which the spell checkable ranges are returned. /// - [DataMember(Name = "_vs_textDocument", IsRequired = true)] + [JsonPropertyName("_vs_textDocument")] + [JsonRequired] public TextDocumentIdentifier TextDocument { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs index 4ad14537b3856..16d07390197b3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents linked editing range capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LinkedEditingRangeOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs index d24bfd158ddd3..78cbad670a95f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRangeParams.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Class representing the parameters sent for a textDocument/linkedEditingRange request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LinkedEditingRangeParams : TextDocumentPositionParams { } diff --git a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs index 9c0b78fd5c768..a60978b1e0cb9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LinkedEditingRanges.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the response of an LinkedEditingRanges response. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LinkedEditingRanges { /// /// Gets or sets the ranges for the type rename. /// - [DataMember(Name = "ranges")] + [JsonPropertyName("ranges")] public Range[] Ranges { get; @@ -28,8 +26,8 @@ public Range[] Ranges /// /// Gets or sets the word pattern for the type rename. /// - [DataMember(Name = "wordPattern")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("wordPattern")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? WordPattern { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Location.cs b/src/Features/LanguageServer/Protocol/Protocol/Location.cs index e91082e4441dd..123dfea04440a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Location.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Location.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a location in a document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Location : IEquatable { /// /// Gets or sets the URI for the document the location belongs to. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -31,7 +29,7 @@ public Uri Uri /// /// Gets or sets the range of the location in the document. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs b/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs index 8d80b156e99a5..6918677cc3014 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/LogMessageParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents parameter sent with window/logMessage requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class LogMessageParams { /// /// Gets or sets the type of message. /// - [DataMember(Name = "type")] + [JsonPropertyName("type")] public MessageType MessageType { get; @@ -27,7 +26,7 @@ public MessageType MessageType /// /// Gets or sets the message. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs b/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs index 62c6b0d8a16d1..3ca516d0589ab 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MarkedString.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing human readable text that should be rendered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class MarkedString { /// /// Gets or sets the language of the code stored in . /// - [DataMember(Name = "language")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("language")] + [JsonRequired] public string Language { get; @@ -29,8 +27,8 @@ public string Language /// /// Gets or sets the code. /// - [DataMember(Name = "value")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("value")] + [JsonRequired] public string Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs b/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs index ce92eda072808..de670049566fc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MarkupContent.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing text and an associated format that should be rendered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class MarkupContent { /// /// Gets or sets the representing the text's format. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public MarkupKind Kind { get; @@ -27,7 +26,7 @@ public MarkupKind Kind /// /// Gets or sets the text that should be rendered. /// - [DataMember(Name = "value")] + [JsonPropertyName("value")] public string Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs b/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs index 29dda1ba997c7..e4ec736ad7694 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MarkupKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the various formats of markup text. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct MarkupKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs index 4dc981f6f1827..8f4739241c9e2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MessageActionItem.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represent an action the user performs after a window/showMessageRequest request is sent. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class MessageActionItem { /// /// Gets or sets the title. /// - [DataMember(Name = "title")] + [JsonPropertyName("title")] public string Title { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs b/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs index 3b243d8a2f55f..b36f16e24c9d4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/MessageType.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Message type enum. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum MessageType { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs index d34f9a1010fd7..32004eb595da6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/OptionalVersionedTextDocumentIdentifier.cs @@ -6,22 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Globalization; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document, but optionally has a version identifier. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class OptionalVersionedTextDocumentIdentifier : TextDocumentIdentifier, IEquatable { /// /// Gets or sets the version of the document. /// - [DataMember(Name = "version")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("version")] public int? Version { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs index 38327189dcf73..e92aeb737af31 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformation.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a parameter of a callable signature. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(ParameterInformationConverter))] internal class ParameterInformation { /// /// Gets or sets the label of the parameter. /// - [DataMember(Name = "label")] + [JsonPropertyName("label")] public SumType> Label { get; @@ -30,8 +28,8 @@ public SumType> Label /// /// Gets or sets the human-readable documentation of the parameter. /// - [DataMember(Name = "documentation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Documentation { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs index debdaa4cdcbd0..a392f4102186d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ParameterInformationSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameter information initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ParameterInformationSetting { /// /// Gets or sets a value indicating whether the client supports label offset. /// - [DataMember(Name = "labelOffsetSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("labelOffsetSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool LabelOffsetSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Position.cs b/src/Features/LanguageServer/Protocol/Protocol/Position.cs index ec4a102e561be..a4a770efad361 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Position.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Position.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a position on a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Position : IEquatable { /// @@ -37,7 +35,7 @@ public Position(int line, int character) /// /// Gets or sets the line number. /// - [DataMember(Name = "line")] + [JsonPropertyName("line")] public int Line { get; @@ -47,7 +45,7 @@ public int Line /// /// Gets or sets the character number. /// - [DataMember(Name = "character")] + [JsonPropertyName("character")] public int Character { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs b/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs index 73184699a73db..2ef5c34b6eb05 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PrepareRenameParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the 'textDocument/prepare' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class PrepareRenameParams : ITextDocumentPositionParams { /// /// Gets or sets the value which identifies the document. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the position in which the rename is requested. /// - [DataMember(Name = "position")] + [JsonPropertyName("position")] public Position Position { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs b/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs index 90dfe145b7c39..fb4a1319a87e1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PrepareSupportDefaultBehavior.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum representing the default behavior used by the client for computing a rename range. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum PrepareSupportDefaultBehavior { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs b/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs index 59026f13ff01c..e8ae0c7e4e6bc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PreviousResultId.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a previous result id in a workspace pull request. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class PreviousResultId { /// /// Gets or sets the URI for which the client knows a result id. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the value of the previous result id. /// - [DataMember(Name = "value")] + [JsonPropertyName("value")] public string Value { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs index c8cdae892ad2e..5b759a01c9aa3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticParams.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that's sent with 'textDocument/publishDiagnostics' messages. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class PublishDiagnosticParams { /// /// Gets or sets the URI of the text document. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the collection of diagnostics. /// - [DataMember(Name = "diagnostics")] + [JsonPropertyName("diagnostics")] public Diagnostic[] Diagnostics { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs index 044cc92b08910..63f44c94aabbf 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/PublishDiagnosticsSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the initialization setting for publish diagnostics. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class PublishDiagnosticsSetting { /// /// Gets or sets a value indicating whether gets or sets the capabilities. /// - [DataMember(Name = "tagSupport")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("tagSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public TagSupport? TagSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Range.cs b/src/Features/LanguageServer/Protocol/Protocol/Range.cs index 47284808a2d33..a1e423608df98 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Range.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Range.cs @@ -6,22 +6,20 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document text range. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Range : IEquatable { /// /// Gets or sets the text start position. /// - [DataMember(Name = "start")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("start")] + [JsonRequired] public Position Start { get; @@ -31,8 +29,8 @@ public Position Start /// /// Gets or sets the text end position. /// - [DataMember(Name = "end")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("end")] + [JsonRequired] public Position End { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs b/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs index 36703524d53bd..2e7411b4411a0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ReferenceContext.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing reference context information for find reference request parameter. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ReferenceContext { /// /// Gets or sets a value indicating whether declaration should be included. /// - [DataMember(Name = "includeDeclaration")] + [JsonPropertyName("includeDeclaration")] public bool IncludeDeclaration { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs index a65a49c2b9046..2282473292523 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ReferenceOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ReferenceOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs index 0809067d7a9ef..00a2106a75ddc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ReferenceParams.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing find reference parameter for find reference request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ReferenceParams : TextDocumentPositionParams, IPartialResultParams { // Using IPartialResultParams instead of IPartialResultParams to @@ -22,7 +20,7 @@ internal class ReferenceParams : TextDocumentPositionParams, IPartialResultParam /// /// Gets or sets the reference context. /// - [DataMember(Name = "context")] + [JsonPropertyName("context")] public ReferenceContext Context { get; @@ -32,8 +30,8 @@ public ReferenceContext Context /// /// Gets or sets the value of the PartialResultToken instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Registration.cs b/src/Features/LanguageServer/Protocol/Protocol/Registration.cs index fcf186fb298ff..906b4f4277d8e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Registration.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Registration.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the general registration information for registering for a capability. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Registration { /// /// Gets or sets the id used to register the request. This can be used to deregister later. /// - [DataMember(Name = "id")] + [JsonPropertyName("id")] public string Id { get; @@ -28,7 +26,7 @@ public string Id /// /// Gets or sets the method / capability to register for. /// - [DataMember(Name = "method")] + [JsonPropertyName("method")] public string Method { get; @@ -38,8 +36,8 @@ public string Method /// /// Gets or sets the options necessary for registration. /// - [DataMember(Name = "registerOptions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("registerOptions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? RegisterOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs index 0a212c4740440..4125036ab0e1f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RegistrationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for the client/registerCapability request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RegistrationParams { /// /// Gets or sets the set of capabilities that are being registered. /// - [DataMember(Name = "registrations")] + [JsonPropertyName("registrations")] public Registration[] Registrations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs index 6c672b19efdaf..7b6fbc77a4210 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RelatedFullDocumentDiagnosticReport.cs @@ -6,23 +6,21 @@ namespace Roslyn.LanguageServer.Protocol; using System; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a full diagnostic report with a set of related documents. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Full)] internal class RelatedFullDocumentDiagnosticReport : FullDocumentDiagnosticReport { /// /// Gets or sets the map of related document diagnostic reports. /// - [DataMember(Name = "relatedDocuments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("relatedDocuments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary>? RelatedDocuments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs index b4f1c2949d759..b9e968a0f9eda 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RelatedUnchangedDocumentDiagnosticReport.cs @@ -6,23 +6,21 @@ namespace Roslyn.LanguageServer.Protocol; using System; using System.Collections.Generic; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing an unchanged diagnostic report with a set of related documents. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Unchanged)] internal class RelatedUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport { /// /// Gets or sets the map of related document diagnostic reports. /// - [DataMember(Name = "relatedDocuments")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("relatedDocuments")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary>? RelatedDocuments { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs index df1fc09d55b7f..bea10606988cb 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameClientCapabilities.cs @@ -4,25 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using System.Xml.Linq; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - using static System.Net.Mime.MediaTypeNames; + using System.Text.Json.Serialization; /// /// Class which represents renaming client capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameClientCapabilities : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether the client supports testing for validity of rename operations before execution. /// - [DataMember(Name = "prepareSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("prepareSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PrepareSupport { get; @@ -33,8 +28,8 @@ public bool PrepareSupport /// Gets or sets the value indicating the default behavior used by the client when the (`{ defaultBehavior: boolean }`) /// result is used in the 'textDocument/prepareRename' request. /// - [DataMember(Name = "prepareSupportDefaultBehavior")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("prepareSupportDefaultBehavior")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public PrepareSupportDefaultBehavior? PrepareSupportDefaultBehavior { get; @@ -46,8 +41,8 @@ public PrepareSupportDefaultBehavior? PrepareSupportDefaultBehavior /// operations returned via the rename request's workspace edit, by for example presenting the workspace edit in /// the user interface and asking for confirmation. /// - [DataMember(Name = "honorsChangeAnnotations")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("honorsChangeAnnotations")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool HonorsChangeAnnotations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs index 9e7a366ef345a..969e8a039d867 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameFile.cs @@ -5,29 +5,28 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a rename file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [Kind("rename")] internal class RenameFile { /// /// Gets the kind value. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Member can't be static since it's part of the protocol")] public string Kind => "rename"; /// /// Gets or sets the old (existing) location. /// - [DataMember(Name = "oldUri", IsRequired = true)] + [JsonPropertyName("oldUri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri OldUri { @@ -38,7 +37,8 @@ public Uri OldUri /// /// Gets or sets the new location. /// - [DataMember(Name = "newUri", IsRequired = true)] + [JsonPropertyName("newUri")] + [JsonRequired] [JsonConverter(typeof(DocumentUriConverter))] public Uri NewUri { @@ -49,8 +49,8 @@ public Uri NewUri /// /// Gets or sets the rename options. /// - [DataMember(Name = "options")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public RenameFileOptions? Options { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs index 64af96d73853a..59d45c42a225b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameFileOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for a create file operation. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameFileOptions { /// /// Gets or sets a value indicating whether the rename should overwrite the target if it already exists. (Overwrite wins over ignoreIfExists). /// - [DataMember(Name = "overwrite")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("overwrite")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Overwrite { get; @@ -29,8 +27,8 @@ public bool Overwrite /// /// Gets or sets a value indicating whether the action should be ignored if the file already exists. /// - [DataMember(Name = "ignoreIfExists")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("ignoreIfExists")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IgnoreIfExists { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs index f869eea186238..841b679e2e005 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the rename options for server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether renames should be checked and tested before being executed. /// - [DataMember(Name = "prepareProvider")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("prepareProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool PrepareProvider { get; @@ -29,8 +27,8 @@ public bool PrepareProvider /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs index 29b85b249c9a3..3c95b96d9393e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the rename parameters for the textDocument/rename request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameParams : TextDocumentPositionParams { /// /// Gets or sets the new name of the renamed symbol. /// - [DataMember(Name = "newName")] + [JsonPropertyName("newName")] public string NewName { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs b/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs index f424a260e236c..984b50dc823a0 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/RenameRange.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a possible result value of the 'textDocument/prepareRename' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class RenameRange { /// /// Gets or sets the range of the string to rename. /// - [DataMember(Name = "range")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("range")] + [JsonRequired] public Range Range { get; @@ -29,8 +27,8 @@ public Range Range /// /// Gets or sets the placeholder text of the string content to be renamed. /// - [DataMember(Name = "placeholder")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("placeholder")] + [JsonRequired] public string Placeholder { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs index aa05c8d8e28ed..6ff560bb95808 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ResolveSupportSetting.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents initialization setting for properties a client can resolve lazily on a completion item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ResolveSupportSetting { /// /// Gets or sets a value indicating the properties that a client can resolve lazily. /// - [DataMember(Name = "properties", IsRequired = true)] + [JsonPropertyName("properties")] + [JsonRequired] public string[] Properties { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs b/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs index 1b56ff4510eaf..228b44234bf60 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ResourceOperationKind.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the kind of resource operations supported by the client. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct ResourceOperationKind(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs index e14887dc310ef..9d04b661df742 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SaveOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents save option configurations. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SaveOptions { /// /// Gets or sets a value indicating whether clients include text content on save. /// - [DataMember(Name = "includeText")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("includeText")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool IncludeText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs index 3b46370ef84c7..5a75e425e946b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokenFormat.cs @@ -5,15 +5,13 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the format used to describe semantic tokens. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [JsonConverter(typeof(StringEnumConverter))] [TypeConverter(typeof(StringEnumConverter.TypeConverter))] internal readonly record struct SemanticTokenFormat(string Value) : IStringEnum diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs index 5b1c243765f2a..bd467fbf475d6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokens.cs @@ -4,28 +4,27 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing response to semantic tokens messages. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokens { /// /// Gets or sets a property that identifies this version of the document's semantic tokens. /// - [DataMember(Name = "resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// /// Gets or sets and array containing encoded semantic tokens data. /// - [DataMember(Name = "data", IsRequired = true)] + [JsonPropertyName("data")] + [JsonRequired] public int[] Data { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs index c67e0d1e61523..f1697e0ac4939 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDelta.cs @@ -4,31 +4,29 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Represents a response from a semantic tokens Document provider Edits request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensDelta { /// /// Gets or sets the Id for the client's new version after applying all /// edits to their current semantic tokens data. /// - [DataMember(Name = "resultId")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resultId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ResultId { get; set; } /// /// Gets or sets an array of edits to apply to a previous response from a /// semantic tokens Document provider. /// - [DataMember(Name = "edits", IsRequired = true)] + [JsonPropertyName("edits")] + [JsonRequired] public SemanticTokensEdit[] Edits { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs index eee3588bb8a02..aded0c9dd957b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaParams.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameters for a request for Edits that can be applied to a previous response @@ -14,27 +13,26 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensDeltaParams : ITextDocumentParams, IPartialResultParams { /// /// Gets or sets an identifier for the document to fetch semantic tokens from. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets a property indicating the version of the semantic /// tokens Document provider response that the edits will be applied to. /// - [DataMember(Name = "previousResultId")] + [JsonPropertyName("previousResultId")] public string PreviousResultId { get; set; } /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs index 5905b06a385ab..dadc783ce447f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensDeltaPartialResult.cs @@ -4,23 +4,21 @@ namespace Roslyn.LanguageServer.Protocol { - using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Represents a response from a semantic tokens Document provider Edits request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensDeltaPartialResult { /// /// Gets or sets an array of edits to apply to a previous response from a /// semantic tokens Document provider. /// - [DataMember(Name = "edits", IsRequired = true)] + [JsonPropertyName("edits")] + [JsonRequired] public SemanticTokensEdit[] Edits { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs index 9958a6d858b5c..c06932c59a9f7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensEdit.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing an individual edit incrementally applied to a previous @@ -14,7 +13,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1036:Override methods on comparable types", Justification = "Pending implementation of IComparable")] internal class SemanticTokensEdit : IComparable { @@ -22,22 +20,22 @@ internal class SemanticTokensEdit : IComparable /// Gets or sets the position in the previous response's /// to begin the edit. /// - [DataMember(Name = "start")] + [JsonPropertyName("start")] public int Start { get; set; } /// /// Gets or sets the number of numbers to delete in the /// from the previous response. /// - [DataMember(Name = "deleteCount")] + [JsonPropertyName("deleteCount")] public int DeleteCount { get; set; } /// /// Gets or sets an array containing the encoded semantic tokens information to insert /// into a previous response. /// - [DataMember(Name = "data")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("data")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int[]? Data { get; set; } /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs index ac04122c1dd4e..ff7dcdf3e4970 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Options for the full document semantic tokens classification provider. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensFullOptions { /// /// Gets or sets a value indicating whether the server supports deltas for full documents. /// - [DataMember(Name = "delta")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("delta")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Delta { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs index 1e2deb490d8b4..394c0d45c8f68 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensLegend.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Legend used to encode semantic token types in . /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensLegend { /// /// Gets or sets an array of token types that can be encoded in semantic tokens responses. /// - [DataMember(Name = "tokenTypes")] + [JsonPropertyName("tokenTypes")] public string[] TokenTypes { get; @@ -27,7 +26,7 @@ public string[] TokenTypes /// /// Gets or sets an array of token modfiers that can be encoded in semantic tokens responses. /// - [DataMember(Name = "tokenModifiers")] + [JsonPropertyName("tokenModifiers")] public string[] TokenModifiers { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs index 99e483f5ecb47..30fce0f5efaa6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensOptions.cs @@ -4,42 +4,40 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Initialization options for semantic tokens support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensOptions : IWorkDoneProgressOptions { /// /// Gets or sets a legend describing how semantic token types and modifiers are encoded in responses. /// - [DataMember(Name = "legend")] + [JsonPropertyName("legend")] public SemanticTokensLegend Legend { get; set; } /// /// Gets or sets a value indicating whether semantic tokens Range provider requests are supported. /// - [DataMember(Name = "range")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Range { get; set; } /// /// Gets or sets whether or not the server supports providing semantic tokens for a full document. /// - [DataMember(Name = "full")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("full")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Full { get; set; } /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs index f388365622b58..5cf3738c12b6b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensParams.cs @@ -5,28 +5,26 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Parameters for semantic tokens full Document request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensParams : ITextDocumentParams, IPartialResultParams { /// /// Gets or sets an identifier for the document to fetch semantic tokens from. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; set; } /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs index fef2fa0b35b48..3307b392e936a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensPartialResult.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing response to semantic tokens messages. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensPartialResult { /// /// Gets or sets and array containing encoded semantic tokens data. /// - [DataMember(Name = "data", IsRequired = true)] + [JsonPropertyName("data")] + [JsonRequired] public int[] Data { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs index b99746321df5b..0523c2e25293f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRangeParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Parameters for the semantic tokens Range request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensRangeParams : SemanticTokensParams { /// /// Gets or sets the range within the document to fetch semantic tokens for. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs index 60311963550f3..ce6ad7de1e5d6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsFullSetting.cs @@ -4,8 +4,7 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Client settings for semantic tokens related to the @@ -13,7 +12,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensRequestsFullSetting { /// @@ -21,8 +19,8 @@ internal class SemanticTokensRequestsFullSetting /// textDocument/semanticTokens/full/delta request if the server /// provides a corresponding handler. /// - [DataMember(Name = "range")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Delta { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs index 26884ccc267b2..7c9dbd73769b9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensRequestsSetting.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Requests client settings for semantic tokens. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensRequestsSetting { /// @@ -20,8 +18,8 @@ internal class SemanticTokensRequestsSetting /// `textDocument/semanticTokens/range` request if the server provides a /// corresponding handler. /// - [DataMember(Name = "range")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("range")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Range { get; set; } /// @@ -29,8 +27,8 @@ internal class SemanticTokensRequestsSetting /// `textDocument/semanticTokens/full` request if the server provides a /// corresponding handler. /// - [DataMember(Name = "full")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("full")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Full { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs index ddef8c880b076..85e34d5680f9f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensSetting.cs @@ -4,56 +4,54 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Client settings for semantic tokens. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating which requests the client supports and might send to the server /// depending on the server's capability. /// - [DataMember(Name = "requests")] + [JsonPropertyName("requests")] public SemanticTokensRequestsSetting Requests { get; set; } /// /// Gets or sets an array of token types supported by the client for encoding /// semantic tokens. /// - [DataMember(Name = "tokenTypes")] + [JsonPropertyName("tokenTypes")] public string[] TokenTypes { get; set; } /// /// Gets or sets an array of token modifiers supported by the client for encoding /// semantic tokens. /// - [DataMember(Name = "tokenModifiers")] + [JsonPropertyName("tokenModifiers")] public string[] TokenModifiers { get; set; } /// /// Gets or sets an array of formats the clients supports. /// - [DataMember(Name = "formats")] + [JsonPropertyName("formats")] public SemanticTokenFormat[] Formats { get; set; } /// /// Gets or sets a value indicating whether the client supports tokens that can overlap each other. /// - [DataMember(Name = "overlappingTokenSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("overlappingTokenSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool OverlappingTokenSupport { get; set; } /// /// Gets or sets a value indicating whether the client supports tokens that can span multiple lines. /// - [DataMember(Name = "multilineTokenSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("multilineTokenSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool MultilineTokenSupport { get; set; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs index dd101255ae22e..d560f44d6c5c2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensWorkspaceSetting.cs @@ -4,15 +4,13 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Capabilities specific to the semantic token requests scoped to the workspace. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SemanticTokensWorkspaceSetting { /// @@ -25,8 +23,8 @@ internal class SemanticTokensWorkspaceSetting /// and is useful for situation where a server for example detect a project /// wide change that requires such a calculation. /// - [DataMember(Name = "refreshSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("refreshSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool RefreshSupport { get; set; } } } \ No newline at end of file diff --git a/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs index 8e7416165f35f..840da00bb13b1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ServerCapabilities.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System.Diagnostics.CodeAnalysis; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents server capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ServerCapabilities { /// /// Gets or sets the value which indicates how text document are synced. /// - [DataMember(Name = "textDocumentSync")] + [JsonPropertyName("textDocumentSync")] [JsonConverter(typeof(TextDocumentSyncConverter))] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1513:ClosingCurlyBracketMustBeFollowedByBlankLine", Justification = "There are no issues with this code")] [SuppressMessage("Microsoft.StyleCop.CSharp.LayoutRules", "SA1500:BracesForMultiLineStatementsShouldNotShareLine", Justification = "There are no issues with this code")] @@ -40,8 +38,8 @@ public TextDocumentSyncOptions? TextDocumentSync /// /// Gets or sets the value which indicates if completions are supported. /// - [DataMember(Name = "completionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionOptions? CompletionProvider { get; @@ -51,8 +49,8 @@ public CompletionOptions? CompletionProvider /// /// Gets or sets a value indicating whether the server provides hover support. /// - [DataMember(Name = "hoverProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("hoverProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? HoverProvider { get; @@ -62,8 +60,8 @@ public SumType? HoverProvider /// /// Gets or sets the value which indicates if signature help is supported. /// - [DataMember(Name = "signatureHelpProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("signatureHelpProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelpOptions? SignatureHelpProvider { get; @@ -73,8 +71,8 @@ public SignatureHelpOptions? SignatureHelpProvider /// /// Gets or sets a value indicating whether go to definition is supported. /// - [DataMember(Name = "definitionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("definitionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DefinitionProvider { get; @@ -84,8 +82,8 @@ public SumType? DefinitionProvider /// /// Gets or sets a value indicating whether go to type definition is supported. /// - [DataMember(Name = "typeDefinitionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("typeDefinitionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? TypeDefinitionProvider { get; @@ -95,8 +93,8 @@ public SumType? TypeDefinitionProvider /// /// Gets or sets a value indicating whether go to implementation is supported. /// - [DataMember(Name = "implementationProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("implementationProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? ImplementationProvider { get; @@ -106,8 +104,8 @@ public SumType? ImplementationProvider /// /// Gets or sets a value indicating whether find all references is supported. /// - [DataMember(Name = "referencesProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("referencesProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? ReferencesProvider { get; @@ -117,8 +115,8 @@ public SumType? ReferencesProvider /// /// Gets or sets a value indicating whether the server supports document highlight. /// - [DataMember(Name = "documentHighlightProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentHighlightProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentHighlightProvider { get; @@ -128,8 +126,8 @@ public SumType? DocumentHighlightProvider /// /// Gets or sets a value indicating whether document symbols are supported. /// - [DataMember(Name = "documentSymbolProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentSymbolProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentSymbolProvider { get; @@ -139,8 +137,8 @@ public SumType? DocumentSymbolProvider /// /// Gets or sets a value indicating whether code actions are supported. /// - [DataMember(Name = "codeActionProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeActionProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? CodeActionProvider { get; @@ -150,8 +148,8 @@ public SumType? CodeActionProvider /// /// Gets or sets the value which indicates if code lens is supported. /// - [DataMember(Name = "codeLensProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeLensProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeLensOptions? CodeLensProvider { get; @@ -161,8 +159,8 @@ public CodeLensOptions? CodeLensProvider /// /// Gets or sets the value which indicates if document link is supported. /// - [DataMember(Name = "documentLinkProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentLinkProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentLinkOptions? DocumentLinkProvider { get; @@ -172,8 +170,8 @@ public DocumentLinkOptions? DocumentLinkProvider /// /// Gets or sets the value which indicates if document color is supported. /// - [DataMember(Name = "colorProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("colorProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentColorProvider { get; @@ -183,8 +181,8 @@ public SumType? DocumentColorProvider /// /// Gets or sets a value indicating whether document formatting is supported. /// - [DataMember(Name = "documentFormattingProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentFormattingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentFormattingProvider { get; @@ -194,8 +192,8 @@ public SumType? DocumentFormattingProvider /// /// Gets or sets a value indicating whether document range formatting is supported. /// - [DataMember(Name = "documentRangeFormattingProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentRangeFormattingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? DocumentRangeFormattingProvider { get; @@ -205,8 +203,8 @@ public SumType? DocumentRangeFormattingPro /// /// Gets or sets the value which indicates if document on type formatting is supported. /// - [DataMember(Name = "documentOnTypeFormattingProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentOnTypeFormattingProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentOnTypeFormattingOptions? DocumentOnTypeFormattingProvider { get; @@ -216,8 +214,8 @@ public DocumentOnTypeFormattingOptions? DocumentOnTypeFormattingProvider /// /// Gets or sets a value indicating whether rename is supported. /// - [DataMember(Name = "renameProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("renameProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? RenameProvider { get; @@ -227,8 +225,8 @@ public SumType? RenameProvider /// /// Gets or sets the value which indicates if folding range is supported. /// - [DataMember(Name = "foldingRangeProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("foldingRangeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? FoldingRangeProvider { get; @@ -238,8 +236,8 @@ public SumType? FoldingRangeProvider /// /// Gets or sets the value which indicates if execute command is supported. /// - [DataMember(Name = "executeCommandProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("executeCommandProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ExecuteCommandOptions? ExecuteCommandProvider { get; @@ -249,8 +247,8 @@ public ExecuteCommandOptions? ExecuteCommandProvider /// /// Gets or sets a value indicating whether workspace symbols are supported. /// - [DataMember(Name = "workspaceSymbolProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("workspaceSymbolProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? WorkspaceSymbolProvider { get; @@ -260,8 +258,8 @@ public SumType? WorkspaceSymbolProvider /// /// Gets or sets experimental server capabilities. /// - [DataMember(Name = "experimental")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("experimental")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public object? Experimental { get; @@ -271,8 +269,8 @@ public object? Experimental /// /// Gets or sets a value indicating whether the server supports linked editing range. /// - [DataMember(Name = "linkedEditingRangeProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("linkedEditingRangeProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? LinkedEditingRangeProvider { get; @@ -282,8 +280,8 @@ public SumType? LinkedEditingRangeProvider /// /// Gets or sets the value which indicates if semantic tokens is supported. /// - [DataMember(Name = "semanticTokensProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("semanticTokensProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SemanticTokensOptions? SemanticTokensOptions { get; @@ -293,8 +291,8 @@ public SemanticTokensOptions? SemanticTokensOptions /// /// Gets or sets the value which indicates what support the server has for pull diagnostics. /// - [DataMember(Name = "diagnosticProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnosticProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticOptions? DiagnosticOptions { get; @@ -304,8 +302,8 @@ public DiagnosticOptions? DiagnosticOptions /// /// Gets or sets the value which indicates what support the server has for inlay hints. /// - [DataMember(Name = "inlayHintProvider")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("inlayHintProvider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? InlayHintOptions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs index d86b0e3ec53e7..b7da5703aedba 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents parameter sent with window/showMessage requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ShowMessageParams { /// /// Gets or sets the type of message. /// - [DataMember(Name = "type")] + [JsonPropertyName("type")] public MessageType MessageType { get; @@ -27,7 +26,7 @@ public MessageType MessageType /// /// Gets or sets the message. /// - [DataMember(Name = "message")] + [JsonPropertyName("message")] public string Message { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs index 803ee910b8fa3..b8df89cb3f998 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/ShowMessageRequestParams.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents parameter sent with window/showMessageRequest requests. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class ShowMessageRequestParams : ShowMessageParams { /// /// Gets or sets an array of s to present. /// - [DataMember(Name = "actions")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("actions")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MessageActionItem[]? Actions { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs index ff7baa6c8c6c2..fbc02b0b71901 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelp.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the signature of something callable. This class is returned from the textDocument/signatureHelp request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelp { /// /// Gets or sets an array of signatures associated with the callable item. /// - [DataMember(Name = "signatures")] - [JsonProperty(Required = Required.Always)] + [JsonPropertyName("signatures")] + [JsonRequired] public SignatureInformation[] Signatures { get; @@ -29,8 +27,8 @@ public SignatureInformation[] Signatures /// /// Gets or sets the active signature. If the value is omitted or falls outside the range of Signatures it defaults to zero. /// - [DataMember(Name = "activeSignature")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("activeSignature")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? ActiveSignature { get; @@ -40,8 +38,8 @@ public int? ActiveSignature /// /// Gets or sets the active parameter. If the value is omitted or falls outside the range of Signatures[ActiveSignature].Parameters it defaults to zero. /// - [DataMember(Name = "activeParameter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("activeParameter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? ActiveParameter { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs index 4bc39650fc574..7d3a42675c951 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpContext.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing additional information about the context in which a signature help request is triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpContext { /// /// Gets or sets the indicating how the signature help was triggered. /// - [DataMember(Name = "triggerKind")] + [JsonPropertyName("triggerKind")] public SignatureHelpTriggerKind TriggerKind { get; @@ -29,8 +27,8 @@ public SignatureHelpTriggerKind TriggerKind /// Gets or sets the character that caused signature help to be triggered. /// This value is null when triggerKind is not TriggerCharacter. /// - [DataMember(Name = "triggerCharacter")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? TriggerCharacter { get; @@ -40,7 +38,7 @@ public string? TriggerCharacter /// /// Gets or sets a value indicating whether signature help was already showing when it was triggered. /// - [DataMember(Name = "isRetrigger")] + [JsonPropertyName("isRetrigger")] public bool IsRetrigger { get; @@ -50,8 +48,8 @@ public bool IsRetrigger /// /// Gets or sets the currently active . /// - [DataMember(Name = "activeSignatureHelp")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("activeSignatureHelp")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelp? ActiveSignatureHelp { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs index 9578bb1585af1..102ef229035f9 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the options for signature help support. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpOptions : IWorkDoneProgressOptions { /// /// Gets or sets the characters that trigger signature help automatically. /// - [DataMember(Name = "triggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("triggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? TriggerCharacters { get; @@ -30,8 +28,8 @@ public string[]? TriggerCharacters /// Gets or sets the characters that re-trigger signature help /// when signature help is already showing. /// - [DataMember(Name = "retriggerCharacters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("retriggerCharacters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? RetriggerCharacters { get; @@ -41,8 +39,8 @@ public string[]? RetriggerCharacters /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs index 765bf13dbb073..52fbd2f43e10a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpParams.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the parameters for the textDocument/signatureHelp request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpParams : TextDocumentPositionParams { /// /// Gets or sets the signature help context. /// - [DataMember(Name = "context")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("context")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelpContext? Context { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs index 7d12130ab2a96..447b91419344d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the signature help initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureHelpSetting : DynamicRegistrationSetting { /// /// Gets or sets the information. /// - [DataMember(Name = "signatureInformation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("signatureInformation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureInformationSetting? SignatureInformation { get; @@ -30,8 +28,8 @@ public SignatureInformationSetting? SignatureInformation /// Gets or sets a value indicating whether additional context information /// is supported for the `textDocument/signatureHelp` request. /// - [DataMember(Name = "contextSupport")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("contextSupport")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ContextSupport { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs index 2ffba29c58692..00ba1f6410d50 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureHelpTriggerKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various ways in which completion can be triggered. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum SignatureHelpTriggerKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs index 6e68c4d386a97..462bd9c6e2b4a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformation.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a single signature of a callable item. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureInformation { /// /// Gets or sets the label of this signature. /// - [DataMember(Name = "label")] + [JsonPropertyName("label")] public string Label { get; @@ -28,8 +26,8 @@ public string Label /// /// Gets or sets the human-readable documentation of this signature. /// - [DataMember(Name = "documentation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Documentation { get; @@ -39,8 +37,8 @@ public SumType? Documentation /// /// Gets or sets the parameters of this signature. /// - [DataMember(Name = "parameters")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("parameters")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ParameterInformation[]? Parameters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs index 083fa7dfc90a9..efb21c658a425 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SignatureInformationSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the signature information initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SignatureInformationSetting { /// /// Gets or sets the set of documentation formats the client supports. /// - [DataMember(Name = "documentationFormat")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentationFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public MarkupKind[]? DocumentationFormat { get; @@ -29,8 +27,8 @@ public MarkupKind[]? DocumentationFormat /// /// Gets or sets the parameter information the client supports. /// - [DataMember(Name = "parameterInformation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("parameterInformation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ParameterInformationSetting? ParameterInformation { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SumType.cs b/src/Features/LanguageServer/Protocol/Protocol/SumType.cs index 3c50c415f59a8..6d656e070a348 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SumType.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SumType.cs @@ -7,9 +7,7 @@ namespace Roslyn.LanguageServer.Protocol using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; - using Newtonsoft.Json; - using System.Runtime.CompilerServices; - using Microsoft.CommonLanguageServerProtocol.Framework; + using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer; /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs index dc3b38d0cdacb..c35871bdaef2b 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolInformation.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing information about programming constructs like variables, classes, interfaces, etc. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SymbolInformation : IEquatable { /// /// Gets or sets the name of this symbol. /// - [DataMember(Name = "name")] + [JsonPropertyName("name")] public string Name { get; @@ -30,7 +28,7 @@ public string Name /// /// Gets or sets the of this symbol. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public SymbolKind Kind { get; @@ -40,7 +38,7 @@ public SymbolKind Kind /// /// Gets or sets the of this symbol. /// - [DataMember(Name = "location")] + [JsonPropertyName("location")] public Location Location { get; @@ -50,8 +48,8 @@ public Location Location /// /// Gets or sets the name of the symbol containing this symbol. /// - [DataMember(Name = "containerName")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("containerName")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? ContainerName { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs index e2d98be0daac4..77c9d57b8b8a3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolKind.cs @@ -4,15 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; - /// /// Enum which represents the various kinds of symbols. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1720:Identifier contains type name", Justification = "Names are defined by the LSP")] internal enum SymbolKind { diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs index ce32110d895d1..3b9f98a15aad2 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolKindSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the symbol kind setting in initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SymbolKindSetting { /// /// Gets or sets the types of symbol kind the client supports. /// - [DataMember(Name = "valueSet")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("valueSet")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolKind[]? ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs index a1b609d5a041e..bad49978458a1 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SymbolSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the symbol setting for initialization. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SymbolSetting : DynamicRegistrationSetting { /// /// Gets or sets the information. /// - [DataMember(Name = "symbolKind")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("symbolKind")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolKindSetting? SymbolKind { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs index 87da1fdc6ac1e..82e339cb5ea56 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/SynchronizationSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents synchronization initialization setting. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class SynchronizationSetting : DynamicRegistrationSetting { /// /// Gets or sets a value indicating whether WillSave event is supported. /// - [DataMember(Name = "willSave")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSave")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSave { get; @@ -29,8 +27,8 @@ public bool WillSave /// /// Gets or sets a value indicating whether WillSaveWaitUntil event is supported. /// - [DataMember(Name = "willSaveWaitUntil")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSaveWaitUntil")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSaveWaitUntil { get; @@ -40,8 +38,8 @@ public bool WillSaveWaitUntil /// /// Gets or sets a value indicating whether DidSave event is supported. /// - [DataMember(Name = "didSave")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("didSave")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DidSave { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs b/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs index e5dfaffa0e439..2f27a28e442e8 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TagSupport.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TagSupport { /// /// Gets or sets a value indicating the tags supported by the client. /// - [DataMember(Name = "valueSet")] + [JsonPropertyName("valueSet")] public DiagnosticTag[] ValueSet { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs index 5ea26816eaf94..ee1b4051ae3ea 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentClientCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents text document capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentClientCapabilities { /// /// Gets or sets the synchronization setting. /// - [DataMember(Name = "synchronization")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("synchronization")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SynchronizationSetting? Synchronization { get; @@ -29,8 +27,8 @@ public SynchronizationSetting? Synchronization /// /// Gets or sets the completion setting. /// - [DataMember(Name = "completion")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("completion")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CompletionSetting? Completion { get; @@ -40,8 +38,8 @@ public CompletionSetting? Completion /// /// Gets or sets the setting which determines if hover can be dynamically registered. /// - [DataMember(Name = "hover")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("hover")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public HoverSetting? Hover { get; @@ -51,8 +49,8 @@ public HoverSetting? Hover /// /// Gets or sets the setting which determines if signature help can be dynamically registered. /// - [DataMember(Name = "signatureHelp")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("signatureHelp")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SignatureHelpSetting? SignatureHelp { get; @@ -62,8 +60,8 @@ public SignatureHelpSetting? SignatureHelp /// /// Gets or sets the setting which determines if definition can be dynamically registered. /// - [DataMember(Name = "definition")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("definition")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? Definition { get; @@ -73,8 +71,8 @@ public DynamicRegistrationSetting? Definition /// /// Gets or sets the settings which determines if type definition can be dynamically registered. /// - [DataMember(Name = "typeDefinition")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("typeDefinition")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? TypeDefinition { get; @@ -84,8 +82,8 @@ public DynamicRegistrationSetting? TypeDefinition /// /// Gets or sets the settings which determines if implementation can be dynamically registered. /// - [DataMember(Name = "implementation")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("implementation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? Implementation { get; @@ -95,8 +93,8 @@ public DynamicRegistrationSetting? Implementation /// /// Gets or sets the setting which determines if references can be dynamically registered. /// - [DataMember(Name = "references")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("references")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? References { get; @@ -106,8 +104,8 @@ public DynamicRegistrationSetting? References /// /// Gets or sets the setting which determines if document highlight can be dynamically registered. /// - [DataMember(Name = "documentHighlight")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentHighlight")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DocumentHighlight { get; @@ -117,8 +115,8 @@ public DynamicRegistrationSetting? DocumentHighlight /// /// Gets or sets the setting which determines if document symbol can be dynamically registered. /// - [DataMember(Name = "documentSymbol")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentSymbol")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DocumentSymbolSetting? DocumentSymbol { get; @@ -128,8 +126,8 @@ public DocumentSymbolSetting? DocumentSymbol /// /// Gets or sets the setting which determines if code action can be dynamically registered. /// - [DataMember(Name = "codeAction")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeAction")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeActionSetting? CodeAction { get; @@ -139,8 +137,8 @@ public CodeActionSetting? CodeAction /// /// Gets or sets the setting which determines if code lens can be dynamically registered. /// - [DataMember(Name = "codeLens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeLens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? CodeLens { get; @@ -150,8 +148,8 @@ public DynamicRegistrationSetting? CodeLens /// /// Gets or sets the setting which determines if document link can be dynamically registered. /// - [DataMember(Name = "documentLink")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentLink")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DocumentLink { get; @@ -161,8 +159,8 @@ public DynamicRegistrationSetting? DocumentLink /// /// Gets or sets the setting which determines if formatting can be dynamically registered. /// - [DataMember(Name = "formatting")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("formatting")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? Formatting { get; @@ -172,8 +170,8 @@ public DynamicRegistrationSetting? Formatting /// /// Gets or sets the setting which determines if range formatting can be dynamically registered. /// - [DataMember(Name = "rangeFormatting")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rangeFormatting")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? RangeFormatting { get; @@ -183,8 +181,8 @@ public DynamicRegistrationSetting? RangeFormatting /// /// Gets or sets the setting which determines if on type formatting can be dynamically registered. /// - [DataMember(Name = "onTypeFormatting")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("onTypeFormatting")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? OnTypeFormatting { get; @@ -194,8 +192,8 @@ public DynamicRegistrationSetting? OnTypeFormatting /// /// Gets or sets the setting which determines if rename can be dynamically registered. /// - [DataMember(Name = "rename")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rename")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public RenameClientCapabilities? Rename { get; @@ -205,8 +203,8 @@ public RenameClientCapabilities? Rename /// /// Gets or sets the setting publish diagnostics setting. /// - [DataMember(Name = "publishDiagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("publishDiagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public PublishDiagnosticsSetting? PublishDiagnostics { get; @@ -216,7 +214,7 @@ public PublishDiagnosticsSetting? PublishDiagnostics /// /// Gets or sets the setting which determines how folding range is supported. /// - [DataMember(Name = "foldingRange")] + [JsonPropertyName("foldingRange")] public FoldingRangeSetting? FoldingRange { get; @@ -226,8 +224,8 @@ public FoldingRangeSetting? FoldingRange /// /// Gets or sets the setting which determines if linked editing range can be dynamically registered. /// - [DataMember(Name = "linkedEditingRange")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("linkedEditingRange")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting LinkedEditingRange { get; @@ -237,8 +235,8 @@ public DynamicRegistrationSetting LinkedEditingRange /// /// Gets or sets a setting indicating whether semantic tokens is supported. /// - [DataMember(Name = "semanticTokens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("semanticTokens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SemanticTokensSetting? SemanticTokens { get; @@ -248,8 +246,8 @@ public SemanticTokensSetting? SemanticTokens /// /// Gets or sets the setting which determines what support the client has for pull diagnostics. /// - [DataMember(Name = "diagnostic")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnostic")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticSetting? Diagnostic { get; @@ -259,8 +257,8 @@ public DiagnosticSetting? Diagnostic /// /// Gets or sets the setting which determines what support the client has for pull diagnostics. /// - [DataMember(Name = "inlayHint")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("inlayHint")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintSetting? InlayHint { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs index 8c2dd3dbb9566..64568623fe4dc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentContentChangeEvent.cs @@ -4,21 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which encapsulates a text document changed event. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentContentChangeEvent { /// /// Gets or sets the range of the text that was changed. /// - [DataMember(Name = "range")] + [JsonPropertyName("range")] public Range Range { get; @@ -28,8 +26,8 @@ public Range Range /// /// Gets or sets the length of the range that got replaced. /// - [DataMember(Name = "rangeLength")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("rangeLength")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public int? RangeLength { get; @@ -39,7 +37,7 @@ public int? RangeLength /// /// Gets or sets the new text of the range/document. /// - [DataMember(Name = "text")] + [JsonPropertyName("text")] public string Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs index bfe8999beff4c..26af2f8e87483 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentEdit.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing a set of changes to a single text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentEdit { /// /// Gets or sets a document identifier indication which document to apply the edits to. /// - [DataMember(Name = "textDocument", IsRequired = true)] + [JsonPropertyName("textDocument")] + [JsonRequired] public OptionalVersionedTextDocumentIdentifier TextDocument { get; @@ -27,7 +27,8 @@ public OptionalVersionedTextDocumentIdentifier TextDocument /// /// Gets or sets the array of edits to be applied to the document. /// - [DataMember(Name = "edits", IsRequired = true)] + [JsonPropertyName("edits")] + [JsonRequired] public TextEdit[] Edits { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs index 78ffa87d73c42..9ec8f5a0c8804 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentIdentifier.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which identifies a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentIdentifier : IEquatable { /// /// Gets or sets the URI of the text document. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs index 4885efdb848b9..9a627e3ff7622 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentItem.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentItem { /// /// Gets or sets the document URI. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -30,7 +28,7 @@ public Uri Uri /// /// Gets or sets the document language identifier. /// - [DataMember(Name = "languageId")] + [JsonPropertyName("languageId")] public string LanguageId { get; @@ -40,7 +38,7 @@ public string LanguageId /// /// Gets or sets the document version. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int Version { get; @@ -50,7 +48,7 @@ public int Version /// /// Gets or sets the content of the opened text document. /// - [DataMember(Name = "text")] + [JsonPropertyName("text")] public string Text { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs index 7886b68fb690a..ab430a5ed29c6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentPositionParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a position within a text document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentPositionParams : ITextDocumentPositionParams { /// /// Gets or sets the value which identifies the document. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the value which indicates the position within the document. /// - [DataMember(Name = "position")] + [JsonPropertyName("position")] public Position Position { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs index 6fee4bee03e0f..94a0182345207 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentRegistrationOptions.cs @@ -4,22 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing the registration options for many different text document functions. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentRegistrationOptions : ITextDocumentRegistrationOptions { /// /// Gets or sets the document filters for this registration option. /// - [DataMember(Name = "documentSelector")] - [JsonProperty(NullValueHandling = NullValueHandling.Include)] + [JsonPropertyName("documentSelector")] public DocumentFilter[]? DocumentSelector { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs index c0ad11d2cf815..3fab506457c1f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSaveReason.cs @@ -4,15 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; - /// /// Enum representing the reason a document was saved. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum TextDocumentSaveReason { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs index 105d0dbe2d5fe..5097bb3586c9c 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncKind.cs @@ -4,14 +4,11 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - /// /// Enum which represents the various ways to sync text documents. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal enum TextDocumentSyncKind { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs index 2c398b13b22e2..72f5e2a1c179e 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentSyncOptions.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents configuration values indicating how text documents should be synced. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentSyncOptions { /// /// Gets or sets a value indicating whether open and close notifications are sent to the server. /// - [DataMember(Name = "openClose")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("openClose")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool OpenClose { get; @@ -30,8 +28,8 @@ public bool OpenClose /// /// Gets or sets the value indicating how text documents are synced with the server. /// - [DataMember(Name = "change")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("change")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [DefaultValue(TextDocumentSyncKind.None)] public TextDocumentSyncKind? Change { @@ -42,8 +40,8 @@ public TextDocumentSyncKind? Change /// /// Gets or sets a value indicating whether 'will save' notifications are sent to the server. /// - [DataMember(Name = "willSave")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSave")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSave { get; @@ -53,8 +51,8 @@ public bool WillSave /// /// Gets or sets a value indicating whether 'will save until' notifications are sent to the server. /// - [DataMember(Name = "willSaveWaitUntil")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("willSaveWaitUntil")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WillSaveWaitUntil { get; @@ -64,8 +62,8 @@ public bool WillSaveWaitUntil /// /// Gets or sets a value indicating whether save notifications are sent to the server. /// - [DataMember(Name = "save")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("save")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType? Save { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs index dccc1ce6887c5..c25885343f6c4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextEdit.cs @@ -4,20 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class which represents a text edit to a document. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextEdit { /// /// Gets or sets the value which indicates the range of the text edit. /// - [DataMember(Name = "range", IsRequired = true)] + [JsonPropertyName("range")] + [JsonRequired] public Range Range { get; @@ -27,7 +27,7 @@ public Range Range /// /// Gets or sets the value of the new text. /// - [DataMember(Name = "newText")] + [JsonPropertyName("newText")] public string NewText { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs index 92261d6fc1790..cf1ec787ae2f4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TraceSetting.cs @@ -5,7 +5,7 @@ namespace Roslyn.LanguageServer.Protocol { using System.ComponentModel; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Value representing the language server trace setting. diff --git a/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs index 031e241a9fa5e..b17c3bec86600 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TypeDefinitionOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TypeDefinitionOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs index eaff6e3e52e55..238da08d40af5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/UnchangedDocumentDiagnosticReport.cs @@ -4,21 +4,20 @@ namespace Roslyn.LanguageServer.Protocol; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; /// /// Class representing a diagnostic report indicating that the last returned report is still accurate. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Unchanged)] internal class UnchangedDocumentDiagnosticReport { /// /// Gets the kind of this report. /// - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] #pragma warning disable CA1822 // Mark members as static public string Kind => DocumentDiagnosticReportKind.Unchanged; #pragma warning restore CA1822 // Mark members as static @@ -26,7 +25,7 @@ internal class UnchangedDocumentDiagnosticReport /// /// Gets or sets the optional result id. /// - [DataMember(Name = "resultId")] + [JsonPropertyName("resultId")] public string ResultId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs b/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs index 03493010e0a44..ea2c9e8efbea3 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Unregistration.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the information needed for unregistering a capability. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class Unregistration { /// /// Gets or sets the id of the unregistration. /// - [DataMember(Name = "id")] + [JsonPropertyName("id")] public string Id { get; @@ -27,7 +26,7 @@ public string Id /// /// Gets or sets the method to unregister. /// - [DataMember(Name = "method")] + [JsonPropertyName("method")] public string Method { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs b/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs index 54d954451b88e..118084c82212d 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/UnregistrationParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameter sent for the client/unregisterCapability request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class UnregistrationParams { /// /// Gets or sets the capabilities to unregister. /// - [DataMember(Name = "unregistrations")] + [JsonPropertyName("unregistrations")] public Unregistration[] Unregistrations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs b/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs index ecffd30fcdbba..296808d6a31f4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/VersionedTextDocumentIdentifier.cs @@ -6,21 +6,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; using System.Globalization; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents a text document, but has a version identifier. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class VersionedTextDocumentIdentifier : TextDocumentIdentifier, IEquatable { /// /// Gets or sets the version of the document. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int Version { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs b/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs index fb88efda26c86..46269ba196183 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WillSaveTextDocumentParams.cs @@ -4,20 +4,19 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; + using System.Text.Json.Serialization; /// /// Class representing the parameters sent for the textDocument/willSave request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WillSaveTextDocumentParams : ITextDocumentParams { /// /// Gets or sets the representing the document to be saved. /// - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public TextDocumentIdentifier TextDocument { get; @@ -27,7 +26,7 @@ public TextDocumentIdentifier TextDocument /// /// Gets or sets the reason that the text document was saved. /// - [DataMember(Name = "reason")] + [JsonPropertyName("reason")] public TextDocumentSaveReason Reason { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs index 74cb04f4e3912..a045161ec3973 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceClientCapabilities.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceClientCapabilities { /// /// Gets or sets a value indicating whether apply edit is supported. /// - [DataMember(Name = "applyEdit")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("applyEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool ApplyEdit { get; @@ -29,8 +27,8 @@ public bool ApplyEdit /// /// Gets or sets the workspace edit setting. /// - [DataMember(Name = "workspaceEdit")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("workspaceEdit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public WorkspaceEditSetting? WorkspaceEdit { get; @@ -40,8 +38,8 @@ public WorkspaceEditSetting? WorkspaceEdit /// /// Gets or sets the setting which determines if did change configuration can be dynamically registered. /// - [DataMember(Name = "didChangeConfiguration")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("didChangeConfiguration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DidChangeConfiguration { get; @@ -51,8 +49,8 @@ public DynamicRegistrationSetting? DidChangeConfiguration /// /// Gets or sets the setting which determines if did change watched files can be dynamically registered. /// - [DataMember(Name = "didChangeWatchedFiles")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("didChangeWatchedFiles")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? DidChangeWatchedFiles { get; @@ -62,8 +60,8 @@ public DynamicRegistrationSetting? DidChangeWatchedFiles /// /// Gets or sets the setting which determines if symbols can be dynamically registered. /// - [DataMember(Name = "symbol")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("symbol")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SymbolSetting? Symbol { get; @@ -73,8 +71,8 @@ public SymbolSetting? Symbol /// /// Gets or sets the setting which determines if execute command can be dynamically registered. /// - [DataMember(Name = "executeCommand")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("executeCommand")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DynamicRegistrationSetting? ExecuteCommand { get; @@ -84,8 +82,8 @@ public DynamicRegistrationSetting? ExecuteCommand /// /// Gets or sets capabilities specific to the semantic token requests scoped to the workspace. /// - [DataMember(Name = "semanticTokens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("semanticTokens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SemanticTokensWorkspaceSetting? SemanticTokens { get; @@ -95,8 +93,8 @@ public SemanticTokensWorkspaceSetting? SemanticTokens /// /// Gets or sets capabilities indicating what support the client has for workspace pull diagnostics. /// - [DataMember(Name = "diagnostics")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("diagnostics")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public DiagnosticWorkspaceSetting? Diagnostics { get; @@ -106,8 +104,8 @@ public DiagnosticWorkspaceSetting? Diagnostics /// /// Gets or sets the capabilities if client support 'workspace/configuration' requests. /// - [DataMember(Name = "configuration")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("configuration")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Configuration { get; @@ -117,8 +115,8 @@ public bool Configuration /// /// Gets of sets capabilities specific to the inlay hint requests scoped to the workspace. /// - [DataMember(Name = "inlayHint")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("inlayHint")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public InlayHintWorkspaceSetting? InlayHint { get; @@ -128,8 +126,8 @@ public InlayHintWorkspaceSetting? InlayHint /// /// Gets of sets capabilities specific to the code lens requests scoped to the workspace. /// - [DataMember(Name = "codeLens")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("codeLens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public CodeLensWorkspaceSetting? CodeLens { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs index f8c2cdb7dcfef..850f8c4fa31ab 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticParams.cs @@ -5,8 +5,7 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing the workspace diagnostic request parameters @@ -17,14 +16,13 @@ namespace Roslyn.LanguageServer.Protocol; /// Note that the first literal send needs to be a /// followed by n literals. /// -[DataContract] internal class WorkspaceDiagnosticParams : IPartialResultParams> { /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName, IsRequired = false)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress>? PartialResultToken { get; @@ -34,8 +32,8 @@ public IProgress /// Gets or sets the identifier for which the client is requesting diagnostics for. /// - [DataMember(Name = "identifier")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("identifier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Identifier { get; @@ -45,7 +43,7 @@ public string? Identifier /// /// Gets or sets the result id of a previous diagnostics response if provided. /// - [DataMember(Name = "previousResultIds")] + [JsonPropertyName("previousResultIds")] public PreviousResultId[] PreviousResultId { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs index fff52c02f2e2e..49b33e63f135a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReport.cs @@ -3,23 +3,19 @@ // See the LICENSE file in the project root for more information. namespace Roslyn.LanguageServer.Protocol; - -using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a workspace diagnostic report. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class WorkspaceDiagnosticReport { /// /// Gets or sets the items in this diagnostic report. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public SumType[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs index f2e3387cc2c88..1c29c40074e81 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceDiagnosticReportPartialResult.cs @@ -3,23 +3,19 @@ // See the LICENSE file in the project root for more information. namespace Roslyn.LanguageServer.Protocol; - -using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a partial result for a workspace diagnostic report. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] internal class WorkspaceDiagnosticReportPartialResult { /// /// Gets or sets the items in this diagnostic report. /// - [DataMember(Name = "items")] + [JsonPropertyName("items")] public SumType[] Items { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs index 23fba470b1e07..d3739a5a90425 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEdit.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol { using System.Collections.Generic; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class representing a request sent from a language server to modify resources in the workspace. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceEdit { /// /// Gets or sets a dictionary holding changes to existing resources. /// - [DataMember(Name = "changes")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("changes")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Dictionary? Changes { get; @@ -30,8 +28,8 @@ public Dictionary? Changes /// /// Gets or sets an array representing versioned document changes. /// - [DataMember(Name = "documentChanges")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("documentChanges")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public SumType[]>? DocumentChanges { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs index 26265677883bd..c3aee573bdf8f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceEditSetting.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents initialization settings for workspace edit. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceEditSetting { /// /// Gets or sets a value indicating whether document changes event is supported. /// - [DataMember(Name = "documentChanges")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("documentChanges")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool DocumentChanges { get; @@ -29,8 +27,8 @@ public bool DocumentChanges /// /// GEts or sets the resource operations the client supports. /// - [DataMember(Name = "resourceOperations")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("resourceOperations")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ResourceOperationKind[]? ResourceOperations { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs index 7a2f9d84ee9d2..95ec1cad08b96 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceFullDocumentDiagnosticReport.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a full document diagnostic report for workspace diagnostic result. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Full)] internal class WorkspaceFullDocumentDiagnosticReport : FullDocumentDiagnosticReport { /// /// Gets or sets the URI associated with this diagnostic report. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -32,7 +30,7 @@ public Uri Uri /// Gets or sets the version number for which the diagnostics are reported. /// If the document is not marked as open 'null' can be provided. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int? Version { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs index 8f2f697842d37..01ac93b66f538 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolOptions.cs @@ -4,22 +4,20 @@ namespace Roslyn.LanguageServer.Protocol { - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents workspace symbols capabilities. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceSymbolOptions : IWorkDoneProgressOptions { /// /// Gets or sets a value indicating whether work done progress is supported. /// - [DataMember(Name = "workDoneProgress")] - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("workDoneProgress")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool WorkDoneProgress { get; init; } } } diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs index 2ef10ea9fa3ef..bc70cf64c559a 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceSymbolParams.cs @@ -5,21 +5,19 @@ namespace Roslyn.LanguageServer.Protocol { using System; - using System.Runtime.Serialization; - using Newtonsoft.Json; + using System.Text.Json.Serialization; /// /// Class which represents the parameter that's sent with the 'workspace/symbol' request. /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class WorkspaceSymbolParams : IPartialResultParams { /// /// Gets or sets the query (a non-empty string). /// - [DataMember(Name = "query")] + [JsonPropertyName("query")] public string Query { get; @@ -29,8 +27,8 @@ public string Query /// /// Gets or sets the value of the Progress instance. /// - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs index d7aa7192da725..1fc1bd8cda697 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/WorkspaceUnchangedDocumentDiagnosticReport.cs @@ -5,22 +5,20 @@ namespace Roslyn.LanguageServer.Protocol; using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; /// /// Class representing a unchanged document diagnostic report for workspace diagnostic result. /// /// See the Language Server Protocol specification for additional information. /// -[DataContract] [Kind(DocumentDiagnosticReportKind.Unchanged)] internal class WorkspaceUnchangedDocumentDiagnosticReport : UnchangedDocumentDiagnosticReport { /// /// Gets or sets the URI associated with this diagnostic report. /// - [DataMember(Name = "uri")] + [JsonPropertyName("uri")] [JsonConverter(typeof(DocumentUriConverter))] public Uri Uri { @@ -32,7 +30,7 @@ public Uri Uri /// Gets or sets the version number for which the diagnostics are reported. /// If the document is not marked as open 'null' can be provided. /// - [DataMember(Name = "version")] + [JsonPropertyName("version")] public int? Version { get; diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs index 92ac29ae53f61..bb5490d3220ea 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspMiscellaneousFilesWorkspace.cs @@ -3,15 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Features.Workspaces; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs index 289f609e7df67..5a7b6a6a69a09 100644 --- a/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs +++ b/src/Features/LanguageServer/Protocol/Workspaces/LspWorkspaceManager.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; From 83bd82f64ed1144ed459aefe68e9bc47aaad4384 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 2 May 2024 11:18:15 +0200 Subject: [PATCH 012/423] Allow unsafe blocks in iterators (#73046) * Allow unsafe blocks in iterators * Drop duplicate long diagnostic verifications * Use `nint` in tests to simplify * Add more tests * Handle more iterator kinds * Make unsafe affect only signature, not iterator body * Fix comments * Rename method setting or clearing unsafe region * Improve code * Fix accessors * Remove asserts that can fail for invalid code * Improve tests * Avoid some langversion errors that would still be errors in newer langversions * Refactor tests * Document a breaking change * Fix theory conditions * Test non-iterator unsafe contexts in older language versions as well * Fix tests demonstrating unsafe context in iterators in C# 12 * Improve naming of tests verifying safe context of setters * Clarify comment * Fix test name --- .../Compiler Breaking Changes - DotNet 9.md | 27 + .../Portable/Binder/Binder.ValueChecks.cs | 24 +- .../BinderFactory.BinderFactoryVisitor.cs | 38 +- .../CSharp/Portable/Binder/Binder_Flags.cs | 21 +- .../Portable/Binder/Binder_Statements.cs | 8 +- .../CSharp/Portable/Binder/Binder_Unsafe.cs | 10 +- .../Portable/Binder/ExecutableCodeBinder.cs | 2 +- .../Portable/Binder/LocalBinderFactory.cs | 4 +- .../CSharp/Portable/CSharpResources.resx | 9 +- .../CSharp/Portable/Errors/ErrorCode.cs | 4 +- .../CSharp/Portable/Errors/ErrorFacts.cs | 3 +- .../Symbols/Source/LocalFunctionSymbol.cs | 2 +- .../Symbols/Source/SourcePropertySymbol.cs | 4 +- .../Portable/xlf/CSharpResources.cs.xlf | 15 +- .../Portable/xlf/CSharpResources.de.xlf | 15 +- .../Portable/xlf/CSharpResources.es.xlf | 15 +- .../Portable/xlf/CSharpResources.fr.xlf | 15 +- .../Portable/xlf/CSharpResources.it.xlf | 15 +- .../Portable/xlf/CSharpResources.ja.xlf | 15 +- .../Portable/xlf/CSharpResources.ko.xlf | 15 +- .../Portable/xlf/CSharpResources.pl.xlf | 15 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 15 +- .../Portable/xlf/CSharpResources.ru.xlf | 15 +- .../Portable/xlf/CSharpResources.tr.xlf | 15 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 15 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 15 +- .../Test/Emit2/Emit/NumericIntPtrTests.cs | 43 +- .../Semantic/Semantics/LocalFunctionTests.cs | 47 +- .../Semantic/Semantics/NativeIntegerTests.cs | 29 +- .../Semantic/Semantics/SemanticErrorTests.cs | 43 +- .../Test/Semantic/Semantics/UnsafeTests.cs | 2232 ++++++++++++++++- 31 files changed, 2530 insertions(+), 215 deletions(-) create mode 100644 docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md new file mode 100644 index 0000000000000..0b9cd956cdf28 --- /dev/null +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -0,0 +1,27 @@ +# This document lists known breaking changes in Roslyn after .NET 8 all the way to .NET 9. + +## Iterators introduce safe context in C# 13 and newer + +***Introduced in Visual Studio 2022 version 17.11*** + +Although the language spec states that iterators introduce a safe context, Roslyn does not implement that in C# 12 and lower. +This will change in C# 13 as part of [a feature which allows unsafe code in iterators](https://github.com/dotnet/roslyn/issues/72662). +The change does not break normal scenarios as it was disallowed to use unsafe constructs directly in iterators anyway. +However, it can break scenarios where an unsafe context was previously inherited into nested local functions, for example: + +```cs +unsafe class C // unsafe context +{ + System.Collections.Generic.IEnumerable M() // an iterator + { + yield return 1; + local(); + void local() + { + int* p = null; // allowed in C# 12; error in C# 13 + } + } +} +``` + +You can work around the break simply by adding the `unsafe` modifier to the local function. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 1275fa01322ef..918887857b42b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -872,12 +872,24 @@ private static bool CheckNotNamespaceOrType(BoundExpression expr, BindingDiagnos } } - private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) + private void CheckAddressOfInAsyncOrIteratorMethod(SyntaxNode node, BindValueKind valueKind, BindingDiagnosticBag diagnostics) { - if (valueKind == BindValueKind.AddressOf && this.IsInAsyncMethod()) + if (valueKind == BindValueKind.AddressOf) { - Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); + if (this.IsInAsyncMethod()) + { + Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); + } + else if (this.IsDirectlyInIterator && Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync)) + { + Error(diagnostics, ErrorCode.ERR_AddressOfInIterator, node); + } } + } + + private bool CheckLocalValueKind(SyntaxNode node, BoundLocal local, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) + { + CheckAddressOfInAsyncOrIteratorMethod(node, valueKind, diagnostics); // Local constants are never variables. Local variables are sometimes // not to be treated as variables, if they are fixed, declared in a using, @@ -968,10 +980,8 @@ internal partial class Binder private bool CheckParameterValueKind(SyntaxNode node, BoundParameter parameter, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) { Debug.Assert(!RequiresAssignableVariable(BindValueKind.AddressOf)); - if (valueKind == BindValueKind.AddressOf && this.IsInAsyncMethod()) - { - Error(diagnostics, ErrorCode.WRN_AddressOfInAsync, node); - } + + CheckAddressOfInAsyncOrIteratorMethod(node, valueKind, diagnostics); ParameterSymbol parameterSymbol = parameter.ParameterSymbol; diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 6cd5b93fc0a6f..dd5a9adf44764 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -167,6 +167,7 @@ public override Binder VisitMethodDeclaration(MethodDeclarationSyntax methodDecl } SourceMemberMethodSymbol method = null; + bool isIteratorBody = false; if (usage != NodeUsage.Normal && methodDecl.TypeParameterList != null) { @@ -177,10 +178,11 @@ public override Binder VisitMethodDeclaration(MethodDeclarationSyntax methodDecl if (usage == NodeUsage.MethodBody) { method = method ?? GetMethodSymbol(methodDecl, resultBinder); + isIteratorBody = method.IsIterator; resultBinder = new InMethodBinder(method, resultBinder); } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(methodDecl.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(methodDecl.Modifiers, isIteratorBody: isIteratorBody); binderCache.TryAdd(key, resultBinder); } @@ -218,7 +220,7 @@ public override Binder VisitConstructorDeclaration(ConstructorDeclarationSyntax } } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -245,7 +247,7 @@ public override Binder VisitDestructorDeclaration(DestructorDeclarationSyntax pa SourceMemberMethodSymbol method = GetMethodSymbol(parent, resultBinder); resultBinder = new InMethodBinder(method, resultBinder); - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -307,6 +309,10 @@ public override Binder VisitAccessorDeclaration(AccessorDeclarationSyntax parent if ((object)accessor != null) { resultBinder = new InMethodBinder(accessor, resultBinder); + + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary( + modifiers: default, + isIteratorBody: accessor.IsIterator); } } @@ -334,12 +340,14 @@ private Binder VisitOperatorOrConversionDeclaration(BaseMethodDeclarationSyntax resultBinder = VisitCore(parent.Parent); MethodSymbol method = GetMethodSymbol(parent, resultBinder); + bool isIteratorBody = false; if ((object)method != null && inBody) { + isIteratorBody = method.IsIterator; resultBinder = new InMethodBinder(method, resultBinder); } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers, isIteratorBody: isIteratorBody); binderCache.TryAdd(key, resultBinder); } @@ -359,24 +367,24 @@ public override Binder VisitConversionOperatorDeclaration(ConversionOperatorDecl public override Binder VisitFieldDeclaration(FieldDeclarationSyntax parent) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } public override Binder VisitEventDeclaration(EventDeclarationSyntax parent) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } public override Binder VisitEventFieldDeclaration(EventFieldDeclarationSyntax parent) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } public override Binder VisitPropertyDeclaration(PropertyDeclarationSyntax parent) { if (!LookupPosition.IsInBody(_position, parent)) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } return VisitPropertyOrIndexerExpressionBody(parent); @@ -386,7 +394,7 @@ public override Binder VisitIndexerDeclaration(IndexerDeclarationSyntax parent) { if (!LookupPosition.IsInBody(_position, parent)) { - return VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + return VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); } return VisitPropertyOrIndexerExpressionBody(parent); @@ -399,12 +407,16 @@ private Binder VisitPropertyOrIndexerExpressionBody(BasePropertyDeclarationSynta Binder resultBinder; if (!binderCache.TryGetValue(key, out resultBinder)) { - resultBinder = VisitCore(parent.Parent).WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = VisitCore(parent.Parent).SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); var propertySymbol = GetPropertySymbol(parent, resultBinder); var accessor = propertySymbol.GetMethod; if ((object)accessor != null) { + // Expression body cannot be an iterator, otherwise we would need to pass + // `isIteratorBody` to `SetOrClearUnsafeRegionIfNecessary` above. + Debug.Assert(!accessor.IsIterator); + resultBinder = new InMethodBinder(accessor, resultBinder); } @@ -659,7 +671,7 @@ public override Binder VisitDelegateDeclaration(DelegateDeclarationSyntax parent resultBinder = new WithClassTypeParametersBinder(container, resultBinder); } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -687,7 +699,7 @@ public override Binder VisitEnumDeclaration(EnumDeclarationSyntax parent) resultBinder = new InContainerBinder(container, outer); - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } @@ -769,7 +781,7 @@ internal Binder VisitTypeDeclarationCore(TypeDeclarationSyntax parent, NodeUsage } } - resultBinder = resultBinder.WithUnsafeRegionIfNecessary(parent.Modifiers); + resultBinder = resultBinder.SetOrClearUnsafeRegionIfNecessary(parent.Modifiers); binderCache.TryAdd(key, resultBinder); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs index 7374ede9d1163..e594a2999aea2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Flags.cs @@ -91,11 +91,24 @@ internal Binder WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags flags return new BinderWithContainingMemberOrLambda(this, this.Flags | flags, containing); } - internal Binder WithUnsafeRegionIfNecessary(SyntaxTokenList modifiers) + internal Binder SetOrClearUnsafeRegionIfNecessary(SyntaxTokenList modifiers, bool isIteratorBody = false) { - return (this.Flags.Includes(BinderFlags.UnsafeRegion) || !modifiers.Any(SyntaxKind.UnsafeKeyword)) - ? this - : new Binder(this, this.Flags | BinderFlags.UnsafeRegion); + // In C# 13 and above, iterator bodies define a safe context even when nested in an unsafe context. + // In C# 12 and below, we keep the (spec violating) behavior that iterator bodies inherit the safe/unsafe context + // from their containing scope. Since there are errors for unsafe constructs directly in iterators, + // this inherited unsafe context can be observed only in nested non-iterator local functions. + var withoutUnsafe = isIteratorBody && this.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync); + + if (this.Flags.Includes(BinderFlags.UnsafeRegion)) + { + return withoutUnsafe + ? new Binder(this, this.Flags & ~BinderFlags.UnsafeRegion) + : this; + } + + return !withoutUnsafe && modifiers.Any(SyntaxKind.UnsafeKeyword) + ? new Binder(this, this.Flags | BinderFlags.UnsafeRegion) + : this; } internal Binder WithCheckedOrUncheckedRegion(bool @checked) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index faeeb28982488..d65ff54ae46fe 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -176,9 +176,7 @@ private BoundStatement BindUnsafeStatement(UnsafeStatementSyntax node, BindingDi } else if (this.IsIndirectlyInIterator) // called *after* we know the binder map has been created. { - // Spec 8.2: "An iterator block always defines a safe context, even when its declaration - // is nested in an unsafe context." - Error(diagnostics, ErrorCode.ERR_IllegalInnerUnsafe, node.UnsafeKeyword); + CheckFeatureAvailability(node.UnsafeKeyword, MessageID.IDS_FeatureRefUnsafeInIteratorAsync, diagnostics); } return BindEmbeddedBlock(node.Block, diagnostics); @@ -268,6 +266,10 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi { Error(diagnostics, ErrorCode.ERR_YieldNotAllowedInScript, node.YieldKeyword); } + else if (InUnsafeRegion && Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefUnsafeInIteratorAsync)) + { + Error(diagnostics, ErrorCode.ERR_BadYieldInUnsafe, node.YieldKeyword); + } // NOTE: Error conditions should be checked above this point; only warning conditions below. else if (this.Flags.Includes(BinderFlags.InLockBody)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs index ec8cd5d3885b7..2c4d9b6a7fb88 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs @@ -56,18 +56,16 @@ private CSDiagnosticInfo GetUnsafeDiagnosticInfo(TypeSymbol sizeOfTypeOpt) { return null; } - else if (this.IsIndirectlyInIterator) - { - // Spec 8.2: "An iterator block always defines a safe context, even when its declaration - // is nested in an unsafe context." - return new CSDiagnosticInfo(ErrorCode.ERR_IllegalInnerUnsafe); - } else if (!this.InUnsafeRegion) { return ((object)sizeOfTypeOpt == null) ? new CSDiagnosticInfo(ErrorCode.ERR_UnsafeNeeded) : new CSDiagnosticInfo(ErrorCode.ERR_SizeofUnsafe, sizeOfTypeOpt); } + else if (this.IsIndirectlyInIterator && MessageID.IDS_FeatureRefUnsafeInIteratorAsync.GetFeatureAvailabilityDiagnosticInfo(Compilation) is { } unsafeInIteratorDiagnosticInfo) + { + return unsafeInIteratorDiagnosticInfo; + } else { return null; diff --git a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs index edffa7f5535d3..e08152589f822 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs @@ -129,7 +129,7 @@ public static void ValidateIteratorMethod(CSharpCompilation compilation, MethodS if (((iterator as SourceMemberMethodSymbol)?.IsUnsafe == true || (iterator as LocalFunctionSymbol)?.IsUnsafe == true) && compilation.Options.AllowUnsafe) // Don't cascade { - diagnostics.Add(ErrorCode.ERR_IllegalInnerUnsafe, errorLocation); + MessageID.IDS_FeatureRefUnsafeInIteratorAsync.CheckFeatureAvailability(diagnostics, compilation, errorLocation); } var returnType = iterator.ReturnType; diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index 0695fe3f94395..1e6ee6bfc4953 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -416,7 +416,9 @@ public override void VisitLocalFunctionStatement(LocalFunctionStatementSyntax no ? new WithMethodTypeParametersBinder(match, _enclosing) : _enclosing; - binder = binder.WithUnsafeRegionIfNecessary(node.Modifiers); + binder = binder.SetOrClearUnsafeRegionIfNecessary(node.Modifiers, + isIteratorBody: match.IsIterator); + binder = new InMethodBinder(match, binder); } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 546daa5c1d4b1..2c09d4efb99e5 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2950,9 +2950,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Cannot use ref, out, or in parameter '{0}' inside an anonymous method, lambda expression, query expression, or local function - - Unsafe code may not appear in iterators - Cannot yield a value in the body of a catch clause @@ -7905,4 +7902,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A 'ref' local cannot be preserved across 'await' or 'yield' boundary. + + Cannot use 'yield return' in an 'unsafe' block + + + The '&' operator cannot be used on parameters or local variables in iterator methods. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index b4fb86f47e14b..7f65516069734 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -792,7 +792,7 @@ internal enum ErrorCode ERR_BadYieldInTryOfCatch = 1626, ERR_EmptyYield = 1627, ERR_AnonDelegateCantUse = 1628, - ERR_IllegalInnerUnsafe = 1629, + // ERR_IllegalInnerUnsafe = 1629, //ERR_BadWatsonMode = 1630, ERR_BadYieldInCatch = 1631, ERR_BadDelegateLeave = 1632, @@ -2305,6 +2305,8 @@ internal enum ErrorCode #endregion WRN_BadYieldInLock = 9230, + ERR_BadYieldInUnsafe = 9231, + ERR_AddressOfInIterator = 9232, // Note: you will need to do the following after adding warnings: // 1) Re-generate compiler code (eng\generate-compiler-code.cmd). diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 4722401c000b1..c599c2a5bb15f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -1278,7 +1278,6 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike: case ErrorCode.ERR_AnonDelegateCantUseStructPrimaryConstructorParameterInMember: case ErrorCode.ERR_AnonDelegateCantUseStructPrimaryConstructorParameterCaptured: - case ErrorCode.ERR_IllegalInnerUnsafe: case ErrorCode.ERR_BadYieldInCatch: case ErrorCode.ERR_BadDelegateLeave: case ErrorCode.WRN_IllegalPragma: @@ -2433,6 +2432,8 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_ParamsCollectionMissingConstructor: case ErrorCode.ERR_NoModifiersOnUsing: case ErrorCode.WRN_BadYieldInLock: + case ErrorCode.ERR_BadYieldInUnsafe: + case ErrorCode.ERR_AddressOfInIterator: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index cd3aec6946c79..d502a7080b83c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -58,7 +58,7 @@ public LocalFunctionSymbol( ScopeBinder = binder; - binder = binder.WithUnsafeRegionIfNecessary(syntax.Modifiers); + binder = binder.SetOrClearUnsafeRegionIfNecessary(syntax.Modifiers); if (syntax.TypeParameterList != null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index af93d12c934ff..eb6c3ff553bfa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -59,7 +59,7 @@ private static SourcePropertySymbol Create( bool isExpressionBodied = !hasAccessorList && GetArrowExpression(syntax) != null; - binder = binder.WithUnsafeRegionIfNecessary(modifiersTokenList); + binder = binder.SetOrClearUnsafeRegionIfNecessary(modifiersTokenList); TypeSymbol? explicitInterfaceType; string? aliasQualifierOpt; string memberName = ExplicitInterfaceHelpers.GetMemberNameAndInterfaceSymbol(binder, explicitInterfaceSpecifier, name, diagnostics, out explicitInterfaceType, out aliasQualifierOpt); @@ -431,7 +431,7 @@ private Binder CreateBinderForTypeAndParameters() var binderFactory = compilation.GetBinderFactory(syntaxTree); var binder = binderFactory.GetBinder(syntax, syntax, this); SyntaxTokenList modifiers = GetModifierTokensSyntax(syntax); - binder = binder.WithUnsafeRegionIfNecessary(modifiers); + binder = binder.SetOrClearUnsafeRegionIfNecessary(modifiers); return binder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index fc8ee36929979..8cfc786b84b88 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -32,6 +32,11 @@ {0}: abstraktní událost nemůže používat syntaxi přístupového objektu události. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees '&' pro skupiny metod se nedá použít ve stromech výrazů. @@ -302,6 +307,11 @@ Typ {0} není platný pro using static. Lze použít pouze třídu, strukturu, rozhraní, výčet, delegáta nebo obor názvů. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Atribut AsyncMethodBuilder je u anonymních metod bez explicitního návratového typu zakázaný. @@ -9161,11 +9171,6 @@ Blok catch() po bloku catch (System.Exception e) může zachytit výjimky, kter Parametr ref, out nebo in {0} nejde použít uvnitř anonymní metody, výrazu lambda, výrazu dotazu nebo lokální funkce. - - Unsafe code may not appear in iterators - Iterátory nesmí obsahovat nezabezpečený kód. - - Cannot yield a value in the body of a catch clause V těle klauzule catch nejde použít hodnotu získanou příkazem yield. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index d91808e6fc8ff..296db899ac590 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -32,6 +32,11 @@ {0}: Das abstrakte Ereignis kann die Ereignisaccessorsyntax nicht verwenden. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees "&" für Methodengruppen kann in Ausdrucksbaumstrukturen nicht verwendet werden. @@ -302,6 +307,11 @@ Der Typ "{0}" ist für "using static" ungültig. Nur eine Klasse, Struktur, Schnittstelle, Enumeration, ein Delegat oder ein Namespace kann verwendet werden. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Das AsyncMethodBuilder-Attribut ist für anonyme Methoden ohne expliziten Rückgabetyp unzulässig. @@ -9161,11 +9171,6 @@ Ein catch()-Block nach einem catch (System.Exception e)-Block kann nicht-CLS-Aus Der ref-, out-, oder in-Parameter "{0}" kann nicht in einer anonymen Methode, einem Lambdaausdruck, einem Abfrageausdruck oder einer lokalen Funktion verwendet werden. - - Unsafe code may not appear in iterators - Unsicherer Code wird möglicherweise nicht in Iteratoren angezeigt. - - Cannot yield a value in the body of a catch clause Mit "yield" kann im Text einer catch-Klausel kein Wert zurückgegeben werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 519a716d29c04..97696daf3fc40 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -32,6 +32,11 @@ "{0}": un evento abstracto no puede usar la sintaxis de descriptor de acceso de eventos + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees No se puede usar "&" para los grupos de métodos en los árboles de expresión. @@ -302,6 +307,11 @@ El tipo '{0}' no es válido para 'using static'. Solo se puede usar una clase, estructura, interfaz, enumeración, delegado o espacio de nombres. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. El atributo AsyncMethodBuilder no se permite en métodos anónimos sin un tipo de valor devuelto explícito. @@ -9161,11 +9171,6 @@ Un bloque catch() después de un bloque catch (System.Exception e) puede abarcar No se puede usar el parámetro ref, out o in "{0}" dentro de un método anónimo, una expresión lambda, una expresión de consulta o una función local - - Unsafe code may not appear in iterators - No puede aparecer código no seguro en iteradores - - Cannot yield a value in the body of a catch clause No se puede proporcionar ningún valor en el cuerpo de una cláusula catch diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index ceb81446301bf..6bcd5e346729f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -32,6 +32,11 @@ '{0}' : un événement abstrait ne peut pas utiliser une syntaxe d'accesseur d'événement + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees '&' des groupes de méthodes ne peut pas être utilisé dans les arborescences d'expression @@ -302,6 +307,11 @@ Le type '{0}' n'est pas valide pour 'en utilisant statique'. Seuls une classe, une structure, une interface, une énumération, un délégué ou un espace de noms peuvent être utilisés. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. L'attribut AsyncMethodBuilder n'est pas autorisé pour les méthodes anonymes sans type de retour explicite. @@ -9161,11 +9171,6 @@ Un bloc catch() après un bloc catch (System.Exception e) peut intercepter des e Impossible d'utiliser le paramètre ref, out ou in '{0}' dans une méthode anonyme, une expression lambda, une expression de requête ou une fonction locale - - Unsafe code may not appear in iterators - Du code unsafe ne peut pas s'afficher dans des itérateurs - - Cannot yield a value in the body of a catch clause Impossible de générer une valeur dans le corps d'une clause catch diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index f1568d3b31829..1fdd574f15cc6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -32,6 +32,11 @@ '{0}': l'evento astratto non può usare la sintassi della funzione di accesso agli eventi + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees Non è possibile usare '&' su gruppi di metodi in alberi delle espressioni @@ -302,6 +307,11 @@ '{0}' tipo non valido per 'using static'. È possibile usare solo una classe, una struttura, un'interfaccia, un'enumerazione, un delegato o uno spazio dei nomi. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. L'attributo AsyncMethodBuilder non è consentito in metodi anonimi senza un tipo restituito esplicito. @@ -9161,11 +9171,6 @@ Un blocco catch() dopo un blocco catch (System.Exception e) può rilevare eccezi Non è possibile usare il parametro ref, out o in '{0}' all'interno di un metodo anonimo, di un'espressione lambda, di un'espressione di query o di una funzione locale - - Unsafe code may not appear in iterators - Gli iteratori non possono contenere codice unsafe - - Cannot yield a value in the body of a catch clause Impossibile produrre un valore nel corpo di una clausola catch diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index e98478c3c0f65..3ab67482f019f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -32,6 +32,11 @@ '{0}': 抽象イベントはイベント アクセサーの構文を使用できません + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees メソッド グループの '&' を式ツリーで使用することはできません @@ -302,6 +307,11 @@ '{0}' 型は 'using static' では無効です。使用できるのは、クラス、構造体、インターフェイス、列挙型、デリゲート、名前空間のみです。 + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. AsyncMethodBuilder 属性は、明示的な戻り値の型のない匿名メソッドでは許可されていません。 @@ -9161,11 +9171,6 @@ AssemblyInfo.cs ファイルで RuntimeCompatibilityAttribute が false に設 ref、out、in パラメーター '{0}' は、匿名メソッド、ラムダ式、クエリ式、ローカル関数の内部では使用できません - - Unsafe code may not appear in iterators - アンセーフ コードは反復子には記述できません - - Cannot yield a value in the body of a catch clause catch 句の本体で値を生成することはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 7e87768b4bf62..af60b68bc3d07 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -32,6 +32,11 @@ '{0}': 추상 이벤트는 이벤트 접근자 구문을 사용할 수 없습니다. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees 식 트리에서는 메서드 그룹에 '&'를 사용할 수 없습니다. @@ -302,6 +307,11 @@ '{0}' 유형은 '정적 사용'에 유효하지 않습니다. 클래스, 구조체, 인터페이스, 열거형, 대리자 또는 네임스페이스만 사용할 수 있습니다. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. AsyncMethodBuilder 특성은 명시적 반환 형식이 없는 익명 메서드에서 허용되지 않습니다. @@ -9161,11 +9171,6 @@ catch (System.Exception e) 블록 뒤의 catch() 블록은 RuntimeCompatibilityA 무명 메서드, 람다 식, 쿼리 식 또는 로컬 함수 안에서는 ref, out 또는 in 매개 변수 '{0}'을(를) 사용할 수 없습니다. - - Unsafe code may not appear in iterators - 반복기에는 안전하지 않은 코드를 사용할 수 없습니다. - - Cannot yield a value in the body of a catch clause catch 절 본문에서는 값을 생성할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 7dcc59fdbd7f6..c9b6f65041b02 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -32,6 +32,11 @@ „{0}”: zdarzenie abstrakcyjne nie może używać składni metody dostępu zdarzenia + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees Znak „&” dla grup metod nie może być używany w drzewach wyrażeń @@ -302,6 +307,11 @@ Typ „{0}” jest nieprawidłowy dla „using static”. Można używać tylko klasy, struktury, interfejsu, wyliczenia, delegata lub przestrzeni nazw. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Atrybut AsyncMethodBuilder jest niedozwolony w metodach anonimowych bez jawnego zwracanego typu. @@ -9161,11 +9171,6 @@ Blok catch() po bloku catch (System.Exception e) może przechwytywać wyjątki n Nie można użyć parametru ref, out ani in „{0}” wewnątrz metody anonimowej, wyrażenia lambda, wyrażenia zapytania lub funkcji lokalnej - - Unsafe code may not appear in iterators - Niebezpieczny kod nie może występować w iteratorach. - - Cannot yield a value in the body of a catch clause Nie można użyć instrukcji yield z wartością w treści klauzuli catch. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 578dd52d42e00..2dba1c7baee45 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -32,6 +32,11 @@ '{0}': o evento abstrato não pode usar a sintaxe do acessador de eventos + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees '&' nos grupos de métodos não pode ser usado em árvores de expressão @@ -302,6 +307,11 @@ '{0}' tipo não é válido para 'using static'. Somente uma classe, struct, interface, enumeração, delegado ou namespace podem ser usados. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. O atributo AsyncMethodBuilder não é permitido em métodos anônimos sem um tipo de retorno explícito. @@ -9161,11 +9171,6 @@ Um bloco catch() depois de um bloco catch (System.Exception e) poderá capturar Não é possível usar os parâmetro ref, out ou in '{0}' dentro de um método anônimo, de uma expressão lambda de uma expressão de consulta ou de uma função local - - Unsafe code may not appear in iterators - Código sem segurança só pode aparecer em iteradores - - Cannot yield a value in the body of a catch clause Não é possível usar a instrução yield no corpo de uma cláusula catch diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 619ac7320a17b..12a8569d53702 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -32,6 +32,11 @@ "{0}": абстрактное событие не может использовать синтаксис метода доступа к событиям. + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees "&" в группах методов не может использоваться в деревьях выражений @@ -302,6 +307,11 @@ ' {0} ' недопустим для 'использования статики'. Можно использовать только класс, структуру, интерфейс, перечисление, делегат или пространство имен. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Атрибут AsyncMethodBuilder запрещен для анонимных методов без явного типа возвращаемого значения. @@ -9162,11 +9172,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Недопустимо использовать параметр "{0}" с модификаторами ref, out или in внутри анонимного метода, лямбда-выражения, выражения запроса или локальной функции - - Unsafe code may not appear in iterators - Небезопасный код не может использоваться в итераторах. - - Cannot yield a value in the body of a catch clause Нельзя использовать оператор yield в теле предложения catch. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 981f7741495aa..c2f2c01fd7e71 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -32,6 +32,11 @@ '{0}': soyut olay, olay erişeni söz dizimini kullanamaz + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees Metot gruplarındaki '&', ifade ağaçlarında kullanılamaz @@ -302,6 +307,11 @@ '{0}' türü 'using static' için geçerli değil. Yalnızca bir sınıf, yapı, arabirim, sabit liste, temsilci veya ad alanı kullanılabilir. + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. Açık dönüş türü olmadan, anonim yöntemlerde AsyncMethodBuilder özniteliğine izin verilmez. @@ -9161,11 +9171,6 @@ RuntimeCompatibilityAttribute AssemblyInfo.cs dosyasında false olarak ayarlanm Anonim metot, lambda ifadesi, sorgu ifadesi veya yerel işlev içinde '{0}' ref, out veya in parametresi kullanılamaz - - Unsafe code may not appear in iterators - Güvenli olmayan kod yineleyicilerde görünmeyebilir - - Cannot yield a value in the body of a catch clause Catch yan tümcesinin gövdesinde yield ile bir değer döndürülemez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 5aceeb57b426c..fe0e988c8bca5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -32,6 +32,11 @@ “{0}”: 抽象事件不可使用事件访问器语法 + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees 不可在表达式树中使用方法组上的 "&" @@ -302,6 +307,11 @@ “{0}”类型对于 "using static" 无效。只能使用类、结构、接口、枚举、委托或命名空间。 + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. 没有显式返回类型的匿名方法不允许使用 AsyncMethodBuilder 属性。 @@ -9161,11 +9171,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep 不能在匿名方法、lambda 表达式、查询表达式或本地函数中使用 ref、out 或 in 参数“{0}” - - Unsafe code may not appear in iterators - 迭代器中不能出现不安全的代码 - - Cannot yield a value in the body of a catch clause 无法在 catch 子句体中生成值 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 3b36564d36e33..f2f3842ce8508 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -32,6 +32,11 @@ '{0}' 抽象事件無法使用事件存取子語法 + + The '&' operator cannot be used on parameters or local variables in iterator methods. + The '&' operator cannot be used on parameters or local variables in iterator methods. + + '&' on method groups cannot be used in expression trees 不得在運算式樹狀架構中對方法群組使用 '&' @@ -302,6 +307,11 @@ '{0}' 類型對 'using static' 無效。只能使用類別、結構、介面、列舉、委派或命名空間。 + + Cannot use 'yield return' in an 'unsafe' block + Cannot use 'yield return' in an 'unsafe' block + + The AsyncMethodBuilder attribute is disallowed on anonymous methods without an explicit return type. 沒有明確傳回型別的匿名方法上不允許 AsyncMethodBuilder 屬性。 @@ -9161,11 +9171,6 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep 無法在匿名方法、Lambda 運算式、查詢運算式或區域函式中使用 ref、out 或 in 參數 '{0}' - - Unsafe code may not appear in iterators - Unsafe 程式碼不可出現在迭代器中 - - Cannot yield a value in the body of a catch clause 無法在 catch 子句主體中使用 yield 產生值 diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs index 4760ca1c2c254..c12ba96764e8a 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/NumericIntPtrTests.cs @@ -1359,20 +1359,43 @@ static IEnumerable F() yield return sizeof(System.UIntPtr); } }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70); - comp.VerifyDiagnostics( - // (6,22): error CS1629: Unsafe code may not appear in iterators + var expectedDiagnostics = new[] + { + // (6,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(nint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nint)").WithLocation(6, 22), - // (7,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 22), + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(nuint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nuint)").WithLocation(7, 22), - // (8,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nuint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22), + // (8,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(System.IntPtr); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(System.IntPtr)").WithLocation(8, 22), - // (9,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(System.IntPtr)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 22), + // (9,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return sizeof(System.UIntPtr); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(System.UIntPtr)").WithLocation(9, 22)); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(System.UIntPtr)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); + + expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 22), + // (7,22): error CS0233: 'nuint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nuint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nuint)").WithArguments("nuint").WithLocation(7, 22), + // (8,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(System.IntPtr); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(System.IntPtr)").WithArguments("nint").WithLocation(8, 22), + // (9,22): error CS0233: 'nuint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(System.UIntPtr); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(System.UIntPtr)").WithArguments("nuint").WithLocation(9, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, targetFramework: TargetFramework.Net70).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index d135ca3ae11dd..7e4c694e57ba1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -2394,21 +2394,52 @@ public unsafe IEnumerable M4(int* a) // (33,44): error CS1637: Iterators cannot have pointer type parameters // public unsafe IEnumerable M4(int* a) Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(33, 44), - // (33,36): error CS1629: Unsafe code may not appear in iterators + // (33,36): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // public unsafe IEnumerable M4(int* a) - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "M4").WithLocation(33, 36), - // (37,40): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "M4").WithArguments("ref and unsafe in async and iterator methods").WithLocation(33, 36), + // (37,40): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // IEnumerable Local(int* b) { yield break; } - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "int*").WithLocation(37, 40), - // (39,23): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(37, 40), + // (39,23): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Local(&x); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "&x").WithLocation(39, 23), - // (39,17): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(39, 23), + // (39,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // Local(&x); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Local(&x)").WithLocation(39, 17), + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Local(&x)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(39, 17), // (37,45): error CS1637: Iterators cannot have pointer type parameters // IEnumerable Local(int* b) { yield break; } Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "b").WithLocation(37, 45)); + + var expectedDiagnostics = new[] + { + // (8,37): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* a) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(8, 37), + // (17,41): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* x) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "x").WithLocation(17, 41), + // (27,37): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* a) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(27, 37), + // (33,44): error CS1637: Iterators cannot have pointer type parameters + // public unsafe IEnumerable M4(int* a) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "a").WithLocation(33, 44), + // (37,40): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // IEnumerable Local(int* b) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(37, 40), + // (37,45): error CS1637: Iterators cannot have pointer type parameters + // IEnumerable Local(int* b) { yield break; } + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "b").WithLocation(37, 45), + // (39,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Local(&x); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "Local(&x)").WithLocation(39, 17), + // (39,23): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // Local(&x); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "&x").WithLocation(39, 23) + }; + + CreateCompilation(src, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularNext.WithFeature("run-nullable-analysis", "never")).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(src, options: TestOptions.UnsafeDebugDll, parseOptions: TestOptions.RegularPreview.WithFeature("run-nullable-analysis", "never")).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs index 320f13951a7f9..07bd155f06f33 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NativeIntegerTests.cs @@ -4369,14 +4369,31 @@ static IEnumerable F() yield return sizeof(nuint); } }"; - var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( - // (6,22): error CS1629: Unsafe code may not appear in iterators + var expectedDiagnostics = new[] + { + // (6,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 22), + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // yield return sizeof(nuint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nuint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular9).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + + expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context // yield return sizeof(nint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nint)").WithLocation(6, 22), - // (7,22): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 22), + // (7,22): error CS0233: 'nuint' does not have a predefined size, therefore sizeof can only be used in an unsafe context // yield return sizeof(nuint); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(nuint)").WithLocation(7, 22)); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nuint)").WithArguments("nuint").WithLocation(7, 22) + }; + + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index e83c4cbb6776d..caaf6545e972f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -15272,35 +15272,48 @@ class C { IEnumerator IteratorMeth() { int i; - unsafe // CS1629 + unsafe { int *p = &i; yield return *p; } } - unsafe IEnumerator IteratorMeth2() { // CS1629 + unsafe IEnumerator IteratorMeth2() { yield break; } } "; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (7,7): error CS1629: Unsafe code may not appear in iterators - // unsafe // CS1629 - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe"), - // (9,10): error CS1629: Unsafe code may not appear in iterators + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (7,7): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 7), + // (9,10): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int *p = &i; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "int *"), - // (9,19): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int *").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 10), + // (9,19): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // int *p = &i; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "&i"), - // (10,24): error CS1629: Unsafe code may not appear in iterators + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&i").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 19), + // (10,24): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // yield return *p; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "p"), - // (14,29): error CS1629: Unsafe code may not appear in iterators - // unsafe IEnumerator IteratorMeth2() { // CS1629 - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "IteratorMeth2") + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 24), + // (14,29): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe IEnumerator IteratorMeth2() { + Diagnostic(ErrorCode.ERR_FeatureInPreview, "IteratorMeth2").WithArguments("ref and unsafe in async and iterator methods").WithLocation(14, 29) ); + + var expectedDiagnostics = new[] + { + // (9,20): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &i; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "i").WithLocation(9, 20), + // (10,10): error CS9231: Cannot use 'yield return' in an 'unsafe' block + // yield return *p; + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(10, 10) + }; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 12753c86accd8..2c2a8d1f423e4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -275,67 +275,2120 @@ unsafe System.Collections.Generic.IEnumerator Goo() } "; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,56): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Goo")); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerator Goo() + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Goo").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void IteratorUnsafe3() + { + var text = @" +class C +{ + System.Collections.Generic.IEnumerator Goo() + { + unsafe { } + yield return 1; + } +} +"; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 9)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void IteratorUnsafe4() + { + var text = @" +unsafe class C +{ + System.Collections.Generic.IEnumerator Goo() + { + unsafe { } + yield return 1; + } +} +"; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 9)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [WorkItem(546657, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546657")] + [Fact] + public void IteratorUnsafe5() + { + var text = @" +unsafe class C +{ + System.Collections.Generic.IEnumerator Goo() + { + System.Action a = () => { unsafe { } }; + yield return 1; + } +} +"; + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,35): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // System.Action a = () => { unsafe { } }; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 35)); + + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73280")] + public void Iterator_UnsafeBlock_LangVersion() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M1() + { + unsafe // langversion error in C# 12 + { + int* p = null; // unnecessary langversion error in C# 12 + } + yield break; + } + System.Collections.Generic.IEnumerable M2() + { + int* p = null; // necessary langversion error in C# 12 + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe // langversion error in C# 12 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9), + // (7,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int* p = null; // unnecessary langversion error in C# 12 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 13), + // (13,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int* p = null; // necessary langversion error in C# 12 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(13, 9)); + + var expectedDiagnostics = new[] + { + // (13,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int* p = null; // necessary langversion error in C# 12 + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(13, 9) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Local() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + int x = 1; + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - diagnostics inside the unsafe block are unnecessary + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (6,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 9), + // (8,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int *").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 13), + // (8,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 22), + // (9,14): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 14), + // (9,19): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(9, 19)); + + var expectedDiagnostics = new[] + { + // (8,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(8, 23) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Parameter() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M(int x) + { + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - diagnostics inside the unsafe block are unnecessary + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9), + // (7,13): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int *").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 13), + // (7,22): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int *p = &x; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "&x").WithArguments("ref and unsafe in async and iterator methods").WithLocation(7, 22), + // (8,14): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 14), + // (8,19): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // *p = *p + 1; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "p").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 19)); + + var expectedDiagnostics = new[] + { + // (7,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(7, 23) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_01() + { + var code = """ + class Program + { + static int _f; + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + fixed (int* p = &_f) { } + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_02() + { + var code = """ + #pragma warning disable CS0649 // field is never assigned to + unsafe class Program + { + static int* _p; + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + int* p = _p; + p = &p[1]; + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_03() + { + var code = """ + struct S + { + public int F; + } + class Program + { + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + S s = default; + int* p = &s.F; + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (12,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int* p = &s.F; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "s.F").WithLocation(12, 23)); + } + + [Fact] + public void Iterator_UnsafeBlock_Field_04() + { + var code = """ + class Program + { + static int _f; + static System.Collections.Generic.IEnumerable F() + { + unsafe + { + int* p = &_f; + (*p)++; + } + yield break; + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (8,22): error CS0212: You can only take the address of an unfixed expression inside of a fixed statement initializer + // int* p = &_f; + Diagnostic(ErrorCode.ERR_FixedNeeded, "&_f").WithLocation(8, 22)); + } + + [Fact] + public void Iterator_UnsafeBlock_SizeOf() + { + var code = """ + foreach (var x in new C().M()) System.Console.Write(x); + + class C + { + public System.Collections.Generic.IEnumerable M() + { + int x; + unsafe + { + x = sizeof(nint); + } + yield return x; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - diagnostics inside the unsafe block are unnecessary + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (8,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(8, 9), + // (10,17): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // x = sizeof(nint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(10, 17)); + + var expectedOutput = IntPtr.Size.ToString(); + CompileAndVerify(code, expectedOutput: expectedOutput, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CompileAndVerify(code, expectedOutput: expectedOutput, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_YieldBreak() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + unsafe + { + yield break; + } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void Iterator_UnsafeBlock_YieldReturn() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + unsafe + { + yield return 1; + } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe + Diagnostic(ErrorCode.ERR_FeatureInPreview, "unsafe").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + var expectedDiagnostics = new[] + { + // (7,13): error CS9231: Cannot use 'yield return' in an 'unsafe' block + // yield return 1; + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(7, 13) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_UnsafeBlock_Async_01() + { + var code = """ + await foreach (var x in new C().M()) System.Console.Write(x); + + class C + { + public async System.Collections.Generic.IAsyncEnumerable M() + { + int x = 1; + await System.Threading.Tasks.Task.Yield(); + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + } + } + """ + AsyncStreamsTypes; + + var comp = CreateCompilationWithTasksExtensions(code, options: TestOptions.UnsafeReleaseExe); + CompileAndVerify(comp, expectedOutput: "1", verify: Verification.Fails).VerifyDiagnostics( + // (11,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(11, 23)); + } + + // Should behave equivalently to AwaitBetweenUnsafeBlocks. + [Fact] + public void Iterator_UnsafeBlock_Async_02() + { + var code = """ + class C + { + public System.Collections.Generic.IEnumerable M() + { + int x = 1; + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + unsafe + { + int *p = &x; + *p = *p + 1; + } + yield return x; + unsafe + { + int *p = &x; + if (*p == 3) yield break; + } + yield return x; + } + } + """; + + var expectedDiagnostics = new[] + { + // (8,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(8, 23), + // (14,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(14, 23), + // (20,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // int *p = &x; + Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(20, 23) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + // Should behave equivalently to Iterator_UnsafeBlock_Async_02. + [Fact] + public void AwaitBetweenUnsafeBlocks() + { + var code = """ + using System; + using System.Threading.Tasks; + + await new C().M(); + + class C + { + async Task Report(int x) + { + Console.Write(x); + await Task.Yield(); + } + public async Task M() + { + int x = 1; + unsafe + { + int *p = &x; + *p = *p + 1; + } + await Report(x); + unsafe + { + int *p = &x; + *p = *p + 1; + } + await Report(x); + unsafe + { + int *p = &x; + if (*p == 3) return; + } + await Report(x); + } + } + """; + var expectedDiagnostics = new[] + { + // (18,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(18, 23), + // (24,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(24, 23), + // (30,23): warning CS9123: The '&' operator should not be used on parameters or local variables in async methods. + // int *p = &x; + Diagnostic(ErrorCode.WRN_AddressOfInAsync, "x").WithLocation(30, 23) + }; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Iterator_LocalFunction_Nested() + { + var code = """ + class Program + { + static void F() + { + unsafe + { + static System.Collections.Generic.IEnumerable G(int x) + { + unsafe + { + x = sizeof(nint); + } + yield return x; + + unsafe + { + G2(new int*[0]); + static System.Collections.Generic.IEnumerable G2(int*[] x) + { + int y; + unsafe + { + y = *x[0]; + } + yield return y; + } + } + } + G(0); + } + } + } + """; + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LambdaInIterator_Async() + { + var code = """ + using System.Collections.Generic; + using System.Threading.Tasks; + unsafe class C + { + IEnumerable M() + { + yield return 1; + var lam = async () => await Task.Yield(); + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (8,31): error CS4004: Cannot await in an unsafe context + // var lam = async () => await Task.Yield(); + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(8, 31)); + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_Async() + { + var code = """ + using System.Collections.Generic; + using System.Threading.Tasks; + unsafe class C + { + IEnumerable M() + { + yield return 1; + local(); + async void local() { await Task.Yield(); } + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - these should ideally be langversion errors + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (9,30): error CS4004: Cannot await in an unsafe context + // async void local() { await Task.Yield(); } + Diagnostic(ErrorCode.ERR_AwaitInUnsafeContext, "await Task.Yield()").WithLocation(9, 30)); + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact] + public void UnsafeContext_LambdaInIterator_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + var lam = () => sizeof(nint); + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (6,25): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lam = () => sizeof(nint); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "sizeof(nint)").WithArguments("ref and unsafe in async and iterator methods").WithLocation(6, 25)); + + var expectedDiagnostics = new[] + { + // (6,25): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // var lam = () => sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 25) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + local(); + void local() { int* p = null; } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + + var expectedDiagnostics = new[] + { + // (7,24): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void local() { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(7, 24) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunctionInIterator_Unsafe_BreakingChangeWorkaround() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return 1; + local(); + unsafe void local() { int* p = null; } + } + } + """; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73280")] + public void UnsafeContext_LangVersion() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + int* p = null; + yield break; + } + } + """; + + // https://github.com/dotnet/roslyn/issues/73280 - should not be a langversion error since this remains an error in C# 13 + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // int* p = null; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "int*").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + var expectedDiagnostics = new[] + { + // (5,9): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int* p = null; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(5, 9) + }; + + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Signature_Unsafe(bool unsafeClass, bool unsafeMethod) + { + if (!unsafeClass && !unsafeMethod) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} void M(int* p) { } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Signature_Safe() + { + var code = """ + class C + { + void M(int* p) { } + } + """; + + var expectedDiagnostics = new[] + { + // (3,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void M(int* p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 12) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Body_Unsafe(bool unsafeClass, bool unsafeMethod) + { + if (!unsafeClass && !unsafeMethod) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} int M() + { + return sizeof(nint); + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Body_Safe() + { + var code = """ + class C + { + int M() + { + return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,16): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 16) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Iterator_Signature_UnsafeMethod(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + unsafe System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (3,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "M").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 56)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Signature_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,51): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 51) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22)); + } + + [Fact] + public void UnsafeContext_Method_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable M() + { + yield return local(); + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Method_Iterator_Body_CSharp13(bool unsafeClass, bool unsafeMethod) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeMethod ? "unsafe" : "")}} System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Signature_Unsafe(bool unsafeClass, bool unsafeOperator) + { + if (!unsafeClass && !unsafeOperator) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeOperator ? "unsafe" : "")}} public static C operator+(C c, int* p) + { + return c; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Operator_Signature_Safe() + { + var code = """ + class C + { + public static C operator+(C c, int* p) + { + return c; + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,36): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // public static C operator+(C c, int* p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 36) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Body_Unsafe(bool unsafeClass, bool unsafeOperator) + { + if (!unsafeClass && !unsafeOperator) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeOperator ? "unsafe" : "")}} public static int operator+(C c1, C c2) + { + return sizeof(nint); + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Operator_Body_Safe() + { + var code = """ + class C + { + public static int operator+(C c1, C c2) + { + return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,16): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 16) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + public static System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Iterator_Signature_UnsafeOperator(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + public static unsafe System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (3,78): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static unsafe System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "+").WithArguments("ref and unsafe in async and iterator methods").WithLocation(3, 78)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Signature_Safe() + { + var code = """ + class C + { + public static System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + { + yield break; + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,78): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // public static System.Collections.Generic.IEnumerable operator+(C c, int*[] p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 78) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22)); + } + + [Fact] + public void UnsafeContext_Operator_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return local(); + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Operator_Iterator_Body_CSharp13(bool unsafeClass, bool unsafeIndexer) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} public static System.Collections.Generic.IEnumerable operator+(C c1, C c2) + { + yield return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Signature_Unsafe(bool unsafeClass, bool unsafeIndexer) + { + if (!unsafeClass && !unsafeIndexer) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} int this[int* p] + { + get { return 0; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Indexer_Signature_Safe() + { + var code = """ + class C + { + int this[int* p] + { + get { return 0; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,14): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int this[int* p] + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 14) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Body_Unsafe(bool unsafeClass, bool unsafeIndexer) + { + if (!unsafeClass && !unsafeIndexer) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} int this[int x] + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Indexer_Body_Safe() + { + var code = """ + class C + { + int this[int x] + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable this[int*[] p] + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Iterator_Signature_UnsafeIndexer(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + unsafe System.Collections.Generic.IEnumerable this[int*[] p] + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // get { yield break; } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "get").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Signature_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable this[int*[] p] + { + get { yield break; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,54): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable this[int*[] p] + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 54) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable this[int x] + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15)); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable this[int x] + { + get + { + yield return local(); + int local() => sizeof(nint); + } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Indexer_Iterator_Body_CSharp13_UnsafeSetter(bool unsafeClass, bool unsafeIndexer) + { + if (!unsafeClass && !unsafeIndexer) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeIndexer ? "unsafe" : "")}} System.Collections.Generic.IEnumerable this[int x] + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Indexer_Iterator_Body_CSharp13_SafeSetter() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable this[int x] + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Signature_Unsafe(bool unsafeClass, bool unsafeProperty) + { + if (!unsafeClass && !unsafeProperty) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeProperty ? "unsafe" : "")}} int* P + { + get { throw null; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Property_Signature_Safe() + { + var code = """ + class C + { + int* P + { + get { throw null; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // int* P + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 5) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Body_Unsafe(bool unsafeClass, bool unsafeProperty) + { + if (!unsafeClass && !unsafeProperty) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeProperty ? "unsafe" : "")}} int P + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Property_Body_Safe() + { + var code = """ + class C + { + int P + { + get { return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 22), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Signature_UnsafeClass() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable P + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Iterator_Signature_UnsafeProperty(bool unsafeClass) + { + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + unsafe System.Collections.Generic.IEnumerable P + { + get { yield break; } + set { } + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,9): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // get { yield break; } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "get").WithArguments("ref and unsafe in async and iterator methods").WithLocation(5, 9)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Signature_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable P + { + get { yield break; } + set { } + } + } + """; + + var expectedDiagnostics = new[] + { + // (3,44): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable P + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(3, 44) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Body_CSharp12_Safe() + { + var code = """ + class C + { + System.Collections.Generic.IEnumerable P + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15)); + } + + [Fact] + public void UnsafeContext_Property_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + unsafe class C + { + System.Collections.Generic.IEnumerable P + { + get + { + yield return local(); + int local() => sizeof(nint); + } + set { int* p = null; } + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_Property_Iterator_Body_CSharp13_UnsafeSetter(bool unsafeClass, bool unsafeProperty) + { + if (!unsafeClass && !unsafeProperty) + { + return; + } + + var code = $$""" + {{(unsafeClass ? "unsafe" : "")}} class C + { + {{(unsafeProperty ? "unsafe" : "")}} System.Collections.Generic.IEnumerable P + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; + + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] - public void IteratorUnsafe3() + public void UnsafeContext_Property_Iterator_Body_CSharp13_SafeSetter() { - var text = @" -class C -{ - System.Collections.Generic.IEnumerator Goo() - { - unsafe { } - yield return 1; - } -} -"; + var code = """ + class C + { + System.Collections.Generic.IEnumerable P + { + get { yield return sizeof(nint); } + set { int* p = null; } + } + } + """; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe")); + var expectedDiagnostics = new[] + { + // (5,28): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // get { yield return sizeof(nint); } + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(5, 28), + // (6,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // set { int* p = null; } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(6, 15) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Signature_Unsafe(bool unsafeBlock, bool unsafeFunction) + { + if (!unsafeBlock && !unsafeFunction) + { + return; + } + + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + {{(unsafeFunction ? "unsafe" : "")}} void M(int* p) { } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); } [Fact] - public void IteratorUnsafe4() + public void UnsafeContext_LocalFunction_Signature_Safe() { - var text = @" -unsafe class C -{ - System.Collections.Generic.IEnumerator Goo() - { - unsafe { } - yield return 1; - } -} -"; + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + void M(int* p) { } + """; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe")); + var expectedDiagnostics = new[] + { + // (2,8): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // void M(int* p) { } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 8) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Body_Unsafe(bool unsafeBlock, bool unsafeFunction) + { + if (!unsafeBlock && !unsafeFunction) + { + return; + } + + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + {{(unsafeFunction ? "unsafe" : "")}} int M() + { + return sizeof(nint); + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); } - [WorkItem(546657, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546657")] [Fact] - public void IteratorUnsafe5() + public void UnsafeContext_LocalFunction_Body_Safe() { - var text = @" -unsafe class C -{ - System.Collections.Generic.IEnumerator Goo() - { - System.Action a = () => { unsafe { } }; - yield return 1; - } -} -"; + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + int M() + { + return sizeof(nint); + } + """; - CreateCompilation(text, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (6,9): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "unsafe")); + var expectedDiagnostics = new[] + { + // (4,12): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(4, 12) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Signature_UnsafeBlock() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + unsafe + { + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Iterator_Signature_UnsafeFunction(bool unsafeBlock) + { + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + unsafe System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "M").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Signature_Safe() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + System.Collections.Generic.IEnumerable M(int*[] p) + { + yield break; + } + """; + + var expectedDiagnostics = new[] + { + // (2,47): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // System.Collections.Generic.IEnumerable M(int*[] p) + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(2, 47) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Body_CSharp12_Safe() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics( + // (4,18): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(4, 18)); + } + + [Fact] + public void UnsafeContext_LocalFunction_Iterator_Body_CSharp12_Unsafe() + { + var code = """ + #pragma warning disable CS8321 // The local function 'M' is declared but never used + unsafe + { + System.Collections.Generic.IEnumerable M() + { + yield return local(); + int local() => sizeof(nint); + } + } + """; + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void UnsafeContext_LocalFunction_Iterator_Body_CSharp13(bool unsafeBlock, bool unsafeFunction) + { + var code = $$""" + #pragma warning disable CS8321 // The local function 'M' is declared but never used + {{(unsafeBlock ? "unsafe" : "")}} + { + {{(unsafeFunction ? "unsafe" : "")}} System.Collections.Generic.IEnumerable M() + { + yield return sizeof(nint); + } + } + """; + + var expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(6, 22) + }; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Signature_Unsafe() + { + var code = """ + unsafe + { + var lam = (int* p) => { }; + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Lambda_Signature_Safe() + { + var code = """ + var lam = (int* p) => { }; + """; + + var expectedDiagnostics = new[] + { + // (1,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int* p) => { }; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 12), + // (1,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int* p) => { }; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(1, 17) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Body_Unsafe() + { + var code = """ + unsafe + { + var lam = () => + { + return sizeof(nint); + }; + } + """; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(); + } + + [Fact] + public void UnsafeContext_Lambda_Body_Safe() + { + var code = """ + var lam = () => + { + return sizeof(nint); + }; + """; + + var expectedDiagnostics = new[] + { + // (3,12): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(3, 12) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Signature_Unsafe() + { + var code = """ + unsafe + { + var lam = (int*[] p) => + { + yield break; + }; + } + """; + + var expectedDiagnostics = new[] + { + // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield break; + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Signature_Safe() + { + var code = """ + var lam = (int*[] p) => + { + yield break; + }; + """; + + var expectedDiagnostics = new[] + { + // (1,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int*[] p) => + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(1, 12), + // (1,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // var lam = (int*[] p) => + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "p").WithLocation(1, 19), + // (3,5): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield break; + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(3, 5) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Body_Unsafe() + { + var code = """ + unsafe + { + var lam = () => + { + yield return sizeof(nint); + }; + } + """; + + var expectedDiagnostics = new[] + { + // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + + expectedDiagnostics = [ + // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9), + // (5,9): error CS9231: Cannot use 'yield return' in an 'unsafe' block + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(5, 9) + ]; + + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void UnsafeContext_Lambda_Iterator_Body_Safe() + { + var code = """ + var lam = () => + { + yield return sizeof(nint); + }; + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(3, 5), + // (3,18): error CS0233: 'nint' does not have a predefined size, therefore sizeof can only be used in an unsafe context + // yield return sizeof(nint); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(nint)").WithArguments("nint").WithLocation(3, 18) + }; + + CreateCompilation(code, parseOptions: TestOptions.Regular12, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, parseOptions: TestOptions.RegularNext, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(code, options: TestOptions.UnsafeReleaseExe).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -936,18 +2989,41 @@ public void UnsafeIteratorSignatures() Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p")); var withUnsafeOnMembers = string.Format(template, "", "unsafe"); - CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,64): error CS1637: Iterators cannot have pointer type parameters - Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p"), - // (4,56): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator")); //this is for putting "unsafe" on an iterator, not for the parameter type + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70), + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); //this is for putting "unsafe" on an iterator, not for the parameter type + + var expectedDiagnostics = new[] + { + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70) + }; + + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); var withUnsafeOnTypeAndMembers = string.Format(template, "unsafe", "unsafe"); - CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,64): error CS1637: Iterators cannot have pointer type parameters - Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p"), - // (4,56): error CS1629: Unsafe code may not appear in iterators - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator")); //this is for putting "unsafe" on an iterator, not for the parameter type + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70), + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56)); //this is for putting "unsafe" on an iterator, not for the parameter type + + expectedDiagnostics = [ + // (4,70): error CS1637: Iterators cannot have pointer type parameters + // unsafe System.Collections.Generic.IEnumerable Iterator(int* p) + Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 70) + ]; + + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -974,18 +3050,24 @@ public void UnsafeIteratorSignatures_PointerArray() CreateCompilation(withUnsafeOnType, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics(); var withUnsafeOnMembers = string.Format(template, "", "unsafe"); - CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,56): error CS1629: Unsafe code may not appear in iterators + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe System.Collections.Generic.IEnumerable Iterator(int*[] p) - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator").WithLocation(4, 56) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56) ); + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(withUnsafeOnMembers, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); + var withUnsafeOnTypeAndMembers = string.Format(template, "unsafe", "unsafe"); - CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (4,56): error CS1629: Unsafe code may not appear in iterators + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.Regular12).VerifyDiagnostics( + // (4,56): error CS8652: The feature 'ref and unsafe in async and iterator methods' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // unsafe System.Collections.Generic.IEnumerable Iterator(int*[] p) - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator").WithLocation(4, 56) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Iterator").WithArguments("ref and unsafe in async and iterator methods").WithLocation(4, 56) ); + + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll, parseOptions: TestOptions.RegularNext).VerifyEmitDiagnostics(); + CreateCompilation(withUnsafeOnTypeAndMembers, options: TestOptions.UnsafeReleaseDll).VerifyEmitDiagnostics(); } [Fact] @@ -8156,10 +10238,17 @@ System.Collections.Generic.IEnumerable M() } } "; - CreateCompilation(text).VerifyDiagnostics( - // (6,22): error CS1629: Unsafe code may not appear in iterators + + var expectedDiagnostics = new[] + { + // (6,22): error CS0233: 'S' does not have a predefined size, therefore sizeof can only be used in an unsafe context // yield return sizeof(S); - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "sizeof(S)")); + Diagnostic(ErrorCode.ERR_SizeofUnsafe, "sizeof(S)").WithArguments("S").WithLocation(6, 22) + }; + + CreateCompilation(text, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text).VerifyDiagnostics(expectedDiagnostics); } [Fact] @@ -8522,10 +10611,17 @@ System.Collections.Generic.IEnumerable M() } } "; - CreateCompilation(text).VerifyDiagnostics( - // (6,17): error CS1629: Unsafe code may not appear in iterators + + var expectedDiagnostics = new[] + { + // (6,17): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context // var p = stackalloc int[1]; - Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "stackalloc int[1]")); + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "stackalloc int[1]").WithLocation(6, 17) + }; + + CreateCompilation(text, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(text).VerifyDiagnostics(expectedDiagnostics); } [Fact] From c861f39175b3dac6a00a9cfb3d9aab45eb03c182 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 2 May 2024 12:27:17 +0200 Subject: [PATCH 013/423] Renumber error codes --- .../CSharp/Warnversion Warning Waves.md | 2 +- .../CSharp/Portable/Errors/ErrorCode.cs | 6 +++--- .../EditAndContinueStateMachineTests.cs | 2 +- .../CSharp/Test/Emit2/Semantics/LockTests.cs | 4 ++-- .../Test/Semantic/Semantics/IteratorTests.cs | 8 ++++---- .../Semantic/Semantics/SemanticErrorTests.cs | 4 ++-- .../Test/Semantic/Semantics/UnsafeTests.cs | 16 ++++++++-------- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/compilers/CSharp/Warnversion Warning Waves.md b/docs/compilers/CSharp/Warnversion Warning Waves.md index fc070915f27bf..f80c80e658838 100644 --- a/docs/compilers/CSharp/Warnversion Warning Waves.md +++ b/docs/compilers/CSharp/Warnversion Warning Waves.md @@ -19,7 +19,7 @@ The compiler shipped with .NET 9 (the C# 13 compiler) contains the following war | Warning ID | Description | |------------|-------------| -| CS9230 | ['yield return' should not be used in the body of a lock statement](https://github.com/dotnet/roslyn/issues/72443) | +| CS9237 | ['yield return' should not be used in the body of a lock statement](https://github.com/dotnet/roslyn/issues/72443) | ## Warning level 8 diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 7f65516069734..4236f8143cb58 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2304,9 +2304,9 @@ internal enum ErrorCode #endregion - WRN_BadYieldInLock = 9230, - ERR_BadYieldInUnsafe = 9231, - ERR_AddressOfInIterator = 9232, + WRN_BadYieldInLock = 9237, + ERR_BadYieldInUnsafe = 9238, + ERR_AddressOfInIterator = 9239, // Note: you will need to do the following after adding warnings: // 1) Re-generate compiler code (eng\generate-compiler-code.cmd). diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 4119d42f5590f..28acddad8aec5 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -5464,7 +5464,7 @@ static IEnumerable F() var v0 = CompileAndVerify(compilation0); v0.VerifyDiagnostics( - // (17,34): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (17,34): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return 1; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(17, 34)); var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs index deb134acef381..61614ef54b64e 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/LockTests.cs @@ -3449,7 +3449,7 @@ IEnumerable M() // (9,15): error CS4007: Instance of type 'System.Threading.Lock.Scope' cannot be preserved across 'await' or 'yield' boundary. // lock (new Lock()) Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(9, 15), - // (11,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (11,13): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return 2; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(11, 13)); } @@ -3561,7 +3561,7 @@ async IAsyncEnumerable M() // (10,15): error CS4007: Instance of type 'System.Threading.Lock.Scope' cannot be preserved across 'await' or 'yield' boundary. // lock (new Lock()) Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "new Lock()").WithArguments("System.Threading.Lock.Scope").WithLocation(10, 15), - // (12,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (12,13): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return 2; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(12, 13)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs index eb0310f80ca82..06055448cc8b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs @@ -149,7 +149,7 @@ private async IAsyncEnumerable GetValuesAsync() var expectedDiagnostics = new[] { - // (23,17): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (23,17): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return i; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(23, 17) }; @@ -213,7 +213,7 @@ static IEnumerable GetValues(object obj) var expectedDiagnostics = new[] { - // (24,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (24,13): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return i; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(24, 13) }; @@ -260,10 +260,10 @@ IEnumerable local() """; CreateCompilation(source).VerifyDiagnostics( - // (10,13): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (10,13): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return 2; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(10, 13), - // (20,21): warning CS9230: 'yield return' should not be used in the body of a lock statement + // (20,21): warning CS9237: 'yield return' should not be used in the body of a lock statement // yield return 4; Diagnostic(ErrorCode.WRN_BadYieldInLock, "yield").WithLocation(20, 21)); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index caaf6545e972f..7842b80cbe8b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -15304,10 +15304,10 @@ unsafe IEnumerator IteratorMeth2() { var expectedDiagnostics = new[] { - // (9,20): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (9,20): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int *p = &i; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "i").WithLocation(9, 20), - // (10,10): error CS9231: Cannot use 'yield return' in an 'unsafe' block + // (10,10): error CS9238: Cannot use 'yield return' in an 'unsafe' block // yield return *p; Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(10, 10) }; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs index 2c2a8d1f423e4..b34a35cf0c104 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs @@ -437,7 +437,7 @@ public System.Collections.Generic.IEnumerable M() var expectedDiagnostics = new[] { - // (8,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (8,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int *p = &x; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(8, 23) }; @@ -484,7 +484,7 @@ public System.Collections.Generic.IEnumerable M(int x) var expectedDiagnostics = new[] { - // (7,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (7,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int *p = &x; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(7, 23) }; @@ -557,7 +557,7 @@ static System.Collections.Generic.IEnumerable F() } """; CreateCompilation(code, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( - // (12,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (12,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int* p = &s.F; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "s.F").WithLocation(12, 23)); } @@ -668,7 +668,7 @@ public System.Collections.Generic.IEnumerable M() var expectedDiagnostics = new[] { - // (7,13): error CS9231: Cannot use 'yield return' in an 'unsafe' block + // (7,13): error CS9238: Cannot use 'yield return' in an 'unsafe' block // yield return 1; Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(7, 13) }; @@ -740,13 +740,13 @@ public System.Collections.Generic.IEnumerable M() var expectedDiagnostics = new[] { - // (8,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (8,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int *p = &x; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(8, 23), - // (14,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (14,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int *p = &x; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(14, 23), - // (20,23): error CS9232: The '&' operator cannot be used on parameters or local variables in iterator methods. + // (20,23): error CS9239: The '&' operator cannot be used on parameters or local variables in iterator methods. // int *p = &x; Diagnostic(ErrorCode.ERR_AddressOfInIterator, "x").WithLocation(20, 23) }; @@ -2357,7 +2357,7 @@ public void UnsafeContext_Lambda_Iterator_Body_Unsafe() // (5,9): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression // yield return sizeof(nint); Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 9), - // (5,9): error CS9231: Cannot use 'yield return' in an 'unsafe' block + // (5,9): error CS9238: Cannot use 'yield return' in an 'unsafe' block // yield return sizeof(nint); Diagnostic(ErrorCode.ERR_BadYieldInUnsafe, "yield").WithLocation(5, 9) ]; From 830d23800b3ed031079db41de85c2a4efe064b2c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 2 May 2024 12:34:29 +0000 Subject: [PATCH 014/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From b292a51d13e020e350366ac305706367cd9db5c2 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 24 Apr 2024 10:58:02 -0700 Subject: [PATCH 015/423] Switch Roslyn language server to using System.Text.Json --- .../AbstractInProcLanguageClient.cs | 14 +-- .../AbstractLanguageServerProtocolTests.cs | 27 ++--- .../LspFileChangeWatcherTests.cs | 7 +- .../AbstractLanguageServerHostTests.cs | 3 +- .../ServiceBrokerConnectHandler.cs | 5 +- .../FileWatching/LspContractTypes.cs | 18 ++- .../HostWorkspace/OpenProjectsHandler.cs | 5 +- .../HostWorkspace/OpenSolutionHandler.cs | 9 +- .../HostWorkspace/ProjectDependencyHelper.cs | 5 +- .../ProjectLoadTelemetryEvent.cs | 21 ++-- .../RazorDynamicFileInfoProvider.cs | 11 +- .../ProjectDebugConfiguration.cs | 16 ++- .../WorkspaceDebugConfigurationParams.cs | 6 +- .../Handler/Restore/RestoreParams.cs | 10 +- .../Handler/Restore/RestorePartialResult.cs | 7 +- .../LanguageServer/LanguageServerHost.cs | 14 ++- .../Logging/ShowToastNotification.cs | 9 +- .../NamedPipeInformation.cs | 5 +- .../Program.cs | 4 +- .../ExampleLanguageServer.cs | 6 +- .../RequestExecutionQueueTests.cs | 2 +- .../TestExampleLanguageServer.cs | 16 +-- .../AbstractLanguageServer.cs | 85 +++++--------- ...eServerProtocol.Framework.Shared.projitems | 2 + .../NewtonsoftLanguageServer.cs | 102 +++++++++++++++++ .../SystemTextJsonLanguageServer.cs | 107 ++++++++++++++++++ .../CSharpVisualBasicLanguageServerFactory.cs | 6 +- .../Extensions/ProtocolConversions.cs | 15 +++ .../Razor/FormatNewFileParams.cs | 9 +- .../Razor/SemanticTokensRangesParams.cs | 4 +- .../Razor/SimplifyMethodParams.cs | 7 +- .../Diagnostics/DiagnosticAnalyzerService.cs | 1 + .../CodeActionFixAllResolveHandler.cs | 15 ++- .../CodeActions/CodeActionResolveHandler.cs | 15 ++- .../CodeLens/CodeLensResolveHandler.cs | 6 +- .../Handler/Completion/CompletionHandler.cs | 3 +- .../Completion/CompletionResolveHandler.cs | 6 +- ...dChangeConfigurationNotificationHandler.cs | 10 +- .../BuildOnlyDiagnosticIdsHandler.cs | 5 +- .../RegisterSolutionSnapshotHandler.cs | 4 +- .../InlayHint/InlayHintResolveHandler.cs | 14 +-- .../ServerLifetime/InitializeHandler.cs | 4 +- .../Handler/Symbols/RoslynDocumentSymbol.cs | 9 +- .../Handler/Testing/DebugAttachParams.cs | 5 +- .../Handler/Testing/DebugAttachResult.cs | 5 +- .../Handler/Testing/RunTestsParams.cs | 16 ++- .../Handler/Testing/RunTestsPartialResult.cs | 9 +- .../Protocol/Handler/Testing/TestProgress.cs | 11 +- .../Protocol/ILanguageServerFactory.cs | 4 +- .../JsonConverterCollectionUtilities.cs | 19 ++++ .../Converters/ContainerElementConverter.cs | 2 +- .../Converters/DropProgressConverter.cs | 52 +++++++++ .../Converters/ImageElementConverter.cs | 4 +- .../Internal/Text/ClassifiedTextElement.cs | 3 + .../Internal/Text/ClassifiedTextRun.cs | 3 + .../Internal/Text/ContainerElement.cs | 5 +- .../Internal/Text/ContainerElementStyle.cs | 2 +- .../Protocol/Internal/Text/ImageElement.cs | 4 + .../Protocol/Internal/Text/ImageId.cs | 5 +- .../Internal/VSInternalCompletionList.cs | 2 +- .../VSInternalCompletionListSetting.cs | 4 +- .../Protocol/Internal/VSInternalHover.cs | 1 - .../VSInternalInlineCompletionContext.cs | 1 - .../VSInternalInlineCompletionItem.cs | 1 - .../VSInternalInlineCompletionList.cs | 1 - .../VSInternalInlineCompletionRequest.cs | 1 - .../Internal/VSInternalReferenceParams.cs | 1 - .../VSInternalSelectedCompletionInfo.cs | 1 - ...SInternalTextDocumentClientCapabilities.cs | 1 - .../Protocol/RoslynLanguageServer.cs | 39 ++++--- .../CodeActions/CodeActionsTests.cs | 9 +- .../CodeActions/RunCodeActionsTests.cs | 4 +- .../CodeLens/CSharpCodeLensTests.cs | 4 +- .../Completion/CompletionFeaturesTests.cs | 6 +- .../Completion/CompletionResolveTests.cs | 13 +-- ...ngeConfigurationNotificationHandlerTest.cs | 6 +- .../DiagnosticRegistrationTests.cs | 4 +- .../ProtocolUnitTests/HandlerTests.cs | 13 +-- .../InlayHint/CSharpInlayHintTests.cs | 4 +- .../FindAllReferencesHandlerTests.cs | 6 +- .../VSTypeScriptHandlerTests.cs | 4 +- .../Lsif/GeneratorTest/SemanticTokensTests.vb | 4 +- ...stractRazorLanguageServerFactoryWrapper.cs | 6 +- .../RazorDynamicRegistrationServiceFactory.cs | 8 +- .../RazorLanguageServerFactoryWrapper.cs | 20 ++-- .../Xaml/External/ResolveDataConversions.cs | 8 +- 86 files changed, 611 insertions(+), 359 deletions(-) create mode 100644 src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs create mode 100644 src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs create mode 100644 src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs create mode 100644 src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs diff --git a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs index 0038d5fbfe21b..cea46937b42b3 100644 --- a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.IO; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -19,8 +20,6 @@ using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.Threading; using Nerdbank.Streams; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; @@ -200,10 +199,9 @@ internal async Task> CreateAsync> CreateAsync> CreateAsync Create( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, + JsonSerializerOptions options, ICapabilitiesProvider capabilitiesProvider, WellKnownLspServerKinds serverKind, AbstractLspLogger logger, @@ -236,7 +234,7 @@ public virtual AbstractLanguageServer Create( var server = new RoslynLanguageServer( LspServiceProvider, jsonRpc, - jsonSerializer, + options, capabilitiesProvider, logger, hostServices, diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index b4b32bd2383e1..0cdfa7bd0739d 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -7,6 +7,8 @@ using System.Collections.Immutable; using System.IO; using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -26,13 +28,11 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CommonLanguageServerProtocol.Framework; using Nerdbank.Streams; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; using Xunit; @@ -44,6 +44,8 @@ namespace Roslyn.Test.Utilities [UseExportProvider] public abstract partial class AbstractLanguageServerProtocolTests { + private static readonly SystemTextJsonFormatter s_messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); + private protected readonly AbstractLspLogger TestOutputLspLogger; protected AbstractLanguageServerProtocolTests(ITestOutputHelper? testOutputHelper) { @@ -124,8 +126,8 @@ private protected static LSP.ClientCapabilities GetCapabilities(bool isVS) /// the actual object to be converted to JSON. public static void AssertJsonEquals(T1 expected, T2 actual) { - var expectedStr = JsonConvert.SerializeObject(expected); - var actualStr = JsonConvert.SerializeObject(actual); + var expectedStr = JsonSerializer.Serialize(expected, s_messageFormatter.JsonSerializerOptions); + var actualStr = JsonSerializer.Serialize(actual, s_messageFormatter.JsonSerializerOptions); AssertEqualIgnoringWhitespace(expectedStr, actualStr); } @@ -269,7 +271,7 @@ private protected static LSP.CompletionParams CreateCompletionParams( SortText = sortText, InsertTextFormat = LSP.InsertTextFormat.Plaintext, Kind = kind, - Data = JObject.FromObject(new CompletionResolveData(resultId, ProtocolConversions.DocumentToTextDocumentIdentifier(document))), + Data = JsonSerializer.SerializeToElement(new CompletionResolveData(resultId, ProtocolConversions.DocumentToTextDocumentIdentifier(document)), s_messageFormatter.JsonSerializerOptions), Preselect = preselect, VsResolveTextEditOnCommit = vsResolveTextEditOnCommit, LabelDetails = labelDetails @@ -512,13 +514,6 @@ private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(U } }; - internal static JsonMessageFormatter CreateJsonMessageFormatter() - { - var messageFormatter = new JsonMessageFormatter(); - LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(messageFormatter.JsonSerializer); - return messageFormatter; - } - internal sealed class TestLspServer : IAsyncDisposable { public readonly EditorTestWorkspace TestWorkspace; @@ -545,7 +540,7 @@ private TestLspServer( LanguageServer = target; - _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, CreateJsonMessageFormatter()), clientTarget) + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, RoslynLanguageServer.CreateJsonMessageFormatter()), clientTarget) { ExceptionStrategy = ExceptionProcessing.ISerializable, }; @@ -607,13 +602,13 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var factory = workspace.ExportProvider.GetExportedValue(); - var jsonMessageFormatter = CreateJsonMessageFormatter(); + var jsonMessageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, jsonMessageFormatter)) { ExceptionStrategy = ExceptionProcessing.ISerializable, }; - var languageServer = (RoslynLanguageServer)factory.Create(jsonRpc, jsonMessageFormatter.JsonSerializer, capabilitiesProvider, serverKind, logger, workspace.Services.HostServices); + var languageServer = (RoslynLanguageServer)factory.Create(jsonRpc, jsonMessageFormatter.JsonSerializerOptions, capabilitiesProvider, serverKind, logger, workspace.Services.HostServices); jsonRpc.StartListening(); return languageServer; diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs index 4be80e009e63a..a87b6604e47f6 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/LspFileChangeWatcherTests.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Concurrent; +using System.Text.Json; using Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.FileWatching; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using StreamJsonRpc; using Xunit.Abstractions; using FileSystemWatcher = Roslyn.LanguageServer.Protocol.FileSystemWatcher; @@ -115,8 +114,8 @@ private static async Task WaitForFileWatcherAsync(TestLspServer testLspServer) private static FileSystemWatcher GetSingleFileWatcher(DynamicCapabilitiesRpcTarget dynamicCapabilities) { - var registrationJson = Assert.IsType(Assert.Single(dynamicCapabilities.Registrations).Value.RegisterOptions); - var registration = registrationJson.ToObject()!; + var registrationJson = Assert.IsType(Assert.Single(dynamicCapabilities.Registrations).Value.RegisterOptions); + var registration = JsonSerializer.Deserialize(registrationJson, ProtocolConversions.LspJsonSerializerOptions)!; return Assert.Single(registration.Watchers); } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs index 49c5e48bb623f..504c980a16409 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/AbstractLanguageServerHostTests.cs @@ -52,7 +52,8 @@ private TestLspServer(ExportProvider exportProvider, ILogger logger) var (clientStream, serverStream) = FullDuplexStream.CreatePair(); LanguageServerHost = new LanguageServerHost(serverStream, serverStream, exportProvider, logger); - _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, new JsonMessageFormatter())) + var messageFormatter = LanguageServerHost.CreateJsonMessageFormatter(); + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, messageFormatter)) { AllowModificationWhileListening = true, ExceptionStrategy = ExceptionProcessing.ISerializable, diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs index 8c6d5c693539c..a0f0dbbca8ecc 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerConnectHandler.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; @@ -32,10 +32,9 @@ Task INotificationHandler.HandleNotification return _serviceBrokerFactory.CreateAndConnectAsync(request.PipeName); } - [DataContract] private class NotificationParams { - [DataMember(Name = "pipeName")] + [JsonPropertyName("pipeName")] public required string PipeName { get; set; } } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs index 89a3613e08fe5..6cc5edc44e4f2 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/FileWatching/LspContractTypes.cs @@ -3,44 +3,40 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol; -[DataContract] internal class DidChangeWatchedFilesRegistrationOptions { - [DataMember(Name = "watchers")] + [JsonPropertyName("watchers")] public required FileSystemWatcher[] Watchers { get; set; } } -[DataContract] internal class FileSystemWatcher { - [DataMember(Name = "globPattern")] + [JsonPropertyName("globPattern")] public required RelativePattern GlobPattern { get; set; } - [DataMember(Name = "kind")] + [JsonPropertyName("kind")] public WatchKind? Kind { get; set; } } -[DataContract] internal class RelativePattern { - [DataMember(Name = "baseUri")] + [JsonPropertyName("baseUri")] [JsonConverter(typeof(DocumentUriConverter))] public required Uri BaseUri { get; set; } - [DataMember(Name = "pattern")] + [JsonPropertyName("pattern")] public required string Pattern { get; set; } } // The LSP specification has a spelling error in the protocol, but Microsoft.VisualStudio.LanguageServer.Protocol // didn't carry that error along. This corrects that. -[DataContract] internal class UnregistrationParamsWithMisspelling { - [DataMember(Name = "unregisterations")] + [JsonPropertyName("unregisterations")] public required Unregistration[] Unregistrations { get; set; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenProjectsHandler.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenProjectsHandler.cs index 1e506c5030387..615c0863e5239 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenProjectsHandler.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenProjectsHandler.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; @@ -32,10 +32,9 @@ Task INotificationHandler.HandleNotification return _projectSystem.OpenProjectsAsync(request.Projects.SelectAsArray(p => p.LocalPath)); } - [DataContract] private class NotificationParams { - [DataMember(Name = "projects")] + [JsonPropertyName("projects")] public required Uri[] Projects { get; set; } } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs index da7bf9f0c814b..198c329b2ad96 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/OpenSolutionHandler.cs @@ -1,9 +1,9 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; @@ -31,10 +31,9 @@ Task INotificationHandler.HandleNotification return _projectSystem.OpenSolutionAsync(request.Solution.LocalPath); } - [DataContract] private class NotificationParams { - [DataMember(Name = "solution")] + [JsonPropertyName("solution")] public required Uri Solution { get; set; } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs index 8e7aa86e50b2d..d6aa0fdf31740 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Microsoft.CodeAnalysis.MSBuild; using Microsoft.CodeAnalysis.PooledObjects; @@ -133,7 +133,6 @@ internal static async Task RestoreProjectsAsync(ImmutableHashSet project await languageServerManager.SendRequestAsync(ProjectNeedsRestoreName, unresolvedParams, cancellationToken); } - [DataContract] private record UnresolvedDependenciesParams( - [property: DataMember(Name = "projectFilePaths")] string[] ProjectFilePaths); + [property: JsonPropertyName("projectFilePaths")] string[] ProjectFilePaths); } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs index 97c89c8ff79f8..63f16c646f97a 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectTelemetry/ProjectLoadTelemetryEvent.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry; @@ -12,17 +12,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.ProjectTelemetry; /// except for SdkVersion, which is unused by the client in the O# version. /// -[DataContract] internal record ProjectLoadTelemetryEvent( // The project guid (if it came from a solution), or a hash representing the file path and contents. - [property: DataMember(Name = "ProjectId")] string ProjectId, - [property: DataMember(Name = "SessionId")] string SessionId, - [property: DataMember(Name = "OutputKind")] int OutputKind, - [property: DataMember(Name = "ProjectCapabilities")] IEnumerable ProjectCapabilities, - [property: DataMember(Name = "TargetFrameworks")] IEnumerable TargetFrameworks, - [property: DataMember(Name = "References")] IEnumerable References, - [property: DataMember(Name = "FileExtensions")] IEnumerable FileExtensions, - [property: DataMember(Name = "FileCounts")] IEnumerable FileCounts, - [property: DataMember(Name = "SdkStyleProject")] bool SdkStyleProject) + [property: JsonPropertyName("ProjectId")] string ProjectId, + [property: JsonPropertyName("SessionId")] string SessionId, + [property: JsonPropertyName("OutputKind")] int OutputKind, + [property: JsonPropertyName("ProjectCapabilities")] IEnumerable ProjectCapabilities, + [property: JsonPropertyName("TargetFrameworks")] IEnumerable TargetFrameworks, + [property: JsonPropertyName("References")] IEnumerable References, + [property: JsonPropertyName("FileExtensions")] IEnumerable FileExtensions, + [property: JsonPropertyName("FileCounts")] IEnumerable FileCounts, + [property: JsonPropertyName("SdkStyleProject")] bool SdkStyleProject) { } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs index fd5f36244b1f9..beae910355125 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/RazorDynamicFileInfoProvider.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; @@ -18,26 +18,23 @@ internal class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider { private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; - [DataContract] private class ProvideDynamicFileParams { - [DataMember(Name = "razorFiles")] + [JsonPropertyName("razorFiles")] public required Uri[] RazorFiles { get; set; } } - [DataContract] private class ProvideDynamicFileResponse { - [DataMember(Name = "generatedFiles")] + [JsonPropertyName("generatedFiles")] public required Uri[] GeneratedFiles { get; set; } } private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; - [DataContract] private class RemoveDynamicFileParams { - [DataMember(Name = "razorFiles")] + [JsonPropertyName("razorFiles")] public required Uri[] RazorFiles { get; set; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs index 82e2ab5fa530c..158a7f99d7604 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/ProjectDebugConfiguration.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration; -[DataContract] internal class ProjectDebugConfiguration { public ProjectDebugConfiguration(string projectPath, string outputPath, string projectName, bool targetsDotnetCore, bool isExe, string? solutionPath) @@ -20,21 +18,21 @@ public ProjectDebugConfiguration(string projectPath, string outputPath, string p SolutionPath = solutionPath; } - [JsonProperty(PropertyName = "projectPath")] + [JsonPropertyName("projectPath")] public string ProjectPath { get; } - [JsonProperty(PropertyName = "outputPath")] + [JsonPropertyName("outputPath")] public string OutputPath { get; } - [JsonProperty(PropertyName = "projectName")] + [JsonPropertyName("projectName")] public string ProjectName { get; } - [JsonProperty(PropertyName = "targetsDotnetCore")] + [JsonPropertyName("targetsDotnetCore")] public bool TargetsDotnetCore { get; } - [JsonProperty(PropertyName = "isExe")] + [JsonPropertyName("isExe")] public bool IsExe { get; } - [JsonProperty(PropertyName = "solutionPath")] + [JsonPropertyName("solutionPath")] public string? SolutionPath { get; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs index 1d3b795be6ba1..ac806d4fe897b 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/DebugConfiguration/WorkspaceDebugConfigurationParams.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration; -[DataContract] internal record WorkspaceDebugConfigurationParams( - [JsonProperty(PropertyName = "workspacePath"), JsonConverter(typeof(DocumentUriConverter))] Uri WorkspacePath); + [property: JsonPropertyName("workspacePath"), JsonConverter(typeof(DocumentUriConverter))] Uri WorkspacePath); diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs index 44bf12762fc65..2717d9c21f953 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestoreParams.cs @@ -2,19 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -[DataContract] internal sealed record RestoreParams( // An empty set of project file paths means restore all projects in the workspace. - [property: DataMember(Name = "projectFilePaths")] string[] ProjectFilePaths + [property: JsonPropertyName("projectFilePaths")] string[] ProjectFilePaths ) : IPartialResultParams { - [DataMember(Name = Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs index 40731e53c9d9d..52a26da7932ca 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorePartialResult.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -[DataContract] internal sealed record RestorePartialResult( - [property: DataMember(Name = "stage")] string Stage, - [property: DataMember(Name = "message")] string Message + [property: JsonPropertyName("stage")] string Stage, + [property: JsonPropertyName("message")] string Message ); diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs index 013a155b73eb5..d0cca0eb209bc 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs @@ -7,6 +7,7 @@ using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; +using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.LanguageServer; @@ -28,7 +29,8 @@ internal sealed class LanguageServerHost public LanguageServerHost(Stream inputStream, Stream outputStream, ExportProvider exportProvider, ILogger logger) { - var messageFormatter = new JsonMessageFormatter(); + var messageFormatter = CreateJsonMessageFormatter(); + var handler = new HeaderDelimitedMessageHandler(outputStream, inputStream, messageFormatter); // If there is a jsonrpc disconnect or server shutdown, that is handled by the AbstractLanguageServer. No need to do anything here. @@ -44,7 +46,15 @@ public LanguageServerHost(Stream inputStream, Stream outputStream, ExportProvide var lspLogger = new LspServiceLogger(_logger); var hostServices = exportProvider.GetExportedValue().HostServices; - _roslynLanguageServer = roslynLspFactory.Create(_jsonRpc, messageFormatter.JsonSerializer, capabilitiesProvider, WellKnownLspServerKinds.CSharpVisualBasicLspServer, lspLogger, hostServices); + _roslynLanguageServer = roslynLspFactory.Create(_jsonRpc, messageFormatter.JsonSerializerOptions, capabilitiesProvider, WellKnownLspServerKinds.CSharpVisualBasicLspServer, lspLogger, hostServices); + } + + internal static SystemTextJsonFormatter CreateJsonMessageFormatter() + { + var messageFormatter = new SystemTextJsonFormatter(); + messageFormatter.JsonSerializerOptions.AddVSCodeInternalExtensionConverters(); + messageFormatter.JsonSerializerOptions.Converters.Add(new NaturalObjectConverter()); + return messageFormatter; } public void Start() diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs index b9b7ea2f7a8b8..f223114d93364 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -33,9 +33,8 @@ public static async Task ShowToastNotificationAsync(LSP.MessageType messageType, await languageServerManager.SendNotificationAsync(ShowToastNotificationName, toastParams, cancellationToken); } - [DataContract] private record ShowToastNotificationParams( - [property: DataMember(Name = "messageType")] LSP.MessageType MessageType, - [property: DataMember(Name = "message")] string Message, - [property: DataMember(Name = "commands")] LSP.Command[] Commands); + [property: JsonPropertyName("messageType")] LSP.MessageType MessageType, + [property: JsonPropertyName("message")] string Message, + [property: JsonPropertyName("commands")] LSP.Command[] Commands); } diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs index 650febe23b4a6..e09710a795c5f 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/NamedPipeInformation.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer; -[DataContract] internal record NamedPipeInformation( - [property: DataMember(Name = "pipeName")] string PipeName); + [property: JsonPropertyName("pipeName")] string PipeName); diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs index 5b9b44969f8ab..6baf739265c61 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Program.cs @@ -8,6 +8,7 @@ using System.IO.Pipes; using System.Runtime.InteropServices; using System.Runtime.Loader; +using System.Text.Json; using Microsoft.CodeAnalysis.Contracts.Telemetry; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; @@ -18,7 +19,6 @@ using Microsoft.CodeAnalysis.LanguageServer.StarredSuggestions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Console; -using Newtonsoft.Json; using Roslyn.Utilities; // Setting the title can fail if the process is run without a window, such @@ -120,7 +120,7 @@ static async Task RunAsync(ServerConfiguration serverConfiguration, Cancellation PipeOptions.CurrentUserOnly | PipeOptions.Asynchronous); // Send the named pipe connection info to the client - Console.WriteLine(JsonConvert.SerializeObject(new NamedPipeInformation(clientPipeName))); + Console.WriteLine(JsonSerializer.Serialize(new NamedPipeInformation(clientPipeName))); // Wait for connection from client await pipeServer.WaitForConnectionAsync(cancellationToken); diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs index 4d6774ecd3ab3..db1d59015f096 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.Example/ExampleLanguageServer.cs @@ -3,19 +3,19 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json; using Microsoft.CommonLanguageServerProtocol.Framework.Handlers; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; namespace Microsoft.CommonLanguageServerProtocol.Framework.Example; -internal class ExampleLanguageServer : AbstractLanguageServer +internal class ExampleLanguageServer : SystemTextJsonLanguageServer { private readonly Action? _addExtraHandlers; - public ExampleLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger, Action? addExtraHandlers) : base(jsonRpc, jsonSerializer, logger) + public ExampleLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger, Action? addExtraHandlers) : base(jsonRpc, options, logger) { _addExtraHandlers = addExtraHandlers; // This spins up the queue and ensure the LSP is ready to start receiving requests diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs index 1766f5eeebebc..2a6010a8d4630 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/RequestExecutionQueueTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CommonLanguageServerProtocol.Framework.UnitTests; public class RequestExecutionQueueTests { - private class MockServer : AbstractLanguageServer + private class MockServer : NewtonsoftLanguageServer { public MockServer() : base(new JsonRpc(new HeaderDelimitedMessageHandler(FullDuplexStream.CreatePair().Item1)), JsonSerializer.CreateDefault(), NoOpLspLogger.Instance) { diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs index 8754366f4f30a..f0ae9582cf262 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework.UnitTests/TestExampleLanguageServer.cs @@ -4,12 +4,12 @@ using System; using System.IO; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CommonLanguageServerProtocol.Framework.Example; using Microsoft.Extensions.DependencyInjection; using Nerdbank.Streams; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; @@ -19,8 +19,8 @@ internal class TestExampleLanguageServer : ExampleLanguageServer { private readonly JsonRpc _clientRpc; - public TestExampleLanguageServer(Stream clientSteam, JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger, Action? addExtraHandlers) - : base(jsonRpc, jsonSerializer, logger, addExtraHandlers) + public TestExampleLanguageServer(Stream clientSteam, JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger, Action? addExtraHandlers) + : base(jsonRpc, options, logger, addExtraHandlers) { _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientSteam, clientSteam, CreateJsonMessageFormatter())) { @@ -102,10 +102,10 @@ internal async Task WaitForExit() return await _exiting.Task; } - private static JsonMessageFormatter CreateJsonMessageFormatter() + private static SystemTextJsonFormatter CreateJsonMessageFormatter() { - var messageFormatter = new JsonMessageFormatter(); - messageFormatter.JsonSerializer.AddVSInternalExtensionConverters(); + var messageFormatter = new SystemTextJsonFormatter(); + messageFormatter.JsonSerializerOptions.AddVSCodeInternalExtensionConverters(); return messageFormatter; } @@ -121,7 +121,7 @@ internal static TestExampleLanguageServer CreateBadLanguageServer(ILspLogger log serviceCollection.AddSingleton(); }; - var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializer, logger, extraHandlers); + var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializerOptions, logger, extraHandlers); jsonRpc.StartListening(); server.InitializeTest(); @@ -135,7 +135,7 @@ internal static TestExampleLanguageServer CreateLanguageServer(ILspLogger logger var messageFormatter = CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(serverStream, serverStream, messageFormatter)); - var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializer, logger, addExtraHandlers: null); + var server = new TestExampleLanguageServer(clientStream, jsonRpc, messageFormatter.JsonSerializerOptions, logger, addExtraHandlers: null); jsonRpc.StartListening(); server.InitializeTest(); diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs index 8dddabcf31dbe..2a3ca52c14099 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs @@ -12,8 +12,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using StreamJsonRpc; namespace Microsoft.CommonLanguageServerProtocol.Framework; @@ -23,8 +21,6 @@ internal abstract class AbstractLanguageServer private readonly JsonRpc _jsonRpc; protected readonly ILspLogger Logger; - protected readonly JsonSerializer _jsonSerializer; - /// /// These are lazy to allow implementations to define custom variables that are used by /// or @@ -58,12 +54,10 @@ internal abstract class AbstractLanguageServer protected AbstractLanguageServer( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, ILspLogger logger) { Logger = logger; _jsonRpc = jsonRpc; - _jsonSerializer = jsonSerializer; _jsonRpc.AddLocalRpcTarget(this); _jsonRpc.Disconnected += JsonRpc_Disconnected; @@ -102,7 +96,6 @@ protected virtual AbstractHandlerProvider HandlerProvider protected virtual void SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) { - var entryPointMethodInfo = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.ExecuteRequestAsync))!; // Get unique set of methods from the handler provider for the default language. foreach (var methodGroup in handlerProvider .GetRegisteredMethods() @@ -127,13 +120,16 @@ protected virtual void SetupRequestDispatcher(AbstractHandlerProvider handlerPro throw new InvalidOperationException($"Language specific handlers for {methodGroup.Key} have mis-matched number of returns:{Environment.NewLine}{string.Join(Environment.NewLine, methodGroup)}"); } - var delegatingEntryPoint = new DelegatingEntryPoint(methodGroup.Key, this, methodGroup); + var delegatingEntryPoint = CreateDelegatingEntryPoint(methodGroup.Key, methodGroup); var methodAttribute = new JsonRpcMethodAttribute(methodGroup.Key) { UseSingleObjectParameterDeserialization = true, }; - _jsonRpc.AddLocalRpcMethod(entryPointMethodInfo, delegatingEntryPoint, methodAttribute); + // We verified above that parameters match, set flag if this request has parameters or is parameterless so we can set the entrypoint correctly. + var hasParameters = methodGroup.First().RequestType != null; + var entryPoint = delegatingEntryPoint.GetEntryPoint(hasParameters); + _jsonRpc.AddLocalRpcMethod(entryPoint, delegatingEntryPoint, methodAttribute); } static bool AllTypesMatch(IEnumerable types) @@ -178,24 +174,18 @@ protected IRequestExecutionQueue GetRequestExecutionQueue() return _queue.Value; } - protected virtual string GetLanguageForRequest(string methodName, JToken? parameters) - { - Logger.LogInformation($"Using default language handler for {methodName}"); - return LanguageServerConstants.DefaultLanguageName; - } + protected abstract DelegatingEntryPoint CreateDelegatingEntryPoint(string method, IGrouping handlersForMethod); - private sealed class DelegatingEntryPoint + protected abstract class DelegatingEntryPoint { - private readonly string _method; - private readonly Lazy> _languageEntryPoint; - private readonly AbstractLanguageServer _target; + protected readonly string _method; + protected readonly Lazy> _languageEntryPoint; private static readonly MethodInfo s_queueExecuteAsyncMethod = typeof(RequestExecutionQueue).GetMethod(nameof(RequestExecutionQueue.ExecuteAsync))!; - public DelegatingEntryPoint(string method, AbstractLanguageServer target, IGrouping handlersForMethod) + public DelegatingEntryPoint(string method, IGrouping handlersForMethod) { _method = method; - _target = target; _languageEntryPoint = new Lazy>(() => { var handlerEntryPoints = new Dictionary(); @@ -211,61 +201,40 @@ public DelegatingEntryPoint(string method, AbstractLanguageServer - /// StreamJsonRpc entry point for all handler methods. - /// The optional parameters allow StreamJsonRpc to call into the same method for any kind of request / notification (with any number of params or response). - /// - public async Task ExecuteRequestAsync(JToken? request = null, CancellationToken cancellationToken = default) - { - var queue = _target.GetRequestExecutionQueue(); - var lspServices = _target.GetLspServices(); - - // Retrieve the language of the request so we know how to deserialize it. - var language = _target.GetLanguageForRequest(_method, request); + public abstract MethodInfo GetEntryPoint(bool hasParameter); - // Find the correct request and response types for the given request and language. + protected (MethodInfo MethodInfo, RequestHandlerMetadata Metadata) GetMethodInfo(string language) + { if (!_languageEntryPoint.Value.TryGetValue(language, out var requestInfo) && !_languageEntryPoint.Value.TryGetValue(LanguageServerConstants.DefaultLanguageName, out requestInfo)) { throw new InvalidOperationException($"No default or language specific handler was found for {_method} and document with language {language}"); } - // Deserialize the request parameters (if any). - var requestObject = DeserializeRequest(request, requestInfo.Metadata, _target._jsonSerializer); + return requestInfo; + } - var task = requestInfo.MethodInfo.Invoke(queue, [requestObject, _method, language, lspServices, cancellationToken]) as Task + protected async Task InvokeAsync( + MethodInfo methodInfo, + IRequestExecutionQueue queue, + object? requestObject, + string language, + ILspServices lspServices, + CancellationToken cancellationToken) + { + var task = methodInfo.Invoke(queue, [requestObject, _method, language, lspServices, cancellationToken]) as Task ?? throw new InvalidOperationException($"Queue result task cannot be null"); await task.ConfigureAwait(false); var resultProperty = task.GetType().GetProperty("Result") ?? throw new InvalidOperationException("Result property on task cannot be null"); var result = resultProperty.GetValue(task); - if (result is null || result == NoValue.Instance) + if (result == NoValue.Instance) { return null; } - - return JToken.FromObject(result, _target._jsonSerializer); - } - - private static object DeserializeRequest(JToken? request, RequestHandlerMetadata metadata, JsonSerializer jsonSerializer) - { - if (request is null && metadata.RequestType is not null) - { - throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none"); - } - - if (request is not null && metadata.RequestType is null) + else { - throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some."); + return result; } - - object requestObject = NoValue.Instance; - if (request is not null) - { - requestObject = request.ToObject(metadata.RequestType, jsonSerializer) - ?? throw new InvalidOperationException($"Unable to deserialize {request} into {metadata.RequestType} for {metadata.HandlerDescription}"); - } - - return requestObject; } } diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems index 4f299de12be5e..58e64162145c4 100644 --- a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems @@ -30,10 +30,12 @@ + + \ No newline at end of file diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs new file mode 100644 index 0000000000000..8af14dc358410 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/NewtonsoftLanguageServer.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// This is consumed as 'generated' code in a source package and therefore requires an explicit nullable enable +#nullable enable + +using System; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +/// +/// Basic implementation of using Newtonsoft for serialization. +/// +internal abstract class NewtonsoftLanguageServer : AbstractLanguageServer +{ + private readonly JsonSerializer _jsonSerializer; + protected NewtonsoftLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, ILspLogger logger) : base(jsonRpc, logger) + { + _jsonSerializer = jsonSerializer; + } + + protected override DelegatingEntryPoint CreateDelegatingEntryPoint(string method, IGrouping handlersForMethod) + { + return new NewtonsoftDelegatingEntryPoint(method, handlersForMethod, this); + } + + protected virtual string GetLanguageForRequest(string methodName, JToken? parameters) + { + Logger.LogInformation($"Using default language handler for {methodName}"); + return LanguageServerConstants.DefaultLanguageName; + } + + private class NewtonsoftDelegatingEntryPoint( + string method, + IGrouping handlersForMethod, + NewtonsoftLanguageServer target) : DelegatingEntryPoint(method, handlersForMethod) + { + private static readonly MethodInfo s_entryPoint = typeof(NewtonsoftDelegatingEntryPoint).GetMethod(nameof(NewtonsoftDelegatingEntryPoint.ExecuteRequestAsync), BindingFlags.NonPublic | BindingFlags.Instance)!; + + public override MethodInfo GetEntryPoint(bool hasParameter) + { + return s_entryPoint; + } + + /// + /// StreamJsonRpc entry point for all handler methods. + /// The optional parameters allow StreamJsonRpc to call into the same method for any kind of request / notification (with any number of params or response). + /// + private async Task ExecuteRequestAsync(JToken? request = null, CancellationToken cancellationToken = default) + { + var queue = target.GetRequestExecutionQueue(); + var lspServices = target.GetLspServices(); + + // Retrieve the language of the request so we know how to deserialize it. + var language = target.GetLanguageForRequest(_method, request); + + // Find the correct request and response types for the given request and language. + var requestInfo = GetMethodInfo(language); + + // Deserialize the request parameters (if any). + var requestObject = DeserializeRequest(request, requestInfo.Metadata, target._jsonSerializer); + + var result = await InvokeAsync(requestInfo.MethodInfo, queue, requestObject, language, lspServices, cancellationToken).ConfigureAwait(false); + if (result is null) + { + return null; + } + + return JToken.FromObject(result, target._jsonSerializer); + } + + private static object DeserializeRequest(JToken? request, RequestHandlerMetadata metadata, JsonSerializer jsonSerializer) + { + if (request is null && metadata.RequestType is not null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none"); + } + + if (request is not null && metadata.RequestType is null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some."); + } + + object requestObject = NoValue.Instance; + if (request is not null) + { + requestObject = request.ToObject(metadata.RequestType, jsonSerializer) + ?? throw new InvalidOperationException($"Unable to deserialize {request} into {metadata.RequestType} for {metadata.HandlerDescription}"); + } + + return requestObject; + } + } +} diff --git a/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs new file mode 100644 index 0000000000000..57cebae1a4ecf --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// This is consumed as 'generated' code in a source package and therefore requires an explicit nullable enable +#nullable enable + +using System; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using StreamJsonRpc; + +namespace Microsoft.CommonLanguageServerProtocol.Framework; + +internal abstract class SystemTextJsonLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, ILspLogger logger) : AbstractLanguageServer(jsonRpc, logger) +{ + /// + /// JsonSerializer options used by streamjsonrpc (and for serializing / deserializing the requests to streamjsonrpc). + /// These options are specifically from the that added the exotic type converters. + /// + private readonly JsonSerializerOptions _jsonSerializerOptions = options; + protected override DelegatingEntryPoint CreateDelegatingEntryPoint(string method, IGrouping handlersForMethod) + { + return new SystemTextJsonDelegatingEntryPoint(method, handlersForMethod, this); + } + + protected virtual string GetLanguageForRequest(string methodName, JsonElement? parameters) + { + Logger.LogInformation($"Using default language handler for {methodName}"); + return LanguageServerConstants.DefaultLanguageName; + } + + private sealed class SystemTextJsonDelegatingEntryPoint( + string method, + IGrouping handlersForMethod, + SystemTextJsonLanguageServer target) : DelegatingEntryPoint(method, handlersForMethod) + { + private static readonly MethodInfo s_parameterlessEntryPoint = typeof(SystemTextJsonDelegatingEntryPoint).GetMethod(nameof(SystemTextJsonDelegatingEntryPoint.ExecuteRequest0Async), BindingFlags.NonPublic | BindingFlags.Instance)!; + private static readonly MethodInfo s_entryPoint = typeof(SystemTextJsonDelegatingEntryPoint).GetMethod(nameof(SystemTextJsonDelegatingEntryPoint.ExecuteRequestAsync), BindingFlags.NonPublic | BindingFlags.Instance)!; + + public override MethodInfo GetEntryPoint(bool hasParameter) + { + return hasParameter ? s_entryPoint : s_parameterlessEntryPoint; + } + + /// + /// StreamJsonRpc entry point for handlers with no parameters. + /// Unlike Newtonsoft, we have to differentiate instead of using default parameters. + /// + private Task ExecuteRequest0Async(CancellationToken cancellationToken = default) + { + return ExecuteRequestAsync(null, cancellationToken); + } + + /// + /// StreamJsonRpc entry point for handlers with parameters (and any response) type. + /// + private async Task ExecuteRequestAsync(JsonElement? request, CancellationToken cancellationToken = default) + { + var queue = target.GetRequestExecutionQueue(); + var lspServices = target.GetLspServices(); + + // Retrieve the language of the request so we know how to deserialize it. + var language = target.GetLanguageForRequest(_method, request); + + // Find the correct request and response types for the given request and language. + var requestInfo = GetMethodInfo(language); + + // Deserialize the request parameters (if any). + var requestObject = DeserializeRequest(request, requestInfo.Metadata, target._jsonSerializerOptions); + + var result = await InvokeAsync(requestInfo.MethodInfo, queue, requestObject, language, lspServices, cancellationToken).ConfigureAwait(false); + if (result is null) + { + return null; + } + + var serializedResult = JsonSerializer.SerializeToElement(result, target._jsonSerializerOptions); + return serializedResult; + } + + private static object DeserializeRequest(JsonElement? request, RequestHandlerMetadata metadata, JsonSerializerOptions options) + { + if (request is null && metadata.RequestType is not null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} requires request parameters but received none"); + } + + if (request is not null && metadata.RequestType is null) + { + throw new InvalidOperationException($"Handler {metadata.HandlerDescription} does not accept parameters, but received some."); + } + + object requestObject = NoValue.Instance; + if (request is not null) + { + requestObject = JsonSerializer.Deserialize(request.Value, metadata.RequestType!, options) + ?? throw new InvalidOperationException($"Unable to deserialize {request} into {metadata.RequestType} for {metadata.HandlerDescription}"); + } + + return requestObject; + } + } +} diff --git a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs index 797509201331c..1239d78edbc73 100644 --- a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs @@ -4,11 +4,11 @@ using System; using System.Composition; +using System.Text.Json; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer @@ -28,7 +28,7 @@ public CSharpVisualBasicLanguageServerFactory( public AbstractLanguageServer Create( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, + JsonSerializerOptions options, ICapabilitiesProvider capabilitiesProvider, WellKnownLspServerKinds serverKind, AbstractLspLogger logger, @@ -37,7 +37,7 @@ public AbstractLanguageServer Create( var server = new RoslynLanguageServer( _lspServiceProvider, jsonRpc, - jsonSerializer, + options, capabilitiesProvider, logger, hostServices, diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index b46fbf4e76b96..9b18f67090ed1 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -106,6 +107,20 @@ internal static partial class ProtocolConversions { WellKnownTags.Deprecated, ImmutableArray.Create(LSP.CompletionItemTag.Deprecated) }, }.ToImmutableDictionary(); + public static JsonSerializerOptions AddLspSerializerOptions(this JsonSerializerOptions options) + { + LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(options); + options.Converters.Add(new NaturalObjectConverter()); + return options; + } + + /// + /// Options that know how to serialize / deserialize basic LSP types. + /// Useful when there are particular fields that are not serialized or deserialized by normal request handling (for example + /// deserializing a field that is typed as object instead of a concrete type). + /// + public static JsonSerializerOptions LspJsonSerializerOptions = new JsonSerializerOptions().AddLspSerializerOptions(); + // TO-DO: More LSP.CompletionTriggerKind mappings are required to properly map to Roslyn CompletionTriggerKinds. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1178726 public static async Task LSPToRoslynCompletionTriggerAsync( diff --git a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs index f513d42e385b6..74ced5c517c6f 100644 --- a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs +++ b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/FormatNewFileParams.cs @@ -2,20 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; -[DataContract] internal sealed record FormatNewFileParams { - [DataMember(Name = "document")] + [JsonPropertyName("document")] public required TextDocumentIdentifier Document { get; init; } - [DataMember(Name = "project")] + [JsonPropertyName("project")] public required TextDocumentIdentifier Project { get; init; } - [DataMember(Name = "contents")] + [JsonPropertyName("contents")] public required string Contents { get; init; } } diff --git a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs index 1e5d1fc0f4bb7..059f1e7402c87 100644 --- a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs +++ b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SemanticTokensRangesParams.cs @@ -3,13 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; -[DataContract] internal class SemanticTokensRangesParams : SemanticTokensParams { - [DataMember(Name = "ranges")] + [JsonPropertyName("ranges")] public required Range[] Ranges { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs index ba7495c8f93a9..a36a303b94258 100644 --- a/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs +++ b/src/Features/LanguageServer/Protocol/ExternalAccess/Razor/SimplifyMethodParams.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; @@ -11,18 +11,17 @@ namespace Microsoft.CodeAnalysis.LanguageServer.ExternalAccess.Razor; // Summary: // Class representing the parameters sent from the client to the server for the // roslyn/simplifyMethod request. -[DataContract] internal record SimplifyMethodParams : ITextDocumentParams { // // Summary: // Gets or sets the value which identifies the document where the text edit will be placed. - [DataMember(Name = "textDocument")] + [JsonPropertyName("textDocument")] public required TextDocumentIdentifier TextDocument { get; set; } // // Summary: // Gets or sets the value which identifies the text edit to be simplified. - [DataMember(Name = "textEdit")] + [JsonPropertyName("textEdit")] public required TextEdit TextEdit { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 69aa85d102140..1f96b177f6ca6 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Runtime.CompilerServices; diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs index aad78be589aa2..103dd05fba686 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionFixAllResolveHandler.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -11,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; @@ -35,12 +35,13 @@ internal sealed class CodeActionFixAllResolveHandler( public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(RoslynFixAllCodeAction request) - => ((JToken)request.Data!).ToObject()!.TextDocument; + => GetCodeActionResolveData(request).TextDocument; public async Task HandleRequestAsync(RoslynFixAllCodeAction request, RequestContext context, CancellationToken cancellationToken) { var document = context.GetRequiredDocument(); - var data = ((JToken)request.Data!).ToObject(); + Contract.ThrowIfNull(request.Data); + var data = GetCodeActionResolveData(request); Assumes.Present(data); var options = _globalOptions.GetCodeActionOptionsProvider(); @@ -65,4 +66,12 @@ public async Task HandleRequestAsync(RoslynFixAllCodeAct request.Edit = edit; return request; } + + private static CodeActionResolveData GetCodeActionResolveData(RoslynFixAllCodeAction request) + { + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data!, ProtocolConversions.LspJsonSerializerOptions); + Contract.ThrowIfNull(resolveData, "Missing data for fix all code action resolve request"); + return resolveData; + + } } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs index 064010e74e7f5..26710c2b6ebb0 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveHandler.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -12,9 +13,9 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Microsoft.CodeAnalysis.Options; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; +using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -52,11 +53,12 @@ public CodeActionResolveHandler( public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) - => ((JToken)request.Data!).ToObject()!.TextDocument; + => GetCodeActionResolveData(request).TextDocument; public async Task HandleRequestAsync(LSP.CodeAction codeAction, RequestContext context, CancellationToken cancellationToken) { - var data = ((JToken)codeAction.Data!).ToObject(); + Contract.ThrowIfNull(codeAction.Data); + var data = GetCodeActionResolveData(codeAction); Assumes.Present(data); // Fix All Code Action does not need further resolution since it already has the command callback @@ -97,4 +99,11 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeAction request) codeAction.Edit = edit; return codeAction; } + + private static CodeActionResolveData GetCodeActionResolveData(LSP.CodeAction request) + { + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data!, ProtocolConversions.LspJsonSerializerOptions); + Contract.ThrowIfNull(resolveData, "Missing data for code action resolve request"); + return resolveData; + } } diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs index 39d3299223f3c..5085dcd4ad670 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeLens/CodeLensResolveHandler.cs @@ -5,10 +5,11 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using Microsoft.CodeAnalysis.Shared.Extensions; using LSP = Roslyn.LanguageServer.Protocol; +using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; @@ -81,7 +82,8 @@ public LSP.TextDocumentIdentifier GetTextDocumentIdentifier(LSP.CodeLens request private static CodeLensResolveData GetCodeLensResolveData(LSP.CodeLens codeLens) { - var resolveData = (codeLens.Data as JToken)?.ToObject(); + Contract.ThrowIfNull(codeLens.Data); + var resolveData = JsonSerializer.Deserialize((JsonElement)codeLens.Data, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(resolveData, "Missing data for code lens resolve request"); return resolveData; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs index 4a8d828e9ac97..5a17d02cd577f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionHandler.cs @@ -73,8 +73,9 @@ public CompletionHandler( var (list, isIncomplete, resultId) = completionListResult.Value; var creationService = document.Project.Solution.Services.GetRequiredService(); - return await creationService.ConvertToLspCompletionListAsync(document, position, capabilityHelper, list, isIncomplete, resultId, cancellationToken) + var result = await creationService.ConvertToLspCompletionListAsync(document, position, capabilityHelper, list, isIncomplete, resultId, cancellationToken) .ConfigureAwait(false); + return result; } private async Task<(CompletionList CompletionList, bool IsIncomplete, long ResultId)?> GetFilteredCompletionListAsync( diff --git a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs index 75c3eec604eb8..31f932e9d66e7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Completion/CompletionResolveHandler.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; @@ -11,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -90,7 +90,7 @@ private static bool MatchesLSPCompletionItem(LSP.CompletionItem lspCompletionIte private static LSP.TextDocumentIdentifier? GetTextDocumentCacheEntry(LSP.CompletionItem request) { Contract.ThrowIfNull(request.Data); - var resolveData = ((JToken)request.Data).ToObject(); + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data); if (resolveData is null) { Contract.Fail("Document should always be provided when resolving a completion item request."); @@ -103,7 +103,7 @@ private static bool MatchesLSPCompletionItem(LSP.CompletionItem lspCompletionIte private CompletionListCache.CacheEntry? GetCompletionListCacheEntry(LSP.CompletionItem request) { Contract.ThrowIfNull(request.Data); - var resolveData = ((JToken)request.Data).ToObject(); + var resolveData = JsonSerializer.Deserialize((JsonElement)request.Data, ProtocolConversions.LspJsonSerializerOptions); if (resolveData?.ResultId == null) { Contract.Fail("Result id should always be provided when resolving a completion item we returned."); diff --git a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs index c61ec8ca969a1..d414b90d5cc92 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json; +using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ImplementType; @@ -12,7 +14,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -114,17 +115,18 @@ private void SetOption(IOption2 option, string valueFromClient, string? language } } - private async Task> GetConfigurationsAsync(CancellationToken cancellationToken) + private async Task> GetConfigurationsAsync(CancellationToken cancellationToken) { try { var configurationParams = new ConfigurationParams() { Items = _configurationItems.AsArray() }; - var options = await _clientLanguageServerManager.SendRequestAsync( + var options = await _clientLanguageServerManager.SendRequestAsync( Methods.WorkspaceConfigurationName, configurationParams, cancellationToken).ConfigureAwait(false); // Failed to get result from client. Contract.ThrowIfNull(options); - return options.SelectAsArray(token => token.ToString()); + var converted = options.SelectAsArray(token => token?.ToString()); + return converted; } catch (Exception e) { diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs index f3ac503169a17..1b30a4622f72d 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/BuildOnlyDiagnosticIdsHandler.cs @@ -7,7 +7,7 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler; -[DataContract] -internal record class BuildOnlyDiagnosticIdsResult([property: DataMember(Name = "ids")] string[] Ids); +internal record class BuildOnlyDiagnosticIdsResult([property: JsonPropertyName("ids")] string[] Ids); [ExportCSharpVisualBasicStatelessLspService(typeof(BuildOnlyDiagnosticIdsHandler)), Shared] [Method(BuildOnlyDiagnosticIdsMethodName)] diff --git a/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs b/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs index bc2a111664bc3..3984c475b0542 100644 --- a/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/EditAndContinue/RegisterSolutionSnapshotHandler.cs @@ -5,6 +5,7 @@ using System; using System.Composition; using System.Runtime.Serialization; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.EditAndContinue; @@ -13,8 +14,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.EditAndContinue; -[DataContract] -internal readonly record struct LspSolutionSnapshotId([property: DataMember(Name = "id")] int Id); +internal readonly record struct LspSolutionSnapshotId([property: JsonPropertyName("id")] int Id); [ExportCSharpVisualBasicStatelessLspService(typeof(RegisterSolutionSnapshotHandler)), Shared] [Method("workspace/_vs_registerSolutionSnapshot")] diff --git a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs index d0e81cc719cfe..d1b6b6a966ab4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlayHint/InlayHintResolveHandler.cs @@ -7,10 +7,10 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.InlineHints; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint { @@ -29,7 +29,7 @@ public InlayHintResolveHandler(InlayHintCache inlayHintCache) public bool RequiresLSPSolution => true; public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) - => GetTextDocument(request.Data) ?? throw new ArgumentException(); + => GetInlayHintResolveData(request).TextDocument; public async Task HandleRequestAsync(LSP.InlayHint request, RequestContext context, CancellationToken cancellationToken) { @@ -54,13 +54,6 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) return request; } - private static LSP.TextDocumentIdentifier? GetTextDocument(object? requestData) - { - Contract.ThrowIfNull(requestData); - var resolveData = ((JToken)requestData).ToObject(); - return resolveData?.TextDocument; - } - private (InlayHintCache.InlayHintCacheEntry CacheEntry, InlineHint InlineHintToResolve) GetCacheEntry(InlayHintResolveData resolveData) { var cacheEntry = _inlayHintCache.GetCachedEntry(resolveData.ResultId); @@ -70,7 +63,8 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(LSP.InlayHint request) private static InlayHintResolveData GetInlayHintResolveData(LSP.InlayHint inlayHint) { - var resolveData = (inlayHint.Data as JToken)?.ToObject(); + Contract.ThrowIfNull(inlayHint.Data); + var resolveData = JsonSerializer.Deserialize((JsonElement)inlayHint.Data, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(resolveData, "Missing data for inlay hint resolve request"); return resolveData; } diff --git a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs index 94d4759c38bc1..ea9cd0fe5dea6 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/ServerLifetime/InitializeHandler.cs @@ -3,11 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler; @@ -41,7 +41,7 @@ public Task HandleRequestAsync(InitializeParams request, Reque Logger.Log(FunctionId.LSP_Initialize, KeyValueLogMessage.Create(m => { m["serverKind"] = context.ServerKind.ToTelemetryString(); - m["capabilities"] = JsonConvert.SerializeObject(serverCapabilities); + m["capabilities"] = JsonSerializer.Serialize(serverCapabilities, ProtocolConversions.LspJsonSerializerOptions); })); return Task.FromResult(new InitializeResult diff --git a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs index a508b0251c4b8..d71fe27838375 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbol.cs @@ -2,9 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -15,13 +14,13 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal sealed class RoslynDocumentSymbol : DocumentSymbol { - [DataMember(IsRequired = false, Name = "glyph")] + [JsonPropertyName("glyph")] public int Glyph { get; set; } // Deliberately override the value in the base so that our serializers/deserializers know to include the custom // data we have on the children as well. - [DataMember(Name = "children")] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName("children")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public new RoslynDocumentSymbol[]? Children { get => (RoslynDocumentSymbol[]?)base.Children; diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs index 735db7ae8fa8c..98efe6212d2a3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachParams.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record DebugAttachParams( - [property: DataMember(Name = "processId")] int ProcessId + [property: JsonPropertyName("processId")] int ProcessId ); diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs index f273254c0bdc7..cee2e897c0f14 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/DebugAttachResult.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record DebugAttachResult( - [property: DataMember(Name = "didAttach")] bool DidAttach + [property: JsonPropertyName("didAttach")] bool DidAttach ); diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs index f02f51dc1b58b..6329d24a20070 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsParams.cs @@ -3,21 +3,19 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record RunTestsParams( - [property: DataMember(Name = "textDocument")] LSP.TextDocumentIdentifier TextDocument, - [property: DataMember(Name = "range")] LSP.Range Range, - [property: DataMember(Name = "attachDebugger")] bool AttachDebugger, - [property: DataMember(Name = "runSettingsPath")] string? RunSettingsPath + [property: JsonPropertyName("textDocument")] LSP.TextDocumentIdentifier TextDocument, + [property: JsonPropertyName("range")] LSP.Range Range, + [property: JsonPropertyName("attachDebugger")] bool AttachDebugger, + [property: JsonPropertyName("runSettingsPath")] string? RunSettingsPath ) : LSP.IPartialResultParams { - [DataMember(Name = LSP.Methods.PartialResultTokenName)] - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonPropertyName(LSP.Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IProgress? PartialResultToken { get; set; } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs index 08d3455c26db5..9e6972637b9c2 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs @@ -2,14 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record RunTestsPartialResult( - [property: DataMember(Name = "stage")] string Stage, - [property: DataMember(Name = "message")] string Message, - [property: DataMember(Name = "progress"), JsonProperty(NullValueHandling = NullValueHandling.Ignore)] TestProgress? Progress + [property: JsonPropertyName("stage")] string Stage, + [property: JsonPropertyName("message")] string Message, + [property: JsonPropertyName("progress"), JsonProperty(NullValueHandling = NullValueHandling.Ignore)] TestProgress? Progress ); diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs index 4c5911cadfa7b..8ecff54eec170 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/TestProgress.cs @@ -2,14 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; -[DataContract] internal record struct TestProgress( - [property: DataMember(Name = "testsPassed")] long TestsPassed, - [property: DataMember(Name = "testsFailed")] long TestsFailed, - [property: DataMember(Name = "testsSkipped")] long TestsSkipped, - [property: DataMember(Name = "totalTests")] long TotalTests + [property: JsonPropertyName("testsPassed")] long TestsPassed, + [property: JsonPropertyName("testsFailed")] long TestsFailed, + [property: JsonPropertyName("testsSkipped")] long TestsSkipped, + [property: JsonPropertyName("totalTests")] long TotalTests ); diff --git a/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs index c2145c42bf68c..24dd76f87ba7e 100644 --- a/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/ILanguageServerFactory.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text.Json; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer @@ -14,7 +14,7 @@ internal interface ILanguageServerFactory { public AbstractLanguageServer Create( JsonRpc jsonRpc, - JsonSerializer jsonSerializer, + JsonSerializerOptions options, ICapabilitiesProvider capabilitiesProvider, WellKnownLspServerKinds serverKind, AbstractLspLogger logger, diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs new file mode 100644 index 0000000000000..207ed168f25e6 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Roslyn.LanguageServer.Protocol +{ + using Newtonsoft.Json; + + /// + /// Class containing extension method to thread-safely manage operations. + /// + internal static class JsonConverterCollectionUtilities + { + /// + /// Lock used for modifications to Converters collection. + /// + public static readonly object ConvertersLock = new object(); + } +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs index 4d501e5f6c53b..39ab2a75d776f 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ContainerElementConverter.cs @@ -20,7 +20,7 @@ public override ContainerElement Read(ref Utf8JsonReader reader, Type typeToConv { if (reader.TokenType == JsonTokenType.StartObject) { - ContainerElementStyle? style = default; + ContainerElementStyle? style = null; List objects = new(); while (reader.Read()) diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs new file mode 100644 index 0000000000000..868de33bddf28 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Roslyn.LanguageServer.Protocol +{ + using System; + using System.Linq; + using Newtonsoft.Json; + + /// + /// Converter used to deserialize objects dropping any property. + /// + internal class DropProgressConverter : JsonConverter + { + /// + public override bool CanWrite => true; + + /// + /// Static method to get a containing a . + /// + /// object containing a . + public static JsonSerializer CreateSerializer() + { + var serializer = new JsonSerializer(); + serializer.Converters.Add(new DropProgressConverter()); + return serializer; + } + + /// + public override bool CanConvert(Type objectType) + { + var isIProgressOfT = objectType.IsConstructedGenericType && objectType.GetGenericTypeDefinition().Equals(typeof(IProgress<>)); + var implementsIProgressOfT = objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(IProgress<>))); + + return isIProgressOfT || implementsIProgressOfT; + } + + /// + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + // We deserialize all IProgress objects as null. + return null; + } + + /// + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + writer.WriteNull(); + } + } +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs index 38c46fcb547a0..6b0c54ede6d85 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/ImageElementConverter.cs @@ -17,8 +17,8 @@ public override ImageElement Read(ref Utf8JsonReader reader, Type typeToConvert, { if (reader.TokenType == JsonTokenType.StartObject) { - ImageId? imageId = default; - string? automationName = default; + ImageId? imageId = null; + string? automationName = null; while (reader.Read()) { diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs index a96c5482e09cb..802b5a32ef129 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextElement.cs @@ -5,9 +5,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments { + [JsonConverter(typeof(ClassifiedTextElementConverter))] internal sealed class ClassifiedTextElement { public const string TextClassificationTypeName = "text"; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs index e79d24774d717..89d71bfad4538 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ClassifiedTextRun.cs @@ -3,9 +3,12 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments; +[JsonConverter(typeof(ClassifiedTextRunConverter))] internal sealed class ClassifiedTextRun( string classificationTypeName, string text, diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs index c9f140776da0c..0d237c7b55ce7 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs @@ -7,9 +7,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments { + [JsonConverter(typeof(ContainerElementConverter))] internal sealed class ContainerElement { public IEnumerable Elements { get; } @@ -28,4 +31,4 @@ public ContainerElement(ContainerElementStyle style, params object[] elements) Elements = elements?.ToImmutableList() ?? throw new ArgumentNullException("elements"); } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs index 441741d13ab6a..ec1918dcd7bcc 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElementStyle.cs @@ -25,4 +25,4 @@ internal enum ContainerElementStyle // Additional padding above and below content. VerticalPadding = 0x2 } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs index bbd213a10f8bd..4c818307b7d75 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageElement.cs @@ -2,10 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text.Json.Serialization; using Roslyn.Core.Imaging; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Text.Adornments; +[JsonConverter(typeof(ImageElementConverter))] internal sealed class ImageElement { public static readonly ImageElement Empty = new(default, string.Empty); @@ -17,6 +20,7 @@ public ImageElement(ImageId imageId) : this(imageId, null) { } + [JsonConstructor] public ImageElement(ImageId imageId, string? automationName) { ImageId = imageId; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs index 9b226ebb2938a..bfa412a7959ae 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/Text/ImageId.cs @@ -4,6 +4,8 @@ using System; using System.Globalization; +using System.Text.Json.Serialization; +using Roslyn.LanguageServer.Protocol; namespace Roslyn.Core.Imaging { @@ -15,6 +17,7 @@ namespace Roslyn.Core.Imaging // On Windows systems, Microsoft.VisualStudio.Core.Imaging.ImageId can be converted // to and from various other image representations via the ImageIdExtensions extension // methods. + [JsonConverter(typeof(ImageIdConverter))] internal struct ImageId : IEquatable { // @@ -101,4 +104,4 @@ public override int GetHashCode() return hashCode ^ id.GetHashCode(); } } -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs index a9063b038b456..7a3fc2e1a3997 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionList.cs @@ -20,7 +20,7 @@ internal class VSInternalCompletionList : CompletionList /// Gets or sets a value indicating whether the completion list should use suggestion mode. In suggestion mode items are "soft-selected" by default. /// [JsonPropertyName(SuggestionModeSerializedName)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool SuggestionMode { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs index 413695d0c9f74..151d6c1c78bce 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalCompletionListSetting.cs @@ -16,7 +16,7 @@ internal class VSInternalCompletionListSetting /// onto underlying completion items unless they have their own data bags. /// [JsonPropertyName("_vs_data")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool Data { get; @@ -28,7 +28,7 @@ public bool Data /// onto underlying valid completion items unless they have their own commit characters. /// [JsonPropertyName("_vs_commitCharacters")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool CommitCharacters { get; diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs index 2dcd51847ce4d..54c83f2ebf742 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalHover.cs @@ -9,7 +9,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// Extension to Hover which adds additional data for colorization. /// - [DataContract] internal class VSInternalHover : Hover { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs index 0c40cfcfc3b28..18f1ae5dd0ff6 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionContext.cs @@ -10,7 +10,6 @@ namespace Roslyn.LanguageServer.Protocol /// Context for inline completion request. /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L27. /// - [DataContract] internal class VSInternalInlineCompletionContext { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs index 1c2520f3a6b4b..0970e77332246 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionItem.cs @@ -11,7 +11,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L78. /// - [DataContract] internal class VSInternalInlineCompletionItem { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs index 8fc7113870e2a..cccb4782656a5 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionList.cs @@ -11,7 +11,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L72. /// - [DataContract] internal class VSInternalInlineCompletionList { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs index 3a7e067efbb49..5deb5763fcfc4 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalInlineCompletionRequest.cs @@ -11,7 +11,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L24. /// - [DataContract] internal class VSInternalInlineCompletionRequest : ITextDocumentParams { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs index b589fd7e9b218..790b9ef24d951 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceParams.cs @@ -9,7 +9,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// Class which represents extensions of passed as parameter of find reference requests. /// - [DataContract] internal class VSInternalReferenceParams : ReferenceParams { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs index 85c150b74778f..dd2a72a6119be 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalSelectedCompletionInfo.cs @@ -11,7 +11,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// See https://github.com/microsoft/vscode/blob/075ba020e8493f40dba89891b1a08453f2c067e9/src/vscode-dts/vscode.proposed.inlineCompletions.d.ts#L48. /// - [DataContract] internal class VSInternalSelectedCompletionInfo { /// diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs index 3a7d0af7a9725..3e445253a2244 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/Internal/VSInternalTextDocumentClientCapabilities.cs @@ -9,7 +9,6 @@ namespace Roslyn.LanguageServer.Protocol /// /// Text document capabilities specific to Visual Studio. /// - [DataContract] internal class VSInternalTextDocumentClientCapabilities : TextDocumentClientCapabilities { /// diff --git a/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs b/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs index 8570c4d0db31b..670ced6aa0ad2 100644 --- a/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs +++ b/src/Features/LanguageServer/Protocol/RoslynLanguageServer.cs @@ -5,21 +5,20 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.ServerLifetime; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer { - internal sealed class RoslynLanguageServer : AbstractLanguageServer, IOnInitialized + internal sealed class RoslynLanguageServer : SystemTextJsonLanguageServer, IOnInitialized { private readonly AbstractLspServiceProvider _lspServiceProvider; private readonly ImmutableDictionary>> _baseServices; @@ -28,19 +27,17 @@ internal sealed class RoslynLanguageServer : AbstractLanguageServer supportedLanguages, WellKnownLspServerKinds serverKind) - : base(jsonRpc, serializer, logger) + : base(jsonRpc, serializerOptions, logger) { _lspServiceProvider = lspServiceProvider; _serverKind = serverKind; - VSCodeInternalExtensionUtilities.AddVSCodeInternalExtensionConverters(serializer); - // Create services that require base dependencies (jsonrpc) or are more complex to create to the set manually. _baseServices = GetBaseServices(jsonRpc, logger, capabilitiesProvider, hostServices, serverKind, supportedLanguages); @@ -48,6 +45,13 @@ public RoslynLanguageServer( Initialize(); } + public static SystemTextJsonFormatter CreateJsonMessageFormatter() + { + var messageFormatter = new SystemTextJsonFormatter(); + messageFormatter.JsonSerializerOptions.AddLspSerializerOptions(); + return messageFormatter; + } + protected override ILspServices ConstructLspServices() { return _lspServiceProvider.CreateServices(_serverKind, _baseServices); @@ -113,7 +117,7 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon return Task.CompletedTask; } - protected override string GetLanguageForRequest(string methodName, JToken? parameters) + protected override string GetLanguageForRequest(string methodName, JsonElement? parameters) { if (parameters == null) { @@ -134,12 +138,11 @@ protected override string GetLanguageForRequest(string methodName, JToken? param // { "textDocument": { "uri": "" ... } ... } // // We can easily identify the URI for the request by looking for this structure - var textDocumentToken = parameters["textDocument"] ?? parameters["_vs_textDocument"]; - if (textDocumentToken is not null) + if (parameters.Value.TryGetProperty("textDocument", out var textDocumentToken) || + parameters.Value.TryGetProperty("_vs_textDocument", out textDocumentToken)) { - var uriToken = textDocumentToken["uri"]; - Contract.ThrowIfNull(uriToken, "textDocument does not have a uri property"); - var uri = uriToken.ToObject(_jsonSerializer); + var uriToken = textDocumentToken.GetProperty("uri"); + var uri = JsonSerializer.Deserialize(uriToken, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(uri, "Failed to deserialize uri property"); var language = lspWorkspaceManager.GetLanguageForUri(uri); Logger.LogInformation($"Using {language} from request text document"); @@ -150,10 +153,10 @@ protected override string GetLanguageForRequest(string methodName, JToken? param // { "data": { "TextDocument": { "uri": "" ... } ... } ... } // // We can deserialize the data object using our unified DocumentResolveData. - var dataToken = parameters["data"]; - if (dataToken is not null) + //var dataToken = parameters["data"]; + if (parameters.Value.TryGetProperty("data", out var dataToken)) { - var data = dataToken.ToObject(_jsonSerializer); + var data = JsonSerializer.Deserialize(dataToken, ProtocolConversions.LspJsonSerializerOptions); Contract.ThrowIfNull(data, "Failed to document resolve data object"); var language = lspWorkspaceManager.GetLanguageForUri(data.TextDocument.Uri); Logger.LogInformation($"Using {language} from data text document"); @@ -165,7 +168,8 @@ protected override string GetLanguageForRequest(string methodName, JToken? param return LanguageServerConstants.DefaultLanguageName; static bool ShouldUseDefaultLanguage(string methodName) - => methodName switch + { + return methodName switch { Methods.InitializeName => true, Methods.InitializedName => true, @@ -177,6 +181,7 @@ static bool ShouldUseDefaultLanguage(string methodName) Methods.ExitName => true, _ => false, }; + } } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index 13f1f5a4f7fb6..e8c576772aa0e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -12,11 +12,11 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.CodeActions; @@ -93,7 +93,7 @@ void M() var topLevelAction = Assert.Single(results.Where(action => action.Title == titlePath[0])); var introduceConstant = topLevelAction.Children.FirstOrDefault( - r => ((JObject)r.Data!).ToObject()!.UniqueIdentifier == titlePath[1]); + r => JsonSerializer.Deserialize((JsonElement)r.Data!, ProtocolConversions.LspJsonSerializerOptions)!.UniqueIdentifier == titlePath[1]); AssertJsonEquals(expected, introduceConstant); } @@ -323,8 +323,7 @@ private static async Task RunGetCodeActionResolveAsync( private static CodeActionResolveData? GetCodeActionResolveData(CodeAction codeAction) { - return ((JToken)codeAction.Data!).ToObject(); - + return JsonSerializer.Deserialize((JsonElement)codeAction.Data!, ProtocolConversions.LspJsonSerializerOptions); } internal static CodeActionParams CreateCodeActionParams(LSP.Location caret) @@ -349,7 +348,7 @@ internal static VSInternalCodeAction CreateCodeAction( Title = title, Kind = kind, Children = children, - Data = JToken.FromObject(data), + Data = JsonSerializer.SerializeToElement(data, ProtocolConversions.LspJsonSerializerOptions), Diagnostics = diagnostics, Edit = edit, Group = groupName, diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs index 7812d7ac25c1a..dea32cb40c3e7 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -69,7 +69,7 @@ private static async Task ExecuteRunCodeActionCommandAsync( Command = CodeActionsHandler.RunCodeActionCommandName, Arguments = [ - JToken.FromObject(codeActionData) + JsonSerializer.SerializeToElement(codeActionData, ProtocolConversions.LspJsonSerializerOptions) ] }; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs index f3db678c7c4db..0472b4349df1b 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeLens/CSharpCodeLensTests.cs @@ -3,10 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeLens; -using Newtonsoft.Json; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -387,7 +387,7 @@ void UseM() var actualCodeLenses = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCodeLensName, codeLensParamsDoc1, CancellationToken.None); var firstCodeLens = actualCodeLenses.First(); - var data = JsonConvert.DeserializeObject(firstCodeLens.Data!.ToString()); + var data = JsonSerializer.Deserialize(firstCodeLens.Data!.ToString(), ProtocolConversions.LspJsonSerializerOptions); AssertEx.NotNull(data); // Update the document so the syntax version changes diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs index b2b813d4c07ed..c27ea95177c1b 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionFeaturesTests.cs @@ -297,7 +297,7 @@ class A { }"; AssertJsonEquals(completionParams.TextDocument, resolvedItem.Command.Arguments[0]); AssertJsonEquals(expectedEdit, resolvedItem.Command.Arguments[1]); Assert.Equal(false, resolvedItem.Command.Arguments[2]); - Assert.Equal((long)14, resolvedItem.Command.Arguments[3]); + Assert.Equal(14, resolvedItem.Command.Arguments[3]); } [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1755955")] @@ -934,7 +934,7 @@ public async Task TestHandleExceptionFromGetCompletionChange(bool mutatingLspWor AssertJsonEquals(expectedEdit, resolvedItem.Command.Arguments[1]); Assert.Equal(false, resolvedItem.Command.Arguments[2]); - Assert.Equal((long)-1, resolvedItem.Command.Arguments[3]); + Assert.Equal(-1, resolvedItem.Command.Arguments[3]); } } } @@ -991,7 +991,7 @@ public class MyClass : BaseClass AssertJsonEquals(expectedEdit, resolvedItem.Command.Arguments[1]); Assert.Equal(false, resolvedItem.Command.Arguments[2]); - Assert.Equal((long)268, resolvedItem.Command.Arguments[3]); + Assert.Equal(268, resolvedItem.Command.Arguments[3]); } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/vscode-csharp/issues/6495")] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs index 2ff2d3846bb51..d03e70a4b2c26 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text.Adornments; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; @@ -245,7 +244,6 @@ void M() var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "AMethod").ConfigureAwait(false); - Assert.True(clientCompletionItem is not VSInternalCompletionItem); var expected = @"```csharp void A.AMethod(int i) @@ -307,7 +305,6 @@ void M() var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, label: "AMethod").ConfigureAwait(false); - Assert.True(clientCompletionItem is not VSInternalCompletionItem); var expected = @"void A.AMethod(int i) A cref A.AMethod(int) @@ -455,8 +452,7 @@ private static async Task GetCompletionItemToResolveAsync( Assert.NotNull(vsCompletionList.Data); } - var serverCompletionItem = completionList.Items.FirstOrDefault(item => item.Label == label); - var clientCompletionItem = ConvertToClientCompletionItem((T)serverCompletionItem); + var clientCompletionItem = (T)completionList.Items.FirstOrDefault(item => item.Label == label); return clientCompletionItem; } @@ -482,13 +478,6 @@ completionList is VSInternalCompletionList vsCompletionList && return completionList; } - private static T ConvertToClientCompletionItem(T serverCompletionItem) where T : LSP.CompletionItem - { - var serializedItem = JsonConvert.SerializeObject(serverCompletionItem); - var clientCompletionItem = JsonConvert.DeserializeObject(serializedItem); - return clientCompletionItem; - } - private class TestCaretOutOfScopeCompletionService : CompletionService { public TestCaretOutOfScopeCompletionService(SolutionServices services) : base(services, AsynchronousOperationListenerProvider.NullProvider) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs b/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs index 728291fc02f5c..a70495d329fc3 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Configuration/DidChangeConfigurationNotificationHandlerTest.cs @@ -5,13 +5,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler.Configuration; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -199,7 +199,7 @@ public void ClientRegisterCapability(RegistrationParams @registrationParams, Can } [JsonRpcMethod(Methods.WorkspaceConfigurationName, UseSingleObjectParameterDeserialization = true)] - public JArray WorkspaceConfigurationName(ConfigurationParams configurationParams, CancellationToken _) + public List WorkspaceConfigurationName(ConfigurationParams configurationParams, CancellationToken _) { ReceivedWorkspaceConfigurationRequest = true; var expectConfigurationItemsNumber = DidChangeConfigurationNotificationHandler.SupportedOptions.Sum(option => option is IPerLanguageValuedOption ? 2 : 1); @@ -211,7 +211,7 @@ public JArray WorkspaceConfigurationName(ConfigurationParams configurationParams AssertSectionPattern(item.Section); } - return JArray.FromObject(MockClientSideValues); + return MockClientSideValues; } public void SetClientSideOptionValues(bool setToDefaultValue) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs index cb85dd18a0c98..9ee002a27e92e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/DiagnosticRegistrationTests.cs @@ -5,11 +5,11 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Public; -using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using StreamJsonRpc; @@ -53,7 +53,7 @@ public async Task TestPublicDiagnosticSourcesAreRegisteredWhenSupported(bool mut // Get all registrations for diagnostics (note that workspace registrations are registered against document method name). var diagnosticRegistrations = registrations .Where(r => r.Method == Methods.TextDocumentDiagnosticName) - .Select(r => ((JObject)r.RegisterOptions!).ToObject()!); + .Select(r => JsonSerializer.Deserialize((JsonElement)r.RegisterOptions!, ProtocolConversions.LspJsonSerializerOptions)!); Assert.NotEmpty(diagnosticRegistrations); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs index adb5bc1e36834..4eeaea3b54dcb 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/HandlerTests.cs @@ -4,15 +4,13 @@ using System; using System.Composition; -using System.Runtime.Serialization; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CommonLanguageServerProtocol.Framework; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Xunit; @@ -125,14 +123,11 @@ public async Task ThrowsIfDeserializationFails(bool mutatingLspWorkspace) await Assert.ThrowsAsync(async () => await server.ExecuteRequestAsync(TestDocumentHandler.MethodName, request, CancellationToken.None)); } - [DataContract] - internal record TestRequestTypeOne([property: DataMember(Name = "textDocument"), JsonProperty(Required = Required.Always)] TextDocumentIdentifier TextDocumentIdentifier); + internal record TestRequestTypeOne([property: JsonPropertyName("textDocument"), JsonRequired] TextDocumentIdentifier TextDocumentIdentifier); - [DataContract] - internal record TestRequestTypeTwo([property: DataMember(Name = "textDocument"), JsonProperty(Required = Required.Always)] TextDocumentIdentifier TextDocumentIdentifier); + internal record TestRequestTypeTwo([property: JsonPropertyName("textDocument"), JsonRequired] TextDocumentIdentifier TextDocumentIdentifier); - [DataContract] - internal record TestRequestTypeThree([property: DataMember(Name = "someValue")] string SomeValue); + internal record TestRequestTypeThree([property: JsonPropertyName("someValue")] string SomeValue); [ExportCSharpVisualBasicStatelessLspService(typeof(TestDocumentHandler)), PartNotDiscoverable, Shared] [LanguageServerEndpoint(MethodName, LanguageServerConstants.DefaultLanguageName)] diff --git a/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index d4d0801f11fcc..f6392ba5acec8 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -12,12 +12,12 @@ using Roslyn.LanguageServer.Protocol; using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Text; -using Newtonsoft.Json; using Roslyn.Test.Utilities; using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; +using System.Text.Json; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.InlayHint { @@ -134,7 +134,7 @@ void M() var actualInlayHints = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentInlayHintName, inlayHintParams, CancellationToken.None); var firstInlayHint = actualInlayHints.First(); - var data = JsonConvert.DeserializeObject(firstInlayHint.Data!.ToString()); + var data = JsonSerializer.Deserialize(firstInlayHint.Data!.ToString(), ProtocolConversions.LspJsonSerializerOptions); AssertEx.NotNull(data); var firstResultId = data.ResultId; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 7607c7dc55667..7bc5c9b1c2c85 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -5,13 +5,15 @@ #nullable disable using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.ReferenceHighlighting; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Roslyn.Text.Adornments; using Roslyn.Utilities; @@ -91,7 +93,7 @@ void M2() // with the test creating one, and the handler another, we have to unwrap. // Additionally, the VS LSP protocol specifies T from IProgress as an object and not as the actual VSInternalReferenceItem // so we have to correctly convert the JObject into the expected type. - results = progress.GetValues().Select(reference => ((JArray)reference).ToObject()).SelectMany(v => v).ToArray(); + results = progress.GetValues().SelectMany(r => (List)r).Select(r => JsonSerializer.Deserialize((JsonElement)r, ProtocolConversions.LspJsonSerializerOptions)).ToArray(); Assert.NotNull(results); Assert.NotEmpty(results); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs index 73f22e2e68fc3..40e6fa2e14677 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/VSTypeScriptHandlerTests.cs @@ -111,7 +111,7 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); var servicesProvider = workspace.ExportProvider.GetExportedValue(); - var messageFormatter = CreateJsonMessageFormatter(); + var messageFormatter = RoslynLanguageServer.CreateJsonMessageFormatter(); var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, messageFormatter)) { ExceptionStrategy = ExceptionProcessing.ISerializable, @@ -120,7 +120,7 @@ private static RoslynLanguageServer CreateLanguageServer(Stream inputStream, Str var logger = NoOpLspLogger.Instance; var languageServer = new RoslynLanguageServer( - servicesProvider, jsonRpc, messageFormatter.JsonSerializer, + servicesProvider, jsonRpc, messageFormatter.JsonSerializerOptions, capabilitiesProvider, logger, workspace.Services.HostServices, diff --git a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb index f736e974e5c10..ce1bdcd6704e8 100644 --- a/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb +++ b/src/Features/Lsif/GeneratorTest/SemanticTokensTests.vb @@ -3,10 +3,10 @@ ' See the LICENSE file in the project root for more information. Imports System.Linq +Imports System.Text.Json Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Graph Imports Microsoft.CodeAnalysis.Test.Utilities -Imports Newtonsoft.Json Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests @@ -44,7 +44,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests Dim document = semanticTokensWorkspace.CurrentSolution.Projects.Single().Documents.Single() Dim tokens = lsif.GetSemanticTokens(document) - Dim serializedTokens = JsonConvert.SerializeObject(tokens) + Dim serializedTokens = JsonSerializer.Serialize(tokens) Assert.Equal(expectedTokens, serializedTokens) End Using diff --git a/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs index 93dc75dd8acda..e3c504f40fe0a 100644 --- a/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/AbstractRazorLanguageServerFactoryWrapper.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; +using System.Text.Json; using Microsoft.CodeAnalysis.Host; -using Newtonsoft.Json; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor /// internal abstract class AbstractRazorLanguageServerFactoryWrapper { - internal abstract IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, IRazorTestCapabilitiesProvider capabilitiesProvider, HostServices hostServices); + internal abstract IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider capabilitiesProvider, HostServices hostServices); internal abstract DocumentInfo CreateDocumentInfo( DocumentId id, @@ -31,6 +31,6 @@ internal abstract DocumentInfo CreateDocumentInfo( /// /// Supports the creation of a Roslyn LSP server for functional tests /// - internal abstract void AddJsonConverters(JsonSerializer jsonSerializer); + internal abstract void AddJsonConverters(JsonSerializerOptions options); } } diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs b/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs index 425cfce0e740c..8891b52c7c275 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs +++ b/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs @@ -4,13 +4,13 @@ using System; using System.Composition; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; @@ -74,12 +74,8 @@ private void InitializeRazor(ClientCapabilities clientCapabilities, RequestConte // UIContext will already be active, so this method will be immediately called on the new instance. if (cancellationToken.IsCancellationRequested) return; - var serializer = new JsonSerializer(); - serializer.AddVSInternalExtensionConverters(); - var serializerSettings = new JsonSerializerSettings { Converters = serializer.Converters }; - // We use a string to pass capabilities to/from Razor to avoid version issues with the Protocol DLL - var serializedClientCapabilities = JsonConvert.SerializeObject(clientCapabilities, serializerSettings); + var serializedClientCapabilities = JsonSerializer.Serialize(clientCapabilities, ProtocolConversions.LspJsonSerializerOptions); var razorCohostClientLanguageServerManager = new RazorCohostClientLanguageServerManager(clientLanguageServerManager!); var requestContext = new RazorCohostRequestContext(context); diff --git a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs index 989fc0e911cfb..81cfa6493e4cd 100644 --- a/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/RazorLanguageServerFactoryWrapper.cs @@ -5,11 +5,11 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Text.Json; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.VisualStudio.Composition; -using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; @@ -33,10 +33,10 @@ public RazorLanguageServerFactoryWrapper(ILanguageServerFactory languageServerFa _languageServerFactory = languageServerFactory; } - internal override IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializer jsonSerializer, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) + internal override IRazorLanguageServerTarget CreateLanguageServer(JsonRpc jsonRpc, JsonSerializerOptions options, IRazorTestCapabilitiesProvider razorCapabilitiesProvider, HostServices hostServices) { - var capabilitiesProvider = new RazorCapabilitiesProvider(razorCapabilitiesProvider); - var languageServer = _languageServerFactory.Create(jsonRpc, jsonSerializer, capabilitiesProvider, WellKnownLspServerKinds.RazorLspServer, NoOpLspLogger.Instance, hostServices); + var capabilitiesProvider = new RazorCapabilitiesProvider(razorCapabilitiesProvider, options); + var languageServer = _languageServerFactory.Create(jsonRpc, options, capabilitiesProvider, WellKnownLspServerKinds.RazorLspServer, NoOpLspLogger.Instance, hostServices); return new RazorLanguageServerTargetWrapper(languageServer); } @@ -65,27 +65,29 @@ internal override DocumentInfo CreateDocumentInfo( .WithDocumentServiceProvider(documentServiceProvider); } - internal override void AddJsonConverters(JsonSerializer jsonSerializer) + internal override void AddJsonConverters(JsonSerializerOptions options) { - VSInternalExtensionUtilities.AddVSInternalExtensionConverters(jsonSerializer); + VSInternalExtensionUtilities.AddVSInternalExtensionConverters(options); } private class RazorCapabilitiesProvider : ICapabilitiesProvider { private readonly IRazorTestCapabilitiesProvider _razorTestCapabilitiesProvider; + private readonly JsonSerializerOptions _options; - public RazorCapabilitiesProvider(IRazorTestCapabilitiesProvider razorTestCapabilitiesProvider) + public RazorCapabilitiesProvider(IRazorTestCapabilitiesProvider razorTestCapabilitiesProvider, JsonSerializerOptions options) { _razorTestCapabilitiesProvider = razorTestCapabilitiesProvider; + _options = options; } public ServerCapabilities GetCapabilities(ClientCapabilities clientCapabilities) { // To avoid exposing types from MS.VS.LanguageServer.Protocol types we serialize and deserialize the capabilities // so we can just pass string around. This is obviously not great for perf, but it is only used in Razor tests. - var clientCapabilitiesJson = JsonConvert.SerializeObject(clientCapabilities); + var clientCapabilitiesJson = JsonSerializer.Serialize(clientCapabilities, _options); var serverCapabilitiesJson = _razorTestCapabilitiesProvider.GetServerCapabilitiesJson(clientCapabilitiesJson); - var serverCapabilities = JsonConvert.DeserializeObject(serverCapabilitiesJson); + var serverCapabilities = JsonSerializer.Deserialize(serverCapabilitiesJson, _options); if (serverCapabilities is null) { diff --git a/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs b/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs index a37288b57bf7e..bec4c319fe13a 100644 --- a/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs +++ b/src/Tools/ExternalAccess/Xaml/External/ResolveDataConversions.cs @@ -3,8 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Text.Json; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Newtonsoft.Json.Linq; using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; @@ -21,7 +21,7 @@ public static object ToResolveData(object data, Uri uri) public static (object? data, Uri? uri) FromResolveData(object? requestData) { Contract.ThrowIfNull(requestData); - var resolveData = ((JToken)requestData).ToObject(); + var resolveData = JsonSerializer.Deserialize((JsonElement)requestData); return (resolveData?.Data, resolveData?.Document.Uri); } @@ -35,9 +35,9 @@ internal static object ToCachedResolveData(object data, Uri uri, ResolveDataCach internal static (object? data, Uri? uri) FromCachedResolveData(object? lspData, ResolveDataCache resolveDataCache) { DataIdResolveData? resolveData; - if (lspData is JToken token) + if (lspData is JsonElement token) { - resolveData = token.ToObject(); + resolveData = JsonSerializer.Deserialize(token); Assumes.Present(resolveData); } else From 1a4c3f429fe13a2e928c800cebbf93154447095a Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 2 May 2024 14:51:36 -0700 Subject: [PATCH 016/423] Use Newtonsoft for client side serialization in Document Outline as we have not switched to STJ on the client side --- eng/Directory.Packages.props | 6 +- ...rverProtocolTests.InitializationOptions.cs | 3 + .../AbstractLanguageServerProtocolTests.cs | 20 ++- .../Symbols/RoslynDocumentSymbolParams.cs | 5 +- .../DocumentOutlineTestsBase.cs | 58 ++++---- .../DocumentOutlineViewModel.cs | 1 + .../DocumentOutlineViewModel_Utilities.cs | 61 ++++----- .../DocumentSymbolNewtonsoft.cs | 127 ++++++++++++++++++ 8 files changed, 211 insertions(+), 70 deletions(-) create mode 100644 src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 8adaa8740c2ac..ce2c48d67d235 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -56,7 +56,7 @@ Visual Studio --> - + @@ -99,7 +99,7 @@ - + @@ -138,7 +138,7 @@ - + diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs index a817940d49017..8998fcec76edd 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.InitializationOptions.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Options; +using StreamJsonRpc; using LSP = Roslyn.LanguageServer.Protocol; namespace Roslyn.Test.Utilities @@ -22,10 +23,12 @@ internal readonly record struct InitializationOptions() internal LSP.ClientCapabilities ClientCapabilities { get; init; } = new LSP.ClientCapabilities(); internal WellKnownLspServerKinds ServerKind { get; init; } = WellKnownLspServerKinds.AlwaysActiveVSLspServer; internal Action? OptionUpdater { get; init; } = null; + internal bool CallInitialize { get; init; } = true; internal bool CallInitialized { get; init; } = true; internal object? ClientTarget { get; init; } = null; internal string? Locale { get; init; } = null; internal IEnumerable? AdditionalAnalyzers { get; init; } = null; + internal IJsonRpcMessageFormatter? ClientMessageFormatter { get; init; } = null; } } } diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 0cdfa7bd0739d..1fa9da38fb528 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -531,7 +531,8 @@ private TestLspServer( LSP.ClientCapabilities clientCapabilities, RoslynLanguageServer target, Stream clientStream, - object? clientTarget = null) + object? clientTarget = null, + IJsonRpcMessageFormatter? clientMessageFormatter = null) { TestWorkspace = testWorkspace; ClientCapabilities = clientCapabilities; @@ -540,7 +541,9 @@ private TestLspServer( LanguageServer = target; - _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, RoslynLanguageServer.CreateJsonMessageFormatter()), clientTarget) + clientMessageFormatter ??= RoslynLanguageServer.CreateJsonMessageFormatter(); + + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, clientMessageFormatter), clientTarget) { ExceptionStrategy = ExceptionProcessing.ISerializable, }; @@ -566,13 +569,16 @@ internal static async Task CreateAsync(EditorTestWorkspace testWo var (clientStream, serverStream) = FullDuplexStream.CreatePair(); var languageServer = CreateLanguageServer(serverStream, serverStream, testWorkspace, initializationOptions.ServerKind, logger); - var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, languageServer, clientStream, initializationOptions.ClientTarget); + var server = new TestLspServer(testWorkspace, locations, initializationOptions.ClientCapabilities, languageServer, clientStream, initializationOptions.ClientTarget, initializationOptions.ClientMessageFormatter); - await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams + if (initializationOptions.CallInitialize) { - Capabilities = initializationOptions.ClientCapabilities, - Locale = initializationOptions.Locale, - }, CancellationToken.None); + await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams + { + Capabilities = initializationOptions.ClientCapabilities, + Locale = initializationOptions.Locale, + }, CancellationToken.None); + } if (initializationOptions.CallInitialized) { diff --git a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs index ac79269e2d44b..15f26944b6da3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Symbols/RoslynDocumentSymbolParams.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -18,7 +18,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal class RoslynDocumentSymbolParams : DocumentSymbolParams { - [JsonProperty(PropertyName = "useHierarchicalSymbols", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonPropertyName("useHierarchicalSymbols")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public bool UseHierarchicalSymbols { get; set; } } } diff --git a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs index d386add5b3e2e..9e67add1eaba5 100644 --- a/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs +++ b/src/VisualStudio/CSharp/Test/DocumentOutline/DocumentOutlineTestsBase.cs @@ -2,9 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -12,8 +11,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Editor.UnitTests; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.UnitTests; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -22,9 +19,7 @@ using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.LanguageServices.DocumentOutline; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Threading; -using Moq; -using Newtonsoft.Json.Linq; +using StreamJsonRpc; using Xunit.Abstractions; using static Roslyn.Test.Utilities.AbstractLanguageServerProtocolTests; using IAsyncDisposable = System.IAsyncDisposable; @@ -49,7 +44,7 @@ protected class DocumentOutlineTestMocks : IAsyncDisposable private readonly IAsyncDisposable _disposable; internal DocumentOutlineTestMocks( - LanguageServiceBrokerCallback languageServiceBrokerCallback, + LanguageServiceBrokerCallback languageServiceBrokerCallback, IThreadingContext threadingContext, EditorTestWorkspace workspace, IAsyncDisposable disposable) @@ -61,7 +56,7 @@ internal DocumentOutlineTestMocks( TextBuffer = workspace.Documents.Single().GetTextBuffer(); } - internal LanguageServiceBrokerCallback LanguageServiceBrokerCallback { get; } + internal LanguageServiceBrokerCallback LanguageServiceBrokerCallback { get; } internal IThreadingContext ThreadingContext { get; } @@ -84,27 +79,26 @@ protected async Task CreateMocksAsync(string code) var workspace = EditorTestWorkspace.CreateCSharp(code, composition: s_composition); var threadingContext = workspace.GetService(); - var clientCapabilities = new LSP.ClientCapabilities() + var testLspServer = await CreateTestLspServerAsync(workspace, new InitializationOptions { - TextDocument = new LSP.TextDocumentClientCapabilities() - { - DocumentSymbol = new LSP.DocumentSymbolSetting() - { - HierarchicalDocumentSymbolSupport = true - } - } - }; - - var testLspServer = await CreateTestLspServerAsync(workspace, new InitializationOptions { ClientCapabilities = clientCapabilities }); + // Set the message formatter to use newtonsoft on the client side to match real behavior. + // Also avoid calling initialize / initialized as the test harness uses types only compatible with STJ. + // TODO - switch back to STJ with https://github.com/dotnet/roslyn/issues/73317 + ClientMessageFormatter = new JsonMessageFormatter(), + CallInitialize = false, + CallInitialized = false + }); var mocks = new DocumentOutlineTestMocks(RequestAsync, threadingContext, workspace, testLspServer); return mocks; - async Task RequestAsync(ITextBuffer textBuffer, Func capabilitiesFilter, string languageServerName, string method, Func parameterFactory, CancellationToken cancellationToken) + async Task RequestAsync(Request request, CancellationToken cancellationToken) { - var request = parameterFactory(textBuffer.CurrentSnapshot).ToObject(); - var response = await testLspServer.ExecuteRequestAsync(method, request!, cancellationToken); - return new ManualInvocationResponse(string.Empty, JToken.FromObject(response!)); + var docRequest = (DocumentRequest)request; + var parameters = docRequest.ParameterFactory(docRequest.TextBuffer.CurrentSnapshot); + var response = await testLspServer.ExecuteRequestAsync(request.Method, parameters, cancellationToken); + + return response; } } @@ -139,7 +133,21 @@ private async Task CreateTestLspServerAsync(EditorTestWorkspace w var workspaceWaiter = operations.GetWaiter(FeatureAttribute.Workspace); await workspaceWaiter.ExpeditedWaitAsync(); - return await TestLspServer.CreateAsync(workspace, initializationOptions, _logger); + var server = await TestLspServer.CreateAsync(workspace, initializationOptions, _logger); + + // We disable the default test initialize call because the default test harness intialize types only support STJ (not newtonsoft). + // We only care that initialize has been called with some capability, so call with simple objects. + // TODO - remove with switch to STJ in https://github.com/dotnet/roslyn/issues/73317 + await server.ExecuteRequestAsync(Roslyn.LanguageServer.Protocol.Methods.InitializeName, new NewtonsoftInitializeParams() { Capabilities = new object() }, CancellationToken.None); + + return server; + } + + [DataContract] + private class NewtonsoftInitializeParams + { + [DataMember(Name = "capabilities")] + internal object? Capabilities { get; set; } } } } diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs index e9143db12bc2c..5be44e0a29717 100644 --- a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; +using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs index 7b0668f518478..4d632cf85c644 100644 --- a/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentOutlineViewModel_Utilities.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -12,6 +13,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.Text; using Newtonsoft.Json.Linq; @@ -23,8 +25,7 @@ namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline; using LspDocumentSymbol = DocumentSymbol; using Range = Roslyn.LanguageServer.Protocol.Range; -internal delegate Task LanguageServiceBrokerCallback( - ITextBuffer textBuffer, Func capabilitiesFilter, string languageServerName, string method, Func parameterFactory, CancellationToken cancellationToken); +internal delegate Task LanguageServiceBrokerCallback(Request request, CancellationToken cancellationToken); internal sealed partial class DocumentOutlineViewModel { @@ -32,34 +33,29 @@ internal sealed partial class DocumentOutlineViewModel /// Makes an LSP document symbol request and returns the response and the text snapshot used at /// the time the LSP client sends the request to the server. /// - public static async Task<(JToken response, ITextSnapshot snapshot)?> DocumentSymbolsRequestAsync( + public static async Task<(DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol[] response, ITextSnapshot snapshot)?> DocumentSymbolsRequestAsync( ITextBuffer textBuffer, - LanguageServiceBrokerCallback callbackAsync, + LanguageServiceBrokerCallback callbackAsync, string textViewFilePath, CancellationToken cancellationToken) { ITextSnapshot? requestSnapshot = null; - JToken ParameterFunction(ITextSnapshot snapshot) + + var request = new DocumentRequest() { - requestSnapshot = snapshot; - return JToken.FromObject(new RoslynDocumentSymbolParams() + Method = Methods.TextDocumentDocumentSymbolName, + LanguageServerName = WellKnownLspServerKinds.AlwaysActiveVSLspServer.ToUserVisibleString(), + TextBuffer = textBuffer, + ParameterFactory = (snapshot) => { - UseHierarchicalSymbols = true, - TextDocument = new TextDocumentIdentifier() - { - Uri = ProtocolConversions.CreateAbsoluteUri(textViewFilePath) - } - }); - } + requestSnapshot = snapshot; + return new DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbolParams( + new DocumentSymbolNewtonsoft.NewtonsoftTextDocumentIdentifier(ProtocolConversions.CreateAbsoluteUri(textViewFilePath)), + UseHierarchicalSymbols: true); + } + }; - var manualResponse = await callbackAsync( - textBuffer: textBuffer, - method: Methods.TextDocumentDocumentSymbolName, - capabilitiesFilter: _ => true, - languageServerName: WellKnownLspServerKinds.AlwaysActiveVSLspServer.ToUserVisibleString(), - parameterFactory: ParameterFunction, - cancellationToken: cancellationToken).ConfigureAwait(false); - var response = manualResponse?.Response; + var response = await callbackAsync(request, cancellationToken).ConfigureAwait(false); // The request snapshot or response can be null if there is no LSP server implementation for // the document symbol request for that language. @@ -100,12 +96,8 @@ JToken ParameterFunction(ITextSnapshot snapshot) /// ] /// } /// ] - public static ImmutableArray CreateDocumentSymbolData(JToken token, ITextSnapshot textSnapshot) + public static ImmutableArray CreateDocumentSymbolData(DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol[] documentSymbols, ITextSnapshot textSnapshot) { - // If we get no value results back, treat that as empty results. That way we don't keep showing stale - // results if the server starts returning nothing. - var documentSymbols = token.ToObject() ?? []; - // Obtain a flat list of all the document symbols sorted by location in the document. var allSymbols = documentSymbols .SelectMany(x => x.Children) @@ -124,7 +116,7 @@ public static ImmutableArray CreateDocumentSymbolData(JToken // Returns the symbol in the list at index start (the parent symbol) with the following symbols in the list // (descendants) appropriately nested into the parent. - DocumentSymbolData NestDescendantSymbols(ImmutableArray allSymbols, int start, out int newStart) + DocumentSymbolData NestDescendantSymbols(ImmutableArray allSymbols, int start, out int newStart) { var currentParent = allSymbols[start]; start++; @@ -149,7 +141,7 @@ DocumentSymbolData NestDescendantSymbols(ImmutableArray al // Return the nested parent symbol. return new DocumentSymbolData( currentParent.Detail ?? currentParent.Name, - currentParent.Kind, + (Roslyn.LanguageServer.Protocol.SymbolKind)currentParent.Kind, (Glyph)currentParent.Glyph, GetSymbolRangeSpan(currentParent.Range), GetSymbolRangeSpan(currentParent.SelectionRange), @@ -157,15 +149,18 @@ DocumentSymbolData NestDescendantSymbols(ImmutableArray al } // Returns whether the child symbol is in range of the parent symbol. - static bool Contains(LspDocumentSymbol parent, LspDocumentSymbol child) + static bool Contains(DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol parent, DocumentSymbolNewtonsoft.NewtonsoftRoslynDocumentSymbol child) { - var parentRange = ProtocolConversions.RangeToLinePositionSpan(parent.Range); - var childRange = ProtocolConversions.RangeToLinePositionSpan(child.Range); + var parentRange = RangeToLinePositionSpan(parent.Range); + var childRange = RangeToLinePositionSpan(child.Range); return childRange.Start > parentRange.Start && childRange.End <= parentRange.End; + + static LinePositionSpan RangeToLinePositionSpan(DocumentSymbolNewtonsoft.NewtonsoftRange range) + => new(new LinePosition(range.Start.Line, range.Start.Character), new LinePosition(range.End.Line, range.End.Character)); } // Converts a Document Symbol Range to a SnapshotSpan within the text snapshot used for the LSP request. - SnapshotSpan GetSymbolRangeSpan(Range symbolRange) + SnapshotSpan GetSymbolRangeSpan(DocumentSymbolNewtonsoft.NewtonsoftRange symbolRange) { var originalStartPosition = textSnapshot.GetLineFromLineNumber(symbolRange.Start.Line).Start.Position + symbolRange.Start.Character; var originalEndPosition = textSnapshot.GetLineFromLineNumber(symbolRange.End.Line).Start.Position + symbolRange.End.Character; diff --git a/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs b/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs new file mode 100644 index 0000000000000..962a451c2c5a5 --- /dev/null +++ b/src/VisualStudio/Core/Def/DocumentOutline/DocumentSymbolNewtonsoft.cs @@ -0,0 +1,127 @@ +// 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.Globalization; +using System.Runtime.Serialization; +using Microsoft.CodeAnalysis.LanguageServer; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Microsoft.VisualStudio.LanguageServices.DocumentOutline; + +/// +/// These are very temporary types that we need in order to serialize document symbol data +/// using Newtonsoft instead of System.Text.Json +/// +/// We currently must support Newtonsoft serialization here because we have not yet opted into using STJ +/// in the VS language server client (and so the client will serialize the request using Newtonsoft). +/// +/// https://github.com/dotnet/roslyn/pull/72675 tracks opting in the client to STJ. +/// TODO - everything in this type should be deleted once the client side is using STJ. +/// +internal class DocumentSymbolNewtonsoft +{ + private class NewtonsoftDocumentUriConverter : JsonConverter + { + /// + public override bool CanConvert(Type objectType) + { + return true; + } + + /// + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + reader = reader ?? throw new ArgumentNullException(nameof(reader)); + if (reader.TokenType == JsonToken.String) + { + var token = JToken.ReadFrom(reader); + var uri = new Uri(token.ToObject()); + + return uri; + } + else if (reader.TokenType == JsonToken.Null) + { + return null; + } + + throw new JsonSerializationException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.DocumentUriSerializationError, reader.Value)); + } + + /// + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + writer = writer ?? throw new ArgumentNullException(nameof(writer)); + + if (value is Uri uri) + { + var token = JToken.FromObject(uri.AbsoluteUri); + token.WriteTo(writer); + } + else + { + throw new ArgumentException($"{nameof(value)} must be of type {nameof(Uri)}"); + } + } + } + + [DataContract] + internal record NewtonsoftTextDocumentIdentifier([property: DataMember(Name = "uri"), JsonConverter(typeof(NewtonsoftDocumentUriConverter))] Uri Uri); + + [DataContract] + internal record NewtonsoftRoslynDocumentSymbolParams( + [property: DataMember(Name = "textDocument")] NewtonsoftTextDocumentIdentifier TextDocument, + [property: DataMember(Name = "useHierarchicalSymbols"), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] bool UseHierarchicalSymbols); + + [DataContract] + internal record NewtonsoftRoslynDocumentSymbol( + [property: DataMember(IsRequired = true, Name = "name")] string Name, + [property: DataMember(Name = "detail")][property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Detail, + [property: DataMember(Name = "kind")] NewtonsoftSymbolKind Kind, + [property: DataMember(Name = "deprecated")][property: JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] bool Deprecated, + [property: DataMember(IsRequired = true, Name = "range")] NewtonsoftRange Range, + [property: DataMember(IsRequired = true, Name = "selectionRange")] NewtonsoftRange SelectionRange, + [property: DataMember(Name = "children")][property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] NewtonsoftRoslynDocumentSymbol[]? Children, + [property: DataMember(Name = "glyph")] int Glyph); + + [DataContract] + internal record NewtonsoftRange( + [property: DataMember(Name = "start"), JsonProperty(Required = Required.Always)] NewtonsoftPosition Start, + [property: DataMember(Name = "end"), JsonProperty(Required = Required.Always)] NewtonsoftPosition End); + + [DataContract] + internal record NewtonsoftPosition([property: DataMember(Name = "line")] int Line, [property: DataMember(Name = "character")] int Character); + + [DataContract] + internal enum NewtonsoftSymbolKind + { + File = 1, + Module = 2, + Namespace = 3, + Package = 4, + Class = 5, + Method = 6, + Property = 7, + Field = 8, + Constructor = 9, + Enum = 10, + Interface = 11, + Function = 12, + Variable = 13, + Constant = 14, + String = 15, + Number = 16, + Boolean = 17, + Array = 18, + Object = 19, + Key = 20, + Null = 21, + EnumMember = 22, + Struct = 23, + Event = 24, + Operator = 25, + TypeParameter = 26, + } +} From cade3884c639ffc7168d59335c8099f2adfebcd2 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 2 May 2024 16:50:08 -0700 Subject: [PATCH 017/423] Cleanup a few more instances of newtonsoft and warnings --- .../AbstractInProcLanguageClient.cs | 2 + .../AbstractLanguageClientMiddleLayer.cs | 6 ++- .../CodeActions/CodeActionResolveData.cs | 6 +-- .../CodeActions/RoslynFixAllCodeAction.cs | 6 +-- ...rationNotificationHandler_OnInitialized.cs | 4 +- .../Handler/Testing/RunTestsPartialResult.cs | 3 +- .../JsonConverterCollectionUtilities.cs | 19 ------- .../Converters/DropProgressConverter.cs | 52 ------------------- 8 files changed, 14 insertions(+), 84 deletions(-) delete mode 100644 src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs delete mode 100644 src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs diff --git a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs index cea46937b42b3..8eccf1e6e410c 100644 --- a/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/LanguageServer/AbstractInProcLanguageClient.cs @@ -34,7 +34,9 @@ internal abstract partial class AbstractInProcLanguageClient( AbstractLanguageClientMiddleLayer? middleLayer = null) : ILanguageClient, ILanguageServerFactory, ICapabilitiesProvider, ILanguageClientCustomMessage2 { private readonly IThreadingContext _threadingContext = threadingContext; +#pragma warning disable CS0618 // Type or member is obsolete - blocked on Razor switching to new APIs for STJ - https://github.com/dotnet/roslyn/issues/73317 private readonly ILanguageClientMiddleLayer? _middleLayer = middleLayer; +#pragma warning restore CS0618 // Type or member is obsolete private readonly ILspServiceLoggerFactory _lspLoggerFactory = lspLoggerFactory; private readonly ExportProvider _exportProvider = exportProvider; diff --git a/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs b/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs index e7c26c5b245b0..1ecbf95264cf7 100644 --- a/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs +++ b/src/EditorFeatures/Core/LanguageServer/AbstractLanguageClientMiddleLayer.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -9,11 +9,13 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient; +#pragma warning disable CS0618 // Type or member is obsolete - blocked on Razor switching to new APIs for STJ - https://github.com/dotnet/roslyn/issues/73317 internal abstract class AbstractLanguageClientMiddleLayer : ILanguageClientMiddleLayer +#pragma warning restore CS0618 // Type or member is obsolete { public abstract bool CanHandle(string methodName); public abstract Task HandleNotificationAsync(string methodName, JToken methodParam, Func sendNotification); public abstract Task HandleRequestAsync(string methodName, JToken methodParam, Func> sendRequest); -} \ No newline at end of file +} diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs index d7e32c9199d8e..ce2f356f4730a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/CodeActionResolveData.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions @@ -33,10 +33,10 @@ internal class CodeActionResolveData public string[] CodeActionPath { get; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string[]? FixAllFlavors { get; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public ImmutableArray? NestedCodeActions { get; } public CodeActionResolveData( diff --git a/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs b/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs index 03fdfc48564c3..2d343c92d434e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs +++ b/src/Features/LanguageServer/Protocol/Handler/CodeActions/RoslynFixAllCodeAction.cs @@ -2,15 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Runtime.Serialization; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -[DataContract] internal sealed class RoslynFixAllCodeAction(string scope) : CodeAction { - [JsonProperty(PropertyName = "scope")] + [JsonPropertyName("scope")] public string Scope { get; } = scope; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs index f1e2f16defb48..19acbf8650cc1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Configuration/DidChangeConfigurationNotificationHandler_OnInitialized.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Roslyn.LanguageServer.Protocol; -using Newtonsoft.Json.Linq; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Configuration { @@ -15,7 +15,7 @@ public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, Requ { if (clientCapabilities?.Workspace?.DidChangeConfiguration?.DynamicRegistration is true) { - await _clientLanguageServerManager.SendRequestAsync( + await _clientLanguageServerManager.SendRequestAsync( methodName: Methods.ClientRegisterCapabilityName, @params: new RegistrationParams() { diff --git a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs index 9e6972637b9c2..91e81544887f1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Testing/RunTestsPartialResult.cs @@ -3,12 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; -using Newtonsoft.Json; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Testing; internal record RunTestsPartialResult( [property: JsonPropertyName("stage")] string Stage, [property: JsonPropertyName("message")] string Message, - [property: JsonPropertyName("progress"), JsonProperty(NullValueHandling = NullValueHandling.Ignore)] TestProgress? Progress + [property: JsonPropertyName("progress"), JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] TestProgress? Progress ); diff --git a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs b/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs deleted file mode 100644 index 207ed168f25e6..0000000000000 --- a/src/Features/LanguageServer/Protocol/Protocol/Converters/JsonConverterCollectionUtilities.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Roslyn.LanguageServer.Protocol -{ - using Newtonsoft.Json; - - /// - /// Class containing extension method to thread-safely manage operations. - /// - internal static class JsonConverterCollectionUtilities - { - /// - /// Lock used for modifications to Converters collection. - /// - public static readonly object ConvertersLock = new object(); - } -} diff --git a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs b/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs deleted file mode 100644 index 868de33bddf28..0000000000000 --- a/src/Features/LanguageServer/Protocol/Protocol/Internal/Converters/DropProgressConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Roslyn.LanguageServer.Protocol -{ - using System; - using System.Linq; - using Newtonsoft.Json; - - /// - /// Converter used to deserialize objects dropping any property. - /// - internal class DropProgressConverter : JsonConverter - { - /// - public override bool CanWrite => true; - - /// - /// Static method to get a containing a . - /// - /// object containing a . - public static JsonSerializer CreateSerializer() - { - var serializer = new JsonSerializer(); - serializer.Converters.Add(new DropProgressConverter()); - return serializer; - } - - /// - public override bool CanConvert(Type objectType) - { - var isIProgressOfT = objectType.IsConstructedGenericType && objectType.GetGenericTypeDefinition().Equals(typeof(IProgress<>)); - var implementsIProgressOfT = objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(IProgress<>))); - - return isIProgressOfT || implementsIProgressOfT; - } - - /// - public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) - { - // We deserialize all IProgress objects as null. - return null; - } - - /// - public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) - { - writer.WriteNull(); - } - } -} From e4a799bc4630d3806e78fea6dc9eaa9f60916dba Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Fri, 3 May 2024 12:53:49 +0000 Subject: [PATCH 018/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From c49cd3b10457c195cced3b97348333225ae9984e Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Sat, 4 May 2024 12:36:55 +0000 Subject: [PATCH 019/423] Update dependencies from https://github.com/dotnet/arcade build 20240425.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24204.3 -> To Version 8.0.0-beta.24225.1 From 293d8b8339fc47b2130716d96762fac5cdcf2618 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 11:17:22 -0700 Subject: [PATCH 020/423] Simplify grammar code --- .../Grammar/GrammarGenerator.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index a56836798245d..b7d4270073c54 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -53,8 +53,8 @@ public static string Run(List types) var originalLastFieldKinds = lastField.Kinds.ToList(); for (int i = 0; i < originalFirstFieldKinds.Count; i++) { - firstField.Kinds = new List { originalFirstFieldKinds[i] }; - lastField.Kinds = new List { originalLastFieldKinds[i] }; + firstField.Kinds = [originalFirstFieldKinds[i]]; + lastField.Kinds = [originalLastFieldKinds[i]]; rules[type.Name].Add(HandleChildren(type.Children)); } } @@ -74,11 +74,13 @@ public static string Run(List types) } } + // rules.Add() + // The grammar will bottom out with certain lexical productions. Create rules for these. var lexicalRules = rules.Values.SelectMany(ps => ps).SelectMany(p => p.ReferencedRules) .Where(r => !rules.TryGetValue(r, out var productions) || productions.Count == 0).ToArray(); foreach (var name in lexicalRules) - rules[name] = new List { new Production("/* see lexical specification */") }; + rules[name] = [new("/* see lexical specification */")]; var seen = new HashSet(); @@ -114,13 +116,17 @@ void processRule(string name, ref string result) } private static Production Join(string delim, IEnumerable productions) - => new Production(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); + => new(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); private static Production HandleChildren(IEnumerable children, string delim = " ") => Join(delim, children.Select(child => - child is Choice c ? HandleChildren(c.Children, delim: " | ").Parenthesize().Suffix("?", when: c.Optional) : - child is Sequence s ? HandleChildren(s.Children).Parenthesize() : - child is Field f ? HandleField(f).Suffix("?", when: f.IsOptional) : throw new InvalidOperationException())); + child switch + { + Choice c => HandleChildren(c.Children, delim: " | ").Parenthesize().Suffix("?", when: c.Optional), + Sequence s => HandleChildren(s.Children).Parenthesize(), + Field f => HandleField(f).Suffix("?", when: f.IsOptional), + _ => throw new InvalidOperationException(), + })); private static Production HandleField(Field field) // 'bool' fields are for a few properties we generate on DirectiveTrivia. They're not @@ -162,26 +168,20 @@ private static IEnumerable GetMembers() where TEnum : struct, Enum => (IEnumerable)Enum.GetValues(typeof(TEnum)); private static Production RuleReference(string name) - => new Production( + => new( s_normalizationRegex.Replace(name.EndsWith("Syntax") ? name[..^"Syntax".Length] : name, "_").ToLower(), ImmutableArray.Create(name)); // Converts a PascalCased name into snake_cased name. private static readonly Regex s_normalizationRegex = new Regex( - "(?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", + "(?<=[A-Z0-9])(?=[A-Z0-9][a-z0-9]) | (?<=[^A-Z0-9])(?=[A-Z0-9]) | (?<=[A-Za-z0-9])(?=[^A-Za-z0-9])", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); } - internal readonly struct Production : IComparable + internal readonly struct Production(string text, IEnumerable referencedRules = null) : IComparable { - public readonly string Text; - public readonly ImmutableArray ReferencedRules; - - public Production(string text, IEnumerable referencedRules = null) - { - Text = text; - ReferencedRules = referencedRules?.ToImmutableArray() ?? ImmutableArray.Empty; - } + public readonly string Text = text; + public readonly ImmutableArray ReferencedRules = referencedRules?.ToImmutableArray() ?? ImmutableArray.Empty; public override string ToString() => Text; public int CompareTo(Production other) => StringComparer.Ordinal.Compare(this.Text, other.Text); From 57e11432b9ef22c05333128767464db963139884 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 11:37:39 -0700 Subject: [PATCH 021/423] Add utf8 rules --- .../Portable/Generated/CSharp.Generated.g4 | 35 +++++++++++-------- .../Grammar/GrammarGenerator.cs | 12 ++++--- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index a5ce638524661..ceca14fea1289 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -950,9 +950,21 @@ literal_expression | numeric_literal_token | single_line_raw_string_literal_token | string_literal_token - | utf_8_multi_line_raw_string_literal_token - | utf_8_single_line_raw_string_literal_token - | utf_8_string_literal_token + | utf8_multi_line_raw_string_literal_token + | utf8_single_line_raw_string_literal_token + | utf8_string_literal_token + ; + +utf8_multi_line_raw_string_literal_token + : multi_line_raw_string_literal_token utf8_suffix + ; + +utf8_single_line_raw_string_literal_token + : single_line_raw_string_literal_token utf8_suffix + ; + +utf8_string_literal_token + : string_literal_token utf8_suffix ; make_ref_expression @@ -1347,6 +1359,11 @@ skipped_tokens_trivia : syntax_token* ; +utf8_suffix + : 'U8' + | 'u8' + ; + base_argument_list : argument_list | bracketed_argument_list @@ -1416,18 +1433,6 @@ syntax_token : /* see lexical specification */ ; -utf_8_multi_line_raw_string_literal_token - : /* see lexical specification */ - ; - -utf_8_single_line_raw_string_literal_token - : /* see lexical specification */ - ; - -utf_8_string_literal_token - : /* see lexical specification */ - ; - xml_text_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index b7d4270073c54..c118dcf3ad5cf 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -62,7 +62,7 @@ public static string Run(List types) { for (int i = 0; i < originalFirstFieldKinds.Count; i++) { - firstField.Kinds = new List { originalFirstFieldKinds[i] }; + firstField.Kinds = [originalFirstFieldKinds[i]]; rules[type.Name].Add(HandleChildren(type.Children)); } } @@ -74,7 +74,11 @@ public static string Run(List types) } } - // rules.Add() + // Add some rules not present in Syntax.xml. + rules.Add("Utf8StringLiteralToken", [Join(" ", [RuleReference("StringLiteralToken"), RuleReference("Utf8Suffix")])]); + rules.Add("Utf8MultiLineRawStringLiteralToken", [Join(" ", [RuleReference("MultiLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); + rules.Add("Utf8SingleLineRawStringLiteralToken", [Join(" ", [RuleReference("SingleLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); + rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); // The grammar will bottom out with certain lexical productions. Create rules for these. var lexicalRules = rules.Values.SelectMany(ps => ps).SelectMany(p => p.ReferencedRules) @@ -86,7 +90,7 @@ public static string Run(List types) // Define a few major sections to help keep the grammar file naturally grouped. var majorRules = ImmutableArray.Create( - "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax"); + "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax", "Utf8Suffix"); var result = "// " + Environment.NewLine + "grammar csharp;" + Environment.NewLine; @@ -174,7 +178,7 @@ private static Production RuleReference(string name) // Converts a PascalCased name into snake_cased name. private static readonly Regex s_normalizationRegex = new Regex( - "(?<=[A-Z0-9])(?=[A-Z0-9][a-z0-9]) | (?<=[^A-Z0-9])(?=[A-Z0-9]) | (?<=[A-Za-z0-9])(?=[^A-Za-z0-9])", + "(?<=[A-Z])(?=[A-Z][a-z0-9]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z0-9])(?=[^A-Za-z0-9])", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); } From afded5f6481008da00269f13234c6bf0bc760086 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 11:57:04 -0700 Subject: [PATCH 022/423] add string chars --- .../Portable/Generated/CSharp.Generated.g4 | 17 +++++++- .../Grammar/GrammarGenerator.cs | 41 +++++++++++++++---- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index ceca14fea1289..d1046d7a426a5 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -955,6 +955,15 @@ literal_expression | utf8_string_literal_token ; +string_literal_token + : regular_string_literal_token + | verbatim_string_literal_token + ; + +regular_string_literal_token + : '"' regular_string_literal_character* '"' + ; + utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1421,11 +1430,11 @@ numeric_literal_token : /* see lexical specification */ ; -single_line_raw_string_literal_token +regular_string_literal_character : /* see lexical specification */ ; -string_literal_token +single_line_raw_string_literal_token : /* see lexical specification */ ; @@ -1433,6 +1442,10 @@ syntax_token : /* see lexical specification */ ; +verbatim_string_literal_token + : /* see lexical specification */ + ; + xml_text_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index c118dcf3ad5cf..3ee3e17fcd2f3 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -75,10 +75,7 @@ public static string Run(List types) } // Add some rules not present in Syntax.xml. - rules.Add("Utf8StringLiteralToken", [Join(" ", [RuleReference("StringLiteralToken"), RuleReference("Utf8Suffix")])]); - rules.Add("Utf8MultiLineRawStringLiteralToken", [Join(" ", [RuleReference("MultiLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); - rules.Add("Utf8SingleLineRawStringLiteralToken", [Join(" ", [RuleReference("SingleLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); - rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); + AddLexicalRules(rules); // The grammar will bottom out with certain lexical productions. Create rules for these. var lexicalRules = rules.Values.SelectMany(ps => ps).SelectMany(p => p.ReferencedRules) @@ -106,8 +103,16 @@ void processRule(string name, ref string result) { // Order the productions to keep us independent from whatever changes happen in Syntax.xml. var sorted = rules[name].OrderBy(v => v); - result += Environment.NewLine + RuleReference(name).Text + Environment.NewLine + " : " + - string.Join(Environment.NewLine + " | ", sorted) + Environment.NewLine + " ;" + Environment.NewLine; + var ruleReference = RuleReference(name); + result += + Environment.NewLine + + ruleReference.Text + + Environment.NewLine + + " : " + + string.Join(Environment.NewLine + " | ", sorted) + + Environment.NewLine + + " ;" + + Environment.NewLine; // Now proceed in depth-first fashion through the referenced rules to keep related rules // close by. Don't recurse into major-sections to help keep them separated in grammar file. @@ -119,6 +124,26 @@ void processRule(string name, ref string result) } } + private static void AddLexicalRules(Dictionary> rules) + { + addUtf8Rules(); + addStringLiteralRules(); + + void addUtf8Rules() + { + rules.Add("Utf8StringLiteralToken", [Join(" ", [RuleReference("StringLiteralToken"), RuleReference("Utf8Suffix")])]); + rules.Add("Utf8MultiLineRawStringLiteralToken", [Join(" ", [RuleReference("MultiLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); + rules.Add("Utf8SingleLineRawStringLiteralToken", [Join(" ", [RuleReference("SingleLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); + rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); + } + + void addStringLiteralRules() + { + rules.Add("StringLiteralToken", [RuleReference("RegularStringLiteralToken"), RuleReference("VerbatimStringLiteralToken")]); + rules.Add("RegularStringLiteralToken", [Join(" ", [new("'\"'"), RuleReference("RegularStringLiteralCharacter").Suffix("*"), new("'\"'")])]); + } + } + private static Production Join(string delim, IEnumerable productions) => new(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); @@ -182,9 +207,11 @@ private static Production RuleReference(string name) RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled); } - internal readonly struct Production(string text, IEnumerable referencedRules = null) : IComparable + internal readonly struct Production( + string text, IEnumerable referencedRules = null, bool isFragment = false) : IComparable { public readonly string Text = text; + public readonly bool IsFragment = isFragment; public readonly ImmutableArray ReferencedRules = referencedRules?.ToImmutableArray() ?? ImmutableArray.Empty; public override string ToString() => Text; From 487ab70e0128513cd5add8e1f470196fabcec25d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 11:59:30 -0700 Subject: [PATCH 023/423] add string chars --- src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 | 6 +++++- .../CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index d1046d7a426a5..20927dabf9b69 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -964,6 +964,10 @@ regular_string_literal_token : '"' regular_string_literal_character* '"' ; +verbatim_string_literal_token + : '@"' verbatim_string_literal_character* '"' + ; + utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1442,7 +1446,7 @@ syntax_token : /* see lexical specification */ ; -verbatim_string_literal_token +verbatim_string_literal_character : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 3ee3e17fcd2f3..967a6d0b4205e 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -141,6 +141,7 @@ void addStringLiteralRules() { rules.Add("StringLiteralToken", [RuleReference("RegularStringLiteralToken"), RuleReference("VerbatimStringLiteralToken")]); rules.Add("RegularStringLiteralToken", [Join(" ", [new("'\"'"), RuleReference("RegularStringLiteralCharacter").Suffix("*"), new("'\"'")])]); + rules.Add("VerbatimStringLiteralToken", [Join(" ", [new("'@\"'"), RuleReference("VerbatimStringLiteralCharacter").Suffix("*"), new("'\"'")])]); } } From 3ad14facfa5b55955ac141880b2a778f9a092594 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:04:05 -0700 Subject: [PATCH 024/423] add string chars --- .../Portable/Generated/CSharp.Generated.g4 | 17 +++++++++++++---- .../Grammar/GrammarGenerator.cs | 4 ++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 20927dabf9b69..24a9f5136206a 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -968,6 +968,19 @@ verbatim_string_literal_token : '@"' verbatim_string_literal_character* '"' ; +verbatim_string_literal_character + : quote_escape_sequence + | single_verbatim_string_literal_character + ; + +quote_escape_sequence + : '""' + ; + +single_verbatim_string_literal_character + : ~["] // anything but quotation mark (U+0022) + ; + utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1446,10 +1459,6 @@ syntax_token : /* see lexical specification */ ; -verbatim_string_literal_character - : /* see lexical specification */ - ; - xml_text_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 967a6d0b4205e..d773af13185cb 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -141,7 +141,11 @@ void addStringLiteralRules() { rules.Add("StringLiteralToken", [RuleReference("RegularStringLiteralToken"), RuleReference("VerbatimStringLiteralToken")]); rules.Add("RegularStringLiteralToken", [Join(" ", [new("'\"'"), RuleReference("RegularStringLiteralCharacter").Suffix("*"), new("'\"'")])]); + rules.Add("VerbatimStringLiteralToken", [Join(" ", [new("'@\"'"), RuleReference("VerbatimStringLiteralCharacter").Suffix("*"), new("'\"'")])]); + rules.Add("VerbatimStringLiteralCharacter", [RuleReference("SingleVerbatimStringLiteralCharacter"), RuleReference("QuoteEscapeSequence")]); + rules.Add("SingleVerbatimStringLiteralCharacter", [new("~[\"] // anything but quotation mark (U+0022)")]); + rules.Add("QuoteEscapeSequence", [new("'\"\"'")]); } } From 16dc856658a98a7977a4a605e8143cb48822d424 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:06:44 -0700 Subject: [PATCH 025/423] add string chars --- src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 | 2 +- .../Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 24a9f5136206a..85afd69514b8b 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -978,7 +978,7 @@ quote_escape_sequence ; single_verbatim_string_literal_character - : ~["] // anything but quotation mark (U+0022) + : /* anything but quotation mark (U+0022) */ ; utf8_multi_line_raw_string_literal_token diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index d773af13185cb..db69ffa7b4e54 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -144,7 +144,7 @@ void addStringLiteralRules() rules.Add("VerbatimStringLiteralToken", [Join(" ", [new("'@\"'"), RuleReference("VerbatimStringLiteralCharacter").Suffix("*"), new("'\"'")])]); rules.Add("VerbatimStringLiteralCharacter", [RuleReference("SingleVerbatimStringLiteralCharacter"), RuleReference("QuoteEscapeSequence")]); - rules.Add("SingleVerbatimStringLiteralCharacter", [new("~[\"] // anything but quotation mark (U+0022)")]); + rules.Add("SingleVerbatimStringLiteralCharacter", [new("/* anything but quotation mark (U+0022) */")]); rules.Add("QuoteEscapeSequence", [new("'\"\"'")]); } } From fe68bfdfe058737c0fc76bcd1f303a1eccaa04fa Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:08:53 -0700 Subject: [PATCH 026/423] add string chars --- .../Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index db69ffa7b4e54..45ddbec3886c2 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -141,6 +141,7 @@ void addStringLiteralRules() { rules.Add("StringLiteralToken", [RuleReference("RegularStringLiteralToken"), RuleReference("VerbatimStringLiteralToken")]); rules.Add("RegularStringLiteralToken", [Join(" ", [new("'\"'"), RuleReference("RegularStringLiteralCharacter").Suffix("*"), new("'\"'")])]); + rules.Add("RegularStringLiteralCharacter", [RuleReference("SingleRegularStringLiteralCharacter"), RuleReference("SimpleEscapeSequence"), RuleReference("HexadecimalEscapeSequence"), RuleReference("UnicodeEscapeSequence")]); rules.Add("VerbatimStringLiteralToken", [Join(" ", [new("'@\"'"), RuleReference("VerbatimStringLiteralCharacter").Suffix("*"), new("'\"'")])]); rules.Add("VerbatimStringLiteralCharacter", [RuleReference("SingleVerbatimStringLiteralCharacter"), RuleReference("QuoteEscapeSequence")]); From 5283d28a68edd5b553aa61c10dcc978c0e7a9812 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:13:06 -0700 Subject: [PATCH 027/423] add string chars --- .../Portable/Generated/CSharp.Generated.g4 | 33 +++++++++++++++++-- .../Grammar/GrammarGenerator.cs | 6 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 85afd69514b8b..3134f2715d59f 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -964,6 +964,27 @@ regular_string_literal_token : '"' regular_string_literal_character* '"' ; +regular_string_literal_character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_regular_string_literal_character + | unicode_escape_sequence + ; + +simple_escape_sequence + : '\\"' + | '\\0' + | '\\\'' + | '\\\\' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | '\\t' + | '\\v' + ; + verbatim_string_literal_token : '@"' verbatim_string_literal_character* '"' ; @@ -1419,6 +1440,10 @@ expression_or_pattern | pattern ; +hexadecimal_escape_sequence + : /* see lexical specification */ + ; + identifier_token : /* see lexical specification */ ; @@ -1447,11 +1472,11 @@ numeric_literal_token : /* see lexical specification */ ; -regular_string_literal_character +single_line_raw_string_literal_token : /* see lexical specification */ ; -single_line_raw_string_literal_token +single_regular_string_literal_character : /* see lexical specification */ ; @@ -1459,6 +1484,10 @@ syntax_token : /* see lexical specification */ ; +unicode_escape_sequence + : /* see lexical specification */ + ; + xml_text_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 45ddbec3886c2..93759e69f1333 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -127,6 +127,7 @@ void processRule(string name, ref string result) private static void AddLexicalRules(Dictionary> rules) { addUtf8Rules(); + addEscapeSequenceRules(); addStringLiteralRules(); void addUtf8Rules() @@ -137,6 +138,11 @@ void addUtf8Rules() rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); } + void addEscapeSequenceRules() + { + rules.Add("SimpleEscapeSequence", [new("""'\\\''"""), new("""'\\"'"""), new("""'\\\\'"""), new("""'\\0'"""), new("""'\\a'"""), new("""'\\b'"""), new("""'\\f'"""), new("""'\\n'"""), new("""'\\r'"""), new("""'\\t'"""), new("""'\\v'""")]); + } + void addStringLiteralRules() { rules.Add("StringLiteralToken", [RuleReference("RegularStringLiteralToken"), RuleReference("VerbatimStringLiteralToken")]); From 2f2c0e9140430c5c87a6fd4fc69e67c98bcb3e91 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:15:56 -0700 Subject: [PATCH 028/423] Add hex --- .../Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 93759e69f1333..8d5f9fd2b3070 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -141,6 +141,7 @@ void addUtf8Rules() void addEscapeSequenceRules() { rules.Add("SimpleEscapeSequence", [new("""'\\\''"""), new("""'\\"'"""), new("""'\\\\'"""), new("""'\\0'"""), new("""'\\a'"""), new("""'\\b'"""), new("""'\\f'"""), new("""'\\n'"""), new("""'\\r'"""), new("""'\\t'"""), new("""'\\v'""")]); + rules.Add("HexadecimalEscapeSequence", [Join(" ", [new("""'\\x'"""), RuleReference("HexDigit"), RuleReference("HexDigit").Suffix("?"), RuleReference("HexDigit").Suffix("?"), RuleReference("HexDigit").Suffix("?")])]); } void addStringLiteralRules() From 4c8cb9c2f2355bbb346295c4c904dfc381a51f63 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:19:36 -0700 Subject: [PATCH 029/423] Add hex --- .../CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 8d5f9fd2b3070..50e61d6ceb3b4 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -127,6 +127,7 @@ void processRule(string name, ref string result) private static void AddLexicalRules(Dictionary> rules) { addUtf8Rules(); + addIntegerLiterals(); addEscapeSequenceRules(); addStringLiteralRules(); @@ -138,6 +139,13 @@ void addUtf8Rules() rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); } + void addIntegerLiterals() + { + rules.Add("IntegerLiteralToken", [RuleReference("DecimalIntegerLiteralToken"), RuleReference("HexadecimalIntegerLiteralToken")]); + rules.Add("DecimalIntegerLiteralToken", [Join(" ", [RuleReference("DecimalDigit").Suffix("+"), RuleReference("IntegerTypeSuffix").Suffix("?")])]); + rules.Add("DecimalDigit", [new("'0'..'9'")]); + } + void addEscapeSequenceRules() { rules.Add("SimpleEscapeSequence", [new("""'\\\''"""), new("""'\\"'"""), new("""'\\\\'"""), new("""'\\0'"""), new("""'\\a'"""), new("""'\\b'"""), new("""'\\f'"""), new("""'\\n'"""), new("""'\\r'"""), new("""'\\t'"""), new("""'\\v'""")]); From 33c91fdc8197e46e20ae771d1c360480222c8cd5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:20:04 -0700 Subject: [PATCH 030/423] Add hex --- .../Portable/Generated/CSharp.Generated.g4 | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 3134f2715d59f..31de7af81c9bd 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -971,6 +971,10 @@ regular_string_literal_character | unicode_escape_sequence ; +hexadecimal_escape_sequence + : '\\x' hex_digit hex_digit? hex_digit? hex_digit? + ; + simple_escape_sequence : '\\"' | '\\0' @@ -1435,12 +1439,24 @@ character_literal_token : /* see lexical specification */ ; +decimal_digit + : '0'..'9' + ; + +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? + ; + expression_or_pattern : expression | pattern ; -hexadecimal_escape_sequence +hexadecimal_integer_literal_token + : /* see lexical specification */ + ; + +hex_digit : /* see lexical specification */ ; @@ -1448,6 +1464,15 @@ identifier_token : /* see lexical specification */ ; +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token + ; + +integer_type_suffix + : /* see lexical specification */ + ; + interpolated_multi_line_raw_string_start_token : /* see lexical specification */ ; From 2e4c15224aeaf473408f56ebaa79ad6f01215ed3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:22:43 -0700 Subject: [PATCH 031/423] Add hex --- .../Portable/Generated/CSharp.Generated.g4 | 21 ++++++++++++++----- .../Grammar/GrammarGenerator.cs | 2 +- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 31de7af81c9bd..d43a62180c9d3 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1440,13 +1440,28 @@ character_literal_token ; decimal_digit - : '0'..'9' + : /* see lexical specification */ ; decimal_integer_literal_token : decimal_digit+ integer_type_suffix? ; +integer_type_suffix + : L + | LU + | Lu + | U + | UL + | Ul + | l + | lU + | lu + | u + | uL + | ul + ; + expression_or_pattern : expression | pattern @@ -1469,10 +1484,6 @@ integer_literal_token | hexadecimal_integer_literal_token ; -integer_type_suffix - : /* see lexical specification */ - ; - interpolated_multi_line_raw_string_start_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 50e61d6ceb3b4..794ea061a846d 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -143,7 +143,7 @@ void addIntegerLiterals() { rules.Add("IntegerLiteralToken", [RuleReference("DecimalIntegerLiteralToken"), RuleReference("HexadecimalIntegerLiteralToken")]); rules.Add("DecimalIntegerLiteralToken", [Join(" ", [RuleReference("DecimalDigit").Suffix("+"), RuleReference("IntegerTypeSuffix").Suffix("?")])]); - rules.Add("DecimalDigit", [new("'0'..'9'")]); + rules.Add("IntegerTypeSuffix", [new("U"), new("u"), new("L"), new("l"), new("UL"), new("Ul"), new("uL"), new("ul"), new("LU"), new("Lu"), new("lU"), new("lu")]); } void addEscapeSequenceRules() From 205722f7c31bf044a0c494b9f764109634c4d522 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:24:14 -0700 Subject: [PATCH 032/423] Add hex --- .../Portable/Generated/CSharp.Generated.g4 | 24 +++++++++---------- .../Grammar/GrammarGenerator.cs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index d43a62180c9d3..826184a13c2e8 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1448,18 +1448,18 @@ decimal_integer_literal_token ; integer_type_suffix - : L - | LU - | Lu - | U - | UL - | Ul - | l - | lU - | lu - | u - | uL - | ul + : 'L' + | 'LU' + | 'Lu' + | 'U' + | 'UL' + | 'Ul' + | 'l' + | 'lU' + | 'lu' + | 'u' + | 'uL' + | 'ul' ; expression_or_pattern diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 794ea061a846d..96453d71d3240 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -143,7 +143,7 @@ void addIntegerLiterals() { rules.Add("IntegerLiteralToken", [RuleReference("DecimalIntegerLiteralToken"), RuleReference("HexadecimalIntegerLiteralToken")]); rules.Add("DecimalIntegerLiteralToken", [Join(" ", [RuleReference("DecimalDigit").Suffix("+"), RuleReference("IntegerTypeSuffix").Suffix("?")])]); - rules.Add("IntegerTypeSuffix", [new("U"), new("u"), new("L"), new("l"), new("UL"), new("Ul"), new("uL"), new("ul"), new("LU"), new("Lu"), new("lU"), new("lu")]); + rules.Add("IntegerTypeSuffix", [new("'U'"), new("'u'"), new("'L'"), new("'l'"), new("'UL'"), new("'Ul'"), new("'uL'"), new("'ul'"), new("'LU'"), new("'Lu'"), new("'lU'"), new("'lu'")]); } void addEscapeSequenceRules() From 3379ef61fa729b8d3bfe395b7d86d7b8ac87bdd9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:26:32 -0700 Subject: [PATCH 033/423] Add hex --- .../Portable/Generated/CSharp.Generated.g4 | 27 ++++++++++++++++++- .../Grammar/GrammarGenerator.cs | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 826184a13c2e8..7782ae1e7b2f7 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1440,7 +1440,16 @@ character_literal_token ; decimal_digit - : /* see lexical specification */ + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' ; decimal_integer_literal_token @@ -1467,6 +1476,22 @@ expression_or_pattern | pattern ; +hexadecimal_digit + : 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | decimal_digit + ; + hexadecimal_integer_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 96453d71d3240..87384bd7376c4 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -144,6 +144,8 @@ void addIntegerLiterals() rules.Add("IntegerLiteralToken", [RuleReference("DecimalIntegerLiteralToken"), RuleReference("HexadecimalIntegerLiteralToken")]); rules.Add("DecimalIntegerLiteralToken", [Join(" ", [RuleReference("DecimalDigit").Suffix("+"), RuleReference("IntegerTypeSuffix").Suffix("?")])]); rules.Add("IntegerTypeSuffix", [new("'U'"), new("'u'"), new("'L'"), new("'l'"), new("'UL'"), new("'Ul'"), new("'uL'"), new("'ul'"), new("'LU'"), new("'Lu'"), new("'lU'"), new("'lu'")]); + rules.Add("DecimalDigit", [new("'0'"), new("'1'"), new("'2'"), new("'3'"), new("'4'"), new("'5'"), new("'6'"), new("'7'"), new("'8'"), new("'9'")]); + rules.Add("HexadecimalDigit", [RuleReference("DecimalDigit"), new("'A'"), new("'B'"), new("'C'"), new("'D'"), new("'E'"), new("'F'"), new("'a'"), new("'b'"), new("'c'"), new("'d'"), new("'e'"), new("'f'")]); } void addEscapeSequenceRules() From 3dde41296a461e3666d5441a4a0cfe7aaf90c328 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:29:09 -0700 Subject: [PATCH 034/423] Add hex --- .../Portable/Generated/CSharp.Generated.g4 | 66 +++++++++---------- .../Grammar/GrammarGenerator.cs | 3 +- 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 7782ae1e7b2f7..f6e709382dd75 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -972,7 +972,36 @@ regular_string_literal_character ; hexadecimal_escape_sequence - : '\\x' hex_digit hex_digit? hex_digit? hex_digit? + : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? + ; + +hexadecimal_digit + : 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | decimal_digit + ; + +decimal_digit + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' ; simple_escape_sequence @@ -1439,19 +1468,6 @@ character_literal_token : /* see lexical specification */ ; -decimal_digit - : '0' - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - ; - decimal_integer_literal_token : decimal_digit+ integer_type_suffix? ; @@ -1476,28 +1492,8 @@ expression_or_pattern | pattern ; -hexadecimal_digit - : 'A' - | 'B' - | 'C' - | 'D' - | 'E' - | 'F' - | 'a' - | 'b' - | 'c' - | 'd' - | 'e' - | 'f' - | decimal_digit - ; - hexadecimal_integer_literal_token - : /* see lexical specification */ - ; - -hex_digit - : /* see lexical specification */ + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? ; identifier_token diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 87384bd7376c4..f70b0f5f3d32c 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -146,12 +146,13 @@ void addIntegerLiterals() rules.Add("IntegerTypeSuffix", [new("'U'"), new("'u'"), new("'L'"), new("'l'"), new("'UL'"), new("'Ul'"), new("'uL'"), new("'ul'"), new("'LU'"), new("'Lu'"), new("'lU'"), new("'lu'")]); rules.Add("DecimalDigit", [new("'0'"), new("'1'"), new("'2'"), new("'3'"), new("'4'"), new("'5'"), new("'6'"), new("'7'"), new("'8'"), new("'9'")]); rules.Add("HexadecimalDigit", [RuleReference("DecimalDigit"), new("'A'"), new("'B'"), new("'C'"), new("'D'"), new("'E'"), new("'F'"), new("'a'"), new("'b'"), new("'c'"), new("'d'"), new("'e'"), new("'f'")]); + rules.Add("HexadecimalIntegerLiteralToken", [Join(" ", [new("('0x' | '0X')"), RuleReference("HexadecimalDigit").Suffix("+"), RuleReference("IntegerTypeSuffix").Suffix("?")])]); } void addEscapeSequenceRules() { rules.Add("SimpleEscapeSequence", [new("""'\\\''"""), new("""'\\"'"""), new("""'\\\\'"""), new("""'\\0'"""), new("""'\\a'"""), new("""'\\b'"""), new("""'\\f'"""), new("""'\\n'"""), new("""'\\r'"""), new("""'\\t'"""), new("""'\\v'""")]); - rules.Add("HexadecimalEscapeSequence", [Join(" ", [new("""'\\x'"""), RuleReference("HexDigit"), RuleReference("HexDigit").Suffix("?"), RuleReference("HexDigit").Suffix("?"), RuleReference("HexDigit").Suffix("?")])]); + rules.Add("HexadecimalEscapeSequence", [Join(" ", [new("""'\\x'"""), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit").Suffix("?"), RuleReference("HexadecimalDigit").Suffix("?"), RuleReference("HexadecimalDigit").Suffix("?")])]); } void addStringLiteralRules() From fb57a606bf5fa7f6bc7e08be4816623d420cf53f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:32:42 -0700 Subject: [PATCH 035/423] Add literals --- .../Portable/Generated/CSharp.Generated.g4 | 109 +++++++++--------- .../Grammar/GrammarGenerator.cs | 10 +- 2 files changed, 65 insertions(+), 54 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index f6e709382dd75..eec9a7a2ee5b7 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -955,24 +955,50 @@ literal_expression | utf8_string_literal_token ; -string_literal_token - : regular_string_literal_token - | verbatim_string_literal_token +numeric_literal_token + : integer_literal_token + | real_literal_token ; -regular_string_literal_token - : '"' regular_string_literal_character* '"' +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token ; -regular_string_literal_character - : hexadecimal_escape_sequence - | simple_escape_sequence - | single_regular_string_literal_character - | unicode_escape_sequence +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? ; -hexadecimal_escape_sequence - : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? +decimal_digit + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + ; + +integer_type_suffix + : 'L' + | 'LU' + | 'Lu' + | 'U' + | 'UL' + | 'Ul' + | 'l' + | 'lU' + | 'lu' + | 'u' + | 'uL' + | 'ul' + ; + +hexadecimal_integer_literal_token + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? ; hexadecimal_digit @@ -991,17 +1017,24 @@ hexadecimal_digit | decimal_digit ; -decimal_digit - : '0' - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' +string_literal_token + : regular_string_literal_token + | verbatim_string_literal_token + ; + +regular_string_literal_token + : '"' regular_string_literal_character* '"' + ; + +regular_string_literal_character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_regular_string_literal_character + | unicode_escape_sequence + ; + +hexadecimal_escape_sequence + : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? ; simple_escape_sequence @@ -1468,43 +1501,15 @@ character_literal_token : /* see lexical specification */ ; -decimal_integer_literal_token - : decimal_digit+ integer_type_suffix? - ; - -integer_type_suffix - : 'L' - | 'LU' - | 'Lu' - | 'U' - | 'UL' - | 'Ul' - | 'l' - | 'lU' - | 'lu' - | 'u' - | 'uL' - | 'ul' - ; - expression_or_pattern : expression | pattern ; -hexadecimal_integer_literal_token - : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? - ; - identifier_token : /* see lexical specification */ ; -integer_literal_token - : decimal_integer_literal_token - | hexadecimal_integer_literal_token - ; - interpolated_multi_line_raw_string_start_token : /* see lexical specification */ ; @@ -1525,7 +1530,7 @@ multi_line_raw_string_literal_token : /* see lexical specification */ ; -numeric_literal_token +real_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index f70b0f5f3d32c..acecbb7c0b5c5 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -127,7 +127,8 @@ void processRule(string name, ref string result) private static void AddLexicalRules(Dictionary> rules) { addUtf8Rules(); - addIntegerLiterals(); + addNumericLiteralRules(); + addIntegerLiteralRules(); addEscapeSequenceRules(); addStringLiteralRules(); @@ -139,7 +140,12 @@ void addUtf8Rules() rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); } - void addIntegerLiterals() + void addNumericLiteralRules() + { + rules.Add("NumericLiteralToken", [RuleReference("IntegerLiteralToken"), RuleReference("RealLiteralToken")]); + } + + void addIntegerLiteralRules() { rules.Add("IntegerLiteralToken", [RuleReference("DecimalIntegerLiteralToken"), RuleReference("HexadecimalIntegerLiteralToken")]); rules.Add("DecimalIntegerLiteralToken", [Join(" ", [RuleReference("DecimalDigit").Suffix("+"), RuleReference("IntegerTypeSuffix").Suffix("?")])]); From c8c44fb347834fa583c632df42fca2d4497aa079 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:34:47 -0700 Subject: [PATCH 036/423] Literals at end --- .../Portable/Generated/CSharp.Generated.g4 | 282 +++++++++--------- .../Grammar/GrammarGenerator.cs | 2 +- 2 files changed, 142 insertions(+), 142 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index eec9a7a2ee5b7..d9711213ce08b 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -939,147 +939,6 @@ is_pattern_expression : expression 'is' pattern ; -literal_expression - : '__arglist' - | 'default' - | 'false' - | 'null' - | 'true' - | character_literal_token - | multi_line_raw_string_literal_token - | numeric_literal_token - | single_line_raw_string_literal_token - | string_literal_token - | utf8_multi_line_raw_string_literal_token - | utf8_single_line_raw_string_literal_token - | utf8_string_literal_token - ; - -numeric_literal_token - : integer_literal_token - | real_literal_token - ; - -integer_literal_token - : decimal_integer_literal_token - | hexadecimal_integer_literal_token - ; - -decimal_integer_literal_token - : decimal_digit+ integer_type_suffix? - ; - -decimal_digit - : '0' - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - ; - -integer_type_suffix - : 'L' - | 'LU' - | 'Lu' - | 'U' - | 'UL' - | 'Ul' - | 'l' - | 'lU' - | 'lu' - | 'u' - | 'uL' - | 'ul' - ; - -hexadecimal_integer_literal_token - : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? - ; - -hexadecimal_digit - : 'A' - | 'B' - | 'C' - | 'D' - | 'E' - | 'F' - | 'a' - | 'b' - | 'c' - | 'd' - | 'e' - | 'f' - | decimal_digit - ; - -string_literal_token - : regular_string_literal_token - | verbatim_string_literal_token - ; - -regular_string_literal_token - : '"' regular_string_literal_character* '"' - ; - -regular_string_literal_character - : hexadecimal_escape_sequence - | simple_escape_sequence - | single_regular_string_literal_character - | unicode_escape_sequence - ; - -hexadecimal_escape_sequence - : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? - ; - -simple_escape_sequence - : '\\"' - | '\\0' - | '\\\'' - | '\\\\' - | '\\a' - | '\\b' - | '\\f' - | '\\n' - | '\\r' - | '\\t' - | '\\v' - ; - -verbatim_string_literal_token - : '@"' verbatim_string_literal_character* '"' - ; - -verbatim_string_literal_character - : quote_escape_sequence - | single_verbatim_string_literal_character - ; - -quote_escape_sequence - : '""' - ; - -single_verbatim_string_literal_character - : /* anything but quotation mark (U+0022) */ - ; - -utf8_multi_line_raw_string_literal_token - : multi_line_raw_string_literal_token utf8_suffix - ; - -utf8_single_line_raw_string_literal_token - : single_line_raw_string_literal_token utf8_suffix - ; - -utf8_string_literal_token - : string_literal_token utf8_suffix - ; - make_ref_expression : '__makeref' '(' expression ')' ; @@ -1420,6 +1279,119 @@ line_directive_trivia : '#' 'line' (numeric_literal_token | 'default' | 'hidden') string_literal_token? ; +numeric_literal_token + : integer_literal_token + | real_literal_token + ; + +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token + ; + +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? + ; + +decimal_digit + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + ; + +integer_type_suffix + : 'L' + | 'LU' + | 'Lu' + | 'U' + | 'UL' + | 'Ul' + | 'l' + | 'lU' + | 'lu' + | 'u' + | 'uL' + | 'ul' + ; + +hexadecimal_integer_literal_token + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? + ; + +hexadecimal_digit + : 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | decimal_digit + ; + +string_literal_token + : regular_string_literal_token + | verbatim_string_literal_token + ; + +regular_string_literal_token + : '"' regular_string_literal_character* '"' + ; + +regular_string_literal_character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_regular_string_literal_character + | unicode_escape_sequence + ; + +hexadecimal_escape_sequence + : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? + ; + +simple_escape_sequence + : '\\"' + | '\\0' + | '\\\'' + | '\\\\' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | '\\t' + | '\\v' + ; + +verbatim_string_literal_token + : '@"' verbatim_string_literal_character* '"' + ; + +verbatim_string_literal_character + : quote_escape_sequence + | single_verbatim_string_literal_character + ; + +quote_escape_sequence + : '""' + ; + +single_verbatim_string_literal_character + : /* anything but quotation mark (U+0022) */ + ; + line_span_directive_trivia : '#' 'line' line_directive_position '-' line_directive_position numeric_literal_token? string_literal_token ; @@ -1472,6 +1444,34 @@ skipped_tokens_trivia : syntax_token* ; +literal_expression + : '__arglist' + | 'default' + | 'false' + | 'null' + | 'true' + | character_literal_token + | multi_line_raw_string_literal_token + | numeric_literal_token + | single_line_raw_string_literal_token + | string_literal_token + | utf8_multi_line_raw_string_literal_token + | utf8_single_line_raw_string_literal_token + | utf8_string_literal_token + ; + +utf8_multi_line_raw_string_literal_token + : multi_line_raw_string_literal_token utf8_suffix + ; + +utf8_single_line_raw_string_literal_token + : single_line_raw_string_literal_token utf8_suffix + ; + +utf8_string_literal_token + : string_literal_token utf8_suffix + ; + utf8_suffix : 'U8' | 'u8' diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index acecbb7c0b5c5..82f55b96eb186 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -87,7 +87,7 @@ public static string Run(List types) // Define a few major sections to help keep the grammar file naturally grouped. var majorRules = ImmutableArray.Create( - "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax", "Utf8Suffix"); + "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax", "LiteralExpressionSyntax", "Utf8Suffix"); var result = "// " + Environment.NewLine + "grammar csharp;" + Environment.NewLine; From cecc2edb2f419376bfbcf6e1da1f699f0f74484a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:40:46 -0700 Subject: [PATCH 037/423] Add unicode --- .../Portable/Generated/CSharp.Generated.g4 | 28 +++++++++++++------ .../Grammar/GrammarGenerator.cs | 10 +++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index d9711213ce08b..e766fefef65c5 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1375,6 +1375,11 @@ simple_escape_sequence | '\\v' ; +unicode_escape_sequence + : '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + ; + verbatim_string_literal_token : '@"' verbatim_string_literal_character* '"' ; @@ -1460,6 +1465,17 @@ literal_expression | utf8_string_literal_token ; +character_literal_token + : ''' character ''' + ; + +character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_character + | unicode_escape_sequence + ; + utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1497,10 +1513,6 @@ base_parameter | parameter ; -character_literal_token - : /* see lexical specification */ - ; - expression_or_pattern : expression | pattern @@ -1534,19 +1546,19 @@ real_literal_token : /* see lexical specification */ ; -single_line_raw_string_literal_token +single_character : /* see lexical specification */ ; -single_regular_string_literal_character +single_line_raw_string_literal_token : /* see lexical specification */ ; -syntax_token +single_regular_string_literal_character : /* see lexical specification */ ; -unicode_escape_sequence +syntax_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 82f55b96eb186..48c7f0400f144 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -131,6 +131,7 @@ private static void AddLexicalRules(Dictionary> rules) addIntegerLiteralRules(); addEscapeSequenceRules(); addStringLiteralRules(); + addCharacterLiteralRules(); void addUtf8Rules() { @@ -159,6 +160,9 @@ void addEscapeSequenceRules() { rules.Add("SimpleEscapeSequence", [new("""'\\\''"""), new("""'\\"'"""), new("""'\\\\'"""), new("""'\\0'"""), new("""'\\a'"""), new("""'\\b'"""), new("""'\\f'"""), new("""'\\n'"""), new("""'\\r'"""), new("""'\\t'"""), new("""'\\v'""")]); rules.Add("HexadecimalEscapeSequence", [Join(" ", [new("""'\\x'"""), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit").Suffix("?"), RuleReference("HexadecimalDigit").Suffix("?"), RuleReference("HexadecimalDigit").Suffix("?")])]); + rules.Add("UnicodeEscapeSequence", [ + Join(" ", [new("""'\\u'"""), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit")]), + Join(" ", [new("""'\\U'"""), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit"), RuleReference("HexadecimalDigit")])]); } void addStringLiteralRules() @@ -172,6 +176,12 @@ void addStringLiteralRules() rules.Add("SingleVerbatimStringLiteralCharacter", [new("/* anything but quotation mark (U+0022) */")]); rules.Add("QuoteEscapeSequence", [new("'\"\"'")]); } + + void addCharacterLiteralRules() + { + rules.Add("CharacterLiteralToken", [Join(" ", [new("'\''"), RuleReference("Character"), new("'\''")])]); + rules.Add("Character", [RuleReference("SingleCharacter"), RuleReference("SimpleEscapeSequence"), RuleReference("HexadecimalEscapeSequence"), RuleReference("UnicodeEscapeSequence")]); + } } private static Production Join(string delim, IEnumerable productions) From 932f39360406be07983cb8847b1179ec60b01c97 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:41:54 -0700 Subject: [PATCH 038/423] Add unicode --- src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 | 2 +- .../Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index e766fefef65c5..faec971391904 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1466,7 +1466,7 @@ literal_expression ; character_literal_token - : ''' character ''' + : '\'' character '\'' ; character diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 48c7f0400f144..da3dc9084866d 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -179,7 +179,7 @@ void addStringLiteralRules() void addCharacterLiteralRules() { - rules.Add("CharacterLiteralToken", [Join(" ", [new("'\''"), RuleReference("Character"), new("'\''")])]); + rules.Add("CharacterLiteralToken", [Join(" ", [new("""'\''"""), RuleReference("Character"), new("""'\''""")])]); rules.Add("Character", [RuleReference("SingleCharacter"), RuleReference("SimpleEscapeSequence"), RuleReference("HexadecimalEscapeSequence"), RuleReference("UnicodeEscapeSequence")]); } } From 2b2748f4c668b4faa882336ff84def82704b3df4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:44:03 -0700 Subject: [PATCH 039/423] Add rule --- .../Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index da3dc9084866d..aca4c0e709b2c 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -181,6 +181,7 @@ void addCharacterLiteralRules() { rules.Add("CharacterLiteralToken", [Join(" ", [new("""'\''"""), RuleReference("Character"), new("""'\''""")])]); rules.Add("Character", [RuleReference("SingleCharacter"), RuleReference("SimpleEscapeSequence"), RuleReference("HexadecimalEscapeSequence"), RuleReference("UnicodeEscapeSequence")]); + rules.Add("SingleCharacter", [new("/* anything but ', \\, and new_line_character */")]); } } From 469cfdb1353eb840dfd2f2d9fbd5f1ae2e750b95 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:49:44 -0700 Subject: [PATCH 040/423] Add real literals --- .../Portable/Generated/CSharp.Generated.g4 | 37 +++++++++++++++---- .../Grammar/GrammarGenerator.cs | 18 +++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index faec971391904..0ed28e045ac05 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1341,6 +1341,31 @@ hexadecimal_digit | decimal_digit ; +real_literal_token + : '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ exponent_part real_type_suffix? + | decimal_digit+ real_type_suffix + ; + +exponent_part + : ('e' | 'E') sign? decimal_digit+ + ; + +sign + : '+' + | '-' + ; + +real_type_suffix + : 'D' + | 'F' + | 'M' + | 'd' + | 'f' + | 'm' + ; + string_literal_token : regular_string_literal_token | verbatim_string_literal_token @@ -1476,6 +1501,10 @@ character | unicode_escape_sequence ; +single_character + : /* anything but ', \, and new_line_character */ + ; + utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1542,14 +1571,6 @@ multi_line_raw_string_literal_token : /* see lexical specification */ ; -real_literal_token - : /* see lexical specification */ - ; - -single_character - : /* see lexical specification */ - ; - single_line_raw_string_literal_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index aca4c0e709b2c..ad8b293bed2f7 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -127,6 +127,7 @@ void processRule(string name, ref string result) private static void AddLexicalRules(Dictionary> rules) { addUtf8Rules(); + addRealLiteralRules(); addNumericLiteralRules(); addIntegerLiteralRules(); addEscapeSequenceRules(); @@ -141,6 +142,23 @@ void addUtf8Rules() rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); } + void addRealLiteralRules() + { + var decimalDigitPlus = RuleReference("DecimalDigit").Suffix("+"); + var exponentPartOpt = RuleReference("ExponentPart").Suffix("?"); + var realTypeSuffixOpt = RuleReference("RealTypeSuffix").Suffix("?"); + rules.Add("RealLiteralToken", [ + Join(" ", [decimalDigitPlus, new("'.'"), decimalDigitPlus, exponentPartOpt, realTypeSuffixOpt]), + Join(" ", [new("'.'"), decimalDigitPlus, exponentPartOpt, realTypeSuffixOpt]), + Join(" ", [decimalDigitPlus, RuleReference("ExponentPart"), realTypeSuffixOpt]), + Join(" ", [decimalDigitPlus, RuleReference("RealTypeSuffix")]), + ]); + + rules.Add("ExponentPart", [Join(" ", [new("('e' | 'E')"), RuleReference("Sign").Suffix("?"), decimalDigitPlus])]); + rules.Add("Sign", [new("'+'"), new("'-'")]); + rules.Add("RealTypeSuffix", [new("'F'"), new("'f'"), new("'D'"), new("'d'"), new("'M'"), new("'m'")]); + } + void addNumericLiteralRules() { rules.Add("NumericLiteralToken", [RuleReference("IntegerLiteralToken"), RuleReference("RealLiteralToken")]); From 91f7fed89e8ffd0cdada870fffb23466b872dd53 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 12:57:27 -0700 Subject: [PATCH 041/423] identifiers --- .../Portable/Generated/CSharp.Generated.g4 | 40 ++++++++++++++++++- .../Grammar/GrammarGenerator.cs | 10 +++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 0ed28e045ac05..ecc1f52f8106a 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -9,6 +9,32 @@ extern_alias_directive : 'extern' 'alias' identifier_token ';' ; +identifier_token + : '@'? identifier_start_character identifier_part_character + ; + +identifier_start_character + : letter_character + | underscore_character + ; + +letter_character + : [\p{L}\p{Nl}] + ; + +underscore_character + : '\\u005' // unicode_escape_sequence for underscore + | '_' + ; + +identifier_part_character + : combining_character + | connecting_character + | decimal_digit_character + | formatting_character + | letter_character + ; + using_directive : 'global'? 'using' ('static' | ('unsafe'? name_equals))? type ';' ; @@ -1542,12 +1568,24 @@ base_parameter | parameter ; +combining_character + : /* see lexical specification */ + ; + +connecting_character + : /* see lexical specification */ + ; + +decimal_digit_character + : /* see lexical specification */ + ; + expression_or_pattern : expression | pattern ; -identifier_token +formatting_character : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index ad8b293bed2f7..bd26419ff85c5 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -127,6 +127,7 @@ void processRule(string name, ref string result) private static void AddLexicalRules(Dictionary> rules) { addUtf8Rules(); + addIdentifierRules(); addRealLiteralRules(); addNumericLiteralRules(); addIntegerLiteralRules(); @@ -142,6 +143,15 @@ void addUtf8Rules() rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); } + void addIdentifierRules() + { + rules.Add("IdentifierToken", [Join(" ", [new("'@'?"), RuleReference("IdentifierStartCharacter"), RuleReference("IdentifierPartCharacter")])]); + rules.Add("IdentifierStartCharacter", [RuleReference("LetterCharacter"), RuleReference("UnderscoreCharacter")]); + rules.Add("IdentifierPartCharacter", [RuleReference("LetterCharacter"), RuleReference("DecimalDigitCharacter"), RuleReference("ConnectingCharacter"), RuleReference("CombiningCharacter"), RuleReference("FormattingCharacter")]); + rules.Add("UnderscoreCharacter", [new("'_'"), new("""'\\u005' // unicode_escape_sequence for underscore""")]); + rules.Add("LetterCharacter", [new("""[\p{L}\p{Nl}]""")]); + } + void addRealLiteralRules() { var decimalDigitPlus = RuleReference("DecimalDigit").Suffix("+"); From 67ca764b730999f2fa5b69adcaceb7151775073d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 13:03:23 -0700 Subject: [PATCH 042/423] identifiers --- .../Portable/Generated/CSharp.Generated.g4 | 41 +++++++++++-------- .../Grammar/GrammarGenerator.cs | 22 +++++++++- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index ecc1f52f8106a..3f62ec8b1e51f 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -19,11 +19,12 @@ identifier_start_character ; letter_character - : [\p{L}\p{Nl}] + : /* [\p{L}\p{Nl}] category letter, all subcategories; category number, subcategory letter */ + | unicode_escape_sequence /* only escapes for categories L & Nl allowed */ ; underscore_character - : '\\u005' // unicode_escape_sequence for underscore + : '\\u005' /* unicode_escape_sequence for underscore */ | '_' ; @@ -35,6 +36,26 @@ identifier_part_character | letter_character ; +combining_character + : /* [\p{Mn}\p{Mc}] category Mark, subcategories non-spacing and spacing combining */ + | unicode_escape_sequence /* only escapes for categories Mn & Mc allowed */ + ; + +connecting_character + : /* [\p{Pc}] category Punctuation, subcategory connector */ + | unicode_escape_sequence /* only escapes for category Pc allowed */ + ; + +decimal_digit_character + : /* [\p{Nd}] category number, subcategory decimal digit */ + | unicode_escape_sequence /* only escapes for category Nd allowed */ + ; + +formatting_character + : /* [\p{Cf}] category Other, subcategory format. */ + | unicode_escape_sequence /* only escapes for category Cf allowed */ + ; + using_directive : 'global'? 'using' ('static' | ('unsafe'? name_equals))? type ';' ; @@ -1568,27 +1589,11 @@ base_parameter | parameter ; -combining_character - : /* see lexical specification */ - ; - -connecting_character - : /* see lexical specification */ - ; - -decimal_digit_character - : /* see lexical specification */ - ; - expression_or_pattern : expression | pattern ; -formatting_character - : /* see lexical specification */ - ; - interpolated_multi_line_raw_string_start_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index bd26419ff85c5..1c2229748ad68 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -148,8 +148,26 @@ void addIdentifierRules() rules.Add("IdentifierToken", [Join(" ", [new("'@'?"), RuleReference("IdentifierStartCharacter"), RuleReference("IdentifierPartCharacter")])]); rules.Add("IdentifierStartCharacter", [RuleReference("LetterCharacter"), RuleReference("UnderscoreCharacter")]); rules.Add("IdentifierPartCharacter", [RuleReference("LetterCharacter"), RuleReference("DecimalDigitCharacter"), RuleReference("ConnectingCharacter"), RuleReference("CombiningCharacter"), RuleReference("FormattingCharacter")]); - rules.Add("UnderscoreCharacter", [new("'_'"), new("""'\\u005' // unicode_escape_sequence for underscore""")]); - rules.Add("LetterCharacter", [new("""[\p{L}\p{Nl}]""")]); + rules.Add("UnderscoreCharacter", [new("'_'"), new("""'\\u005' /* unicode_escape_sequence for underscore */""")]); + rules.Add("LetterCharacter", [ + new("""/* [\p{L}\p{Nl}] category letter, all subcategories; category number, subcategory letter */"""), + new("unicode_escape_sequence /* only escapes for categories L & Nl allowed */")]); + + rules.Add("CombiningCharacter", [ + new("""/* [\p{Mn}\p{Mc}] category Mark, subcategories non-spacing and spacing combining */"""), + new("unicode_escape_sequence /* only escapes for categories Mn & Mc allowed */")]); + + rules.Add("DecimalDigitCharacter", [ + new("""/* [\p{Nd}] category number, subcategory decimal digit */"""), + new("unicode_escape_sequence /* only escapes for category Nd allowed */")]); + + rules.Add("ConnectingCharacter", [ + new("""/* [\p{Pc}] category Punctuation, subcategory connector */"""), + new("unicode_escape_sequence /* only escapes for category Pc allowed */")]); + + rules.Add("FormattingCharacter", [ + new("""/* [\p{Cf}] category Other, subcategory format. */"""), + new("unicode_escape_sequence /* only escapes for category Cf allowed */")]); } void addRealLiteralRules() From 9b2e6c1f7328c9b78ded72eafbfec391848cec5d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 13:17:53 -0700 Subject: [PATCH 043/423] in progress --- .../Portable/Generated/CSharp.Generated.g4 | 413 +++++++++++------- .../Grammar/GrammarGenerator.cs | 35 ++ 2 files changed, 286 insertions(+), 162 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 3f62ec8b1e51f..eb342c695f46e 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -77,6 +77,253 @@ attribute_target_specifier : syntax_token ':' ; +syntax_token + : character_literal_token + | identifier_token + | keyword + | numeric_literal_token + | operator_or_punctuator_token + | string_literal_token + ; + +character_literal_token + : '\'' character '\'' + ; + +character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_character + | unicode_escape_sequence + ; + +hexadecimal_escape_sequence + : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? + ; + +hexadecimal_digit + : 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | decimal_digit + ; + +decimal_digit + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + ; + +simple_escape_sequence + : '\\"' + | '\\0' + | '\\\'' + | '\\\\' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | '\\t' + | '\\v' + ; + +single_character + : /* anything but ', \, and new_line_character */ + ; + +unicode_escape_sequence + : '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + ; + +numeric_literal_token + : integer_literal_token + | real_literal_token + ; + +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token + ; + +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? + ; + +integer_type_suffix + : 'L' + | 'LU' + | 'Lu' + | 'U' + | 'UL' + | 'Ul' + | 'l' + | 'lU' + | 'lu' + | 'u' + | 'uL' + | 'ul' + ; + +hexadecimal_integer_literal_token + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? + ; + +real_literal_token + : '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ exponent_part real_type_suffix? + | decimal_digit+ real_type_suffix + ; + +exponent_part + : ('e' | 'E') sign? decimal_digit+ + ; + +sign + : '+' + | '-' + ; + +real_type_suffix + : 'D' + | 'F' + | 'M' + | 'd' + | 'f' + | 'm' + ; + +operator_or_punctuator_token + : 'abstract' + | 'as' + | 'base' + | 'bool' + | 'break' + | 'byte' + | 'case' + | 'catch' + | 'char' + | 'checked' + | 'class' + | 'const' + | 'continue' + | 'decimal' + | 'default' + | 'delegate' + | 'do' + | 'double' + | 'else' + | 'enum' + | 'event' + | 'explicit' + | 'extern' + | 'false' + | 'finally' + | 'fixed' + | 'float' + | 'for' + | 'foreach' + | 'goto' + | 'if' + | 'implicit' + | 'in' + | 'int' + | 'interface' + | 'internal' + | 'is' + | 'lock' + | 'long' + | 'namespace' + | 'new' + | 'null' + | 'object' + | 'operator' + | 'out' + | 'override' + | 'params' + | 'private' + | 'protected' + | 'public' + | 'readonly' + | 'ref' + | 'return' + | 'sbyte' + | 'sealed' + | 'short' + | 'sizeof' + | 'stackalloc' + | 'static' + | 'string' + | 'struct' + | 'switch' + | 'this' + | 'throw' + | 'true' + | 'try' + | 'typeof' + | 'uint' + | 'ulong' + | 'unchecked' + | 'unsafe' + | 'ushort' + | 'using' + | 'virtual' + | 'void' + | 'volatile' + | 'while' + ; + +string_literal_token + : regular_string_literal_token + | verbatim_string_literal_token + ; + +regular_string_literal_token + : '"' regular_string_literal_character* '"' + ; + +regular_string_literal_character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_regular_string_literal_character + | unicode_escape_sequence + ; + +verbatim_string_literal_token + : '@"' verbatim_string_literal_character* '"' + ; + +verbatim_string_literal_character + : quote_escape_sequence + | single_verbatim_string_literal_character + ; + +quote_escape_sequence + : '""' + ; + +single_verbatim_string_literal_character + : /* anything but quotation mark (U+0022) */ + ; + attribute : name attribute_argument_list? ; @@ -1326,149 +1573,6 @@ line_directive_trivia : '#' 'line' (numeric_literal_token | 'default' | 'hidden') string_literal_token? ; -numeric_literal_token - : integer_literal_token - | real_literal_token - ; - -integer_literal_token - : decimal_integer_literal_token - | hexadecimal_integer_literal_token - ; - -decimal_integer_literal_token - : decimal_digit+ integer_type_suffix? - ; - -decimal_digit - : '0' - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - ; - -integer_type_suffix - : 'L' - | 'LU' - | 'Lu' - | 'U' - | 'UL' - | 'Ul' - | 'l' - | 'lU' - | 'lu' - | 'u' - | 'uL' - | 'ul' - ; - -hexadecimal_integer_literal_token - : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? - ; - -hexadecimal_digit - : 'A' - | 'B' - | 'C' - | 'D' - | 'E' - | 'F' - | 'a' - | 'b' - | 'c' - | 'd' - | 'e' - | 'f' - | decimal_digit - ; - -real_literal_token - : '.' decimal_digit+ exponent_part? real_type_suffix? - | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? - | decimal_digit+ exponent_part real_type_suffix? - | decimal_digit+ real_type_suffix - ; - -exponent_part - : ('e' | 'E') sign? decimal_digit+ - ; - -sign - : '+' - | '-' - ; - -real_type_suffix - : 'D' - | 'F' - | 'M' - | 'd' - | 'f' - | 'm' - ; - -string_literal_token - : regular_string_literal_token - | verbatim_string_literal_token - ; - -regular_string_literal_token - : '"' regular_string_literal_character* '"' - ; - -regular_string_literal_character - : hexadecimal_escape_sequence - | simple_escape_sequence - | single_regular_string_literal_character - | unicode_escape_sequence - ; - -hexadecimal_escape_sequence - : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? - ; - -simple_escape_sequence - : '\\"' - | '\\0' - | '\\\'' - | '\\\\' - | '\\a' - | '\\b' - | '\\f' - | '\\n' - | '\\r' - | '\\t' - | '\\v' - ; - -unicode_escape_sequence - : '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit - | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit - ; - -verbatim_string_literal_token - : '@"' verbatim_string_literal_character* '"' - ; - -verbatim_string_literal_character - : quote_escape_sequence - | single_verbatim_string_literal_character - ; - -quote_escape_sequence - : '""' - ; - -single_verbatim_string_literal_character - : /* anything but quotation mark (U+0022) */ - ; - line_span_directive_trivia : '#' 'line' line_directive_position '-' line_directive_position numeric_literal_token? string_literal_token ; @@ -1537,21 +1641,6 @@ literal_expression | utf8_string_literal_token ; -character_literal_token - : '\'' character '\'' - ; - -character - : hexadecimal_escape_sequence - | simple_escape_sequence - | single_character - | unicode_escape_sequence - ; - -single_character - : /* anything but ', \, and new_line_character */ - ; - utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1610,19 +1699,19 @@ interpolated_string_text_token : /* see lexical specification */ ; -multi_line_raw_string_literal_token +keyword : /* see lexical specification */ ; -single_line_raw_string_literal_token +multi_line_raw_string_literal_token : /* see lexical specification */ ; -single_regular_string_literal_character +single_line_raw_string_literal_token : /* see lexical specification */ ; -syntax_token +single_regular_string_literal_character : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 1c2229748ad68..932e11fea716a 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -127,6 +127,8 @@ void processRule(string name, ref string result) private static void AddLexicalRules(Dictionary> rules) { addUtf8Rules(); + addTokenRules(); + addKeywordRules(); addIdentifierRules(); addRealLiteralRules(); addNumericLiteralRules(); @@ -143,6 +145,36 @@ void addUtf8Rules() rules.Add("Utf8Suffix", [new("'u8'"), new("'U8'")]); } + void addTokenRules() + { + rules["SyntaxToken"].AddRange([RuleReference("IdentifierToken"), RuleReference("Keyword"), RuleReference("NumericLiteralToken"), RuleReference("CharacterLiteralToken"), RuleReference("StringLiteralToken"), RuleReference("OperatorOrPunctuatorToken")]); + rules.Add("OperatorOrPunctuatorToken", JoinWords( + GetMembers().Where(m => m.ToString().EndsWith("Keyword")).Select(m => m.ToString()[0..^"Keyword".Length].ToLower())) + .Concat( + "abstract", "as", "base", "bool", "break" + , "byte", "case", "catch", "char", "checked" + , "class", "const", "continue", "decimal", "default" + , "delegate", "do", "double", "else", "enum" + , "event", "explicit", "extern", "false", "finally" + , "fixed", "float", "for", "foreach", "goto" + , "if", "implicit", "in", "int", "interface" + , "internal", "is", "lock", "long", "namespace" + , "new", "null", "object", "operator", "out" + , "override", "params", "private", "protected", "public" + , "readonly", "ref", "return", "sbyte", "sealed" + , "short", "sizeof", "stackalloc", "static", "string" + , "struct", "switch", "this", "throw", "true" + , "try", "typeof", "uint", "ulong", "unchecked" + , "unsafe", "ushort", "using", "virtual", "void" + , "volatile", "while")); + } + + void addKeywordRules() + { + // rules.Add() + // + } + void addIdentifierRules() { rules.Add("IdentifierToken", [Join(" ", [new("'@'?"), RuleReference("IdentifierStartCharacter"), RuleReference("IdentifierPartCharacter")])]); @@ -231,6 +263,9 @@ void addCharacterLiteralRules() } } + private static List JoinWords(params string[] strings) + => strings.Select(s => new Production($"""'{s}'""")).ToList(); + private static Production Join(string delim, IEnumerable productions) => new(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); From b242616f41a9d65753cec6d60fdbd8a312f89ead Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 13:22:20 -0700 Subject: [PATCH 044/423] add keyword --- .../Portable/Generated/CSharp.Generated.g4 | 128 +++++++++--------- .../Grammar/GrammarGenerator.cs | 21 +-- 2 files changed, 68 insertions(+), 81 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index eb342c695f46e..aa66994737527 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -153,66 +153,12 @@ unicode_escape_sequence | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit ; -numeric_literal_token - : integer_literal_token - | real_literal_token - ; - -integer_literal_token - : decimal_integer_literal_token - | hexadecimal_integer_literal_token - ; - -decimal_integer_literal_token - : decimal_digit+ integer_type_suffix? - ; - -integer_type_suffix - : 'L' - | 'LU' - | 'Lu' - | 'U' - | 'UL' - | 'Ul' - | 'l' - | 'lU' - | 'lu' - | 'u' - | 'uL' - | 'ul' - ; - -hexadecimal_integer_literal_token - : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? - ; - -real_literal_token - : '.' decimal_digit+ exponent_part? real_type_suffix? - | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? - | decimal_digit+ exponent_part real_type_suffix? - | decimal_digit+ real_type_suffix - ; - -exponent_part - : ('e' | 'E') sign? decimal_digit+ - ; - -sign - : '+' - | '-' - ; - -real_type_suffix - : 'D' - | 'F' - | 'M' - | 'd' - | 'f' - | 'm' - ; - -operator_or_punctuator_token - : 'abstract' +keyword + : '__arglist' + | '__makeref' + | '__reftype' + | '__refvalue' + | 'abstract' | 'as' | 'base' | 'bool' @@ -291,6 +237,64 @@ operator_or_punctuator_token | 'while' ; +numeric_literal_token + : integer_literal_token + | real_literal_token + ; + +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token + ; + +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? + ; + +integer_type_suffix + : 'L' + | 'LU' + | 'Lu' + | 'U' + | 'UL' + | 'Ul' + | 'l' + | 'lU' + | 'lu' + | 'u' + | 'uL' + | 'ul' + ; + +hexadecimal_integer_literal_token + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? + ; + +real_literal_token + : '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ exponent_part real_type_suffix? + | decimal_digit+ real_type_suffix + ; + +exponent_part + : ('e' | 'E') sign? decimal_digit+ + ; + +sign + : '+' + | '-' + ; + +real_type_suffix + : 'D' + | 'F' + | 'M' + | 'd' + | 'f' + | 'm' + ; + string_literal_token : regular_string_literal_token | verbatim_string_literal_token @@ -1699,11 +1703,11 @@ interpolated_string_text_token : /* see lexical specification */ ; -keyword +multi_line_raw_string_literal_token : /* see lexical specification */ ; -multi_line_raw_string_literal_token +operator_or_punctuator_token : /* see lexical specification */ ; diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 932e11fea716a..f4588cb890d8b 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -148,25 +148,8 @@ void addUtf8Rules() void addTokenRules() { rules["SyntaxToken"].AddRange([RuleReference("IdentifierToken"), RuleReference("Keyword"), RuleReference("NumericLiteralToken"), RuleReference("CharacterLiteralToken"), RuleReference("StringLiteralToken"), RuleReference("OperatorOrPunctuatorToken")]); - rules.Add("OperatorOrPunctuatorToken", JoinWords( - GetMembers().Where(m => m.ToString().EndsWith("Keyword")).Select(m => m.ToString()[0..^"Keyword".Length].ToLower())) - .Concat( - "abstract", "as", "base", "bool", "break" - , "byte", "case", "catch", "char", "checked" - , "class", "const", "continue", "decimal", "default" - , "delegate", "do", "double", "else", "enum" - , "event", "explicit", "extern", "false", "finally" - , "fixed", "float", "for", "foreach", "goto" - , "if", "implicit", "in", "int", "interface" - , "internal", "is", "lock", "long", "namespace" - , "new", "null", "object", "operator", "out" - , "override", "params", "private", "protected", "public" - , "readonly", "ref", "return", "sbyte", "sealed" - , "short", "sizeof", "stackalloc", "static", "string" - , "struct", "switch", "this", "throw", "true" - , "try", "typeof", "uint", "ulong", "unchecked" - , "unsafe", "ushort", "using", "virtual", "void" - , "volatile", "while")); + rules.Add("Keyword", JoinWords( + GetMembers().Where(SyntaxFacts.IsReservedKeyword).Select(SyntaxFacts.GetText).ToArray())); } void addKeywordRules() From 84540c31a09463a336f7d9fdfa628a6ac5b20aba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 13:23:53 -0700 Subject: [PATCH 045/423] tokens at end --- .../Portable/Generated/CSharp.Generated.g4 | 502 +++++++++--------- .../Grammar/GrammarGenerator.cs | 2 +- 2 files changed, 252 insertions(+), 252 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index aa66994737527..4f66e435f72f1 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -77,257 +77,6 @@ attribute_target_specifier : syntax_token ':' ; -syntax_token - : character_literal_token - | identifier_token - | keyword - | numeric_literal_token - | operator_or_punctuator_token - | string_literal_token - ; - -character_literal_token - : '\'' character '\'' - ; - -character - : hexadecimal_escape_sequence - | simple_escape_sequence - | single_character - | unicode_escape_sequence - ; - -hexadecimal_escape_sequence - : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? - ; - -hexadecimal_digit - : 'A' - | 'B' - | 'C' - | 'D' - | 'E' - | 'F' - | 'a' - | 'b' - | 'c' - | 'd' - | 'e' - | 'f' - | decimal_digit - ; - -decimal_digit - : '0' - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - ; - -simple_escape_sequence - : '\\"' - | '\\0' - | '\\\'' - | '\\\\' - | '\\a' - | '\\b' - | '\\f' - | '\\n' - | '\\r' - | '\\t' - | '\\v' - ; - -single_character - : /* anything but ', \, and new_line_character */ - ; - -unicode_escape_sequence - : '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit - | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit - ; - -keyword - : '__arglist' - | '__makeref' - | '__reftype' - | '__refvalue' - | 'abstract' - | 'as' - | 'base' - | 'bool' - | 'break' - | 'byte' - | 'case' - | 'catch' - | 'char' - | 'checked' - | 'class' - | 'const' - | 'continue' - | 'decimal' - | 'default' - | 'delegate' - | 'do' - | 'double' - | 'else' - | 'enum' - | 'event' - | 'explicit' - | 'extern' - | 'false' - | 'finally' - | 'fixed' - | 'float' - | 'for' - | 'foreach' - | 'goto' - | 'if' - | 'implicit' - | 'in' - | 'int' - | 'interface' - | 'internal' - | 'is' - | 'lock' - | 'long' - | 'namespace' - | 'new' - | 'null' - | 'object' - | 'operator' - | 'out' - | 'override' - | 'params' - | 'private' - | 'protected' - | 'public' - | 'readonly' - | 'ref' - | 'return' - | 'sbyte' - | 'sealed' - | 'short' - | 'sizeof' - | 'stackalloc' - | 'static' - | 'string' - | 'struct' - | 'switch' - | 'this' - | 'throw' - | 'true' - | 'try' - | 'typeof' - | 'uint' - | 'ulong' - | 'unchecked' - | 'unsafe' - | 'ushort' - | 'using' - | 'virtual' - | 'void' - | 'volatile' - | 'while' - ; - -numeric_literal_token - : integer_literal_token - | real_literal_token - ; - -integer_literal_token - : decimal_integer_literal_token - | hexadecimal_integer_literal_token - ; - -decimal_integer_literal_token - : decimal_digit+ integer_type_suffix? - ; - -integer_type_suffix - : 'L' - | 'LU' - | 'Lu' - | 'U' - | 'UL' - | 'Ul' - | 'l' - | 'lU' - | 'lu' - | 'u' - | 'uL' - | 'ul' - ; - -hexadecimal_integer_literal_token - : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? - ; - -real_literal_token - : '.' decimal_digit+ exponent_part? real_type_suffix? - | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? - | decimal_digit+ exponent_part real_type_suffix? - | decimal_digit+ real_type_suffix - ; - -exponent_part - : ('e' | 'E') sign? decimal_digit+ - ; - -sign - : '+' - | '-' - ; - -real_type_suffix - : 'D' - | 'F' - | 'M' - | 'd' - | 'f' - | 'm' - ; - -string_literal_token - : regular_string_literal_token - | verbatim_string_literal_token - ; - -regular_string_literal_token - : '"' regular_string_literal_character* '"' - ; - -regular_string_literal_character - : hexadecimal_escape_sequence - | simple_escape_sequence - | single_regular_string_literal_character - | unicode_escape_sequence - ; - -verbatim_string_literal_token - : '@"' verbatim_string_literal_character* '"' - ; - -verbatim_string_literal_character - : quote_escape_sequence - | single_verbatim_string_literal_character - ; - -quote_escape_sequence - : '""' - ; - -single_verbatim_string_literal_character - : /* anything but quotation mark (U+0022) */ - ; - attribute : name attribute_argument_list? ; @@ -1577,6 +1326,149 @@ line_directive_trivia : '#' 'line' (numeric_literal_token | 'default' | 'hidden') string_literal_token? ; +numeric_literal_token + : integer_literal_token + | real_literal_token + ; + +integer_literal_token + : decimal_integer_literal_token + | hexadecimal_integer_literal_token + ; + +decimal_integer_literal_token + : decimal_digit+ integer_type_suffix? + ; + +decimal_digit + : '0' + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + ; + +integer_type_suffix + : 'L' + | 'LU' + | 'Lu' + | 'U' + | 'UL' + | 'Ul' + | 'l' + | 'lU' + | 'lu' + | 'u' + | 'uL' + | 'ul' + ; + +hexadecimal_integer_literal_token + : ('0x' | '0X') hexadecimal_digit+ integer_type_suffix? + ; + +hexadecimal_digit + : 'A' + | 'B' + | 'C' + | 'D' + | 'E' + | 'F' + | 'a' + | 'b' + | 'c' + | 'd' + | 'e' + | 'f' + | decimal_digit + ; + +real_literal_token + : '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ '.' decimal_digit+ exponent_part? real_type_suffix? + | decimal_digit+ exponent_part real_type_suffix? + | decimal_digit+ real_type_suffix + ; + +exponent_part + : ('e' | 'E') sign? decimal_digit+ + ; + +sign + : '+' + | '-' + ; + +real_type_suffix + : 'D' + | 'F' + | 'M' + | 'd' + | 'f' + | 'm' + ; + +string_literal_token + : regular_string_literal_token + | verbatim_string_literal_token + ; + +regular_string_literal_token + : '"' regular_string_literal_character* '"' + ; + +regular_string_literal_character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_regular_string_literal_character + | unicode_escape_sequence + ; + +hexadecimal_escape_sequence + : '\\x' hexadecimal_digit hexadecimal_digit? hexadecimal_digit? hexadecimal_digit? + ; + +simple_escape_sequence + : '\\"' + | '\\0' + | '\\\'' + | '\\\\' + | '\\a' + | '\\b' + | '\\f' + | '\\n' + | '\\r' + | '\\t' + | '\\v' + ; + +unicode_escape_sequence + : '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + ; + +verbatim_string_literal_token + : '@"' verbatim_string_literal_character* '"' + ; + +verbatim_string_literal_character + : quote_escape_sequence + | single_verbatim_string_literal_character + ; + +quote_escape_sequence + : '""' + ; + +single_verbatim_string_literal_character + : /* anything but quotation mark (U+0022) */ + ; + line_span_directive_trivia : '#' 'line' line_directive_position '-' line_directive_position numeric_literal_token? string_literal_token ; @@ -1645,6 +1537,21 @@ literal_expression | utf8_string_literal_token ; +character_literal_token + : '\'' character '\'' + ; + +character + : hexadecimal_escape_sequence + | simple_escape_sequence + | single_character + | unicode_escape_sequence + ; + +single_character + : /* anything but ', \, and new_line_character */ + ; + utf8_multi_line_raw_string_literal_token : multi_line_raw_string_literal_token utf8_suffix ; @@ -1657,6 +1564,99 @@ utf8_string_literal_token : string_literal_token utf8_suffix ; +syntax_token + : character_literal_token + | identifier_token + | keyword + | numeric_literal_token + | operator_or_punctuator_token + | string_literal_token + ; + +keyword + : '__arglist' + | '__makeref' + | '__reftype' + | '__refvalue' + | 'abstract' + | 'as' + | 'base' + | 'bool' + | 'break' + | 'byte' + | 'case' + | 'catch' + | 'char' + | 'checked' + | 'class' + | 'const' + | 'continue' + | 'decimal' + | 'default' + | 'delegate' + | 'do' + | 'double' + | 'else' + | 'enum' + | 'event' + | 'explicit' + | 'extern' + | 'false' + | 'finally' + | 'fixed' + | 'float' + | 'for' + | 'foreach' + | 'goto' + | 'if' + | 'implicit' + | 'in' + | 'int' + | 'interface' + | 'internal' + | 'is' + | 'lock' + | 'long' + | 'namespace' + | 'new' + | 'null' + | 'object' + | 'operator' + | 'out' + | 'override' + | 'params' + | 'private' + | 'protected' + | 'public' + | 'readonly' + | 'ref' + | 'return' + | 'sbyte' + | 'sealed' + | 'short' + | 'sizeof' + | 'stackalloc' + | 'static' + | 'string' + | 'struct' + | 'switch' + | 'this' + | 'throw' + | 'true' + | 'try' + | 'typeof' + | 'uint' + | 'ulong' + | 'unchecked' + | 'unsafe' + | 'ushort' + | 'using' + | 'virtual' + | 'void' + | 'volatile' + | 'while' + ; + utf8_suffix : 'U8' | 'u8' diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index f4588cb890d8b..a64263f262a45 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -87,7 +87,7 @@ public static string Run(List types) // Define a few major sections to help keep the grammar file naturally grouped. var majorRules = ImmutableArray.Create( - "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax", "LiteralExpressionSyntax", "Utf8Suffix"); + "CompilationUnitSyntax", "MemberDeclarationSyntax", "TypeSyntax", "StatementSyntax", "ExpressionSyntax", "XmlNodeSyntax", "StructuredTriviaSyntax", "LiteralExpressionSyntax", "SyntaxToken", "Utf8Suffix"); var result = "// " + Environment.NewLine + "grammar csharp;" + Environment.NewLine; From 64b828115ff27ad8cbc3564cf44ce21a1929a442 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 13:26:29 -0700 Subject: [PATCH 046/423] add operators and punctuation --- .../Portable/Generated/CSharp.Generated.g4 | 73 +++++++++++++++++-- .../Grammar/GrammarGenerator.cs | 13 +--- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 4f66e435f72f1..e35899c97f3c8 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1569,7 +1569,7 @@ syntax_token | identifier_token | keyword | numeric_literal_token - | operator_or_punctuator_token + | operator_or_punctuation_token | string_literal_token ; @@ -1657,6 +1657,73 @@ keyword | 'while' ; +operator_or_punctuation_token + : '!' + | '!=' + | '"' + | '#' + | '%' + | '%=' + | '&&' + | '&' + | '&=' + | ''' + | '(' + | ')' + | '*' + | '*=' + | '+' + | '++' + | '+=' + | ',' + | '-' + | '--' + | '-->' + | '-=' + | '->' + | '.' + | '..' + | '/' + | '/=' + | '/>' + | ':' + | '::' + | ';' + | '' | '-=' | '->' | '.' | '..' - | '/' | '/=' | '/>' | ':' @@ -1691,37 +1710,24 @@ operator_or_punctuation_token | ';' | '' - | '-=' | '->' | '.' | '..' - | '/=' | '/>' | ':' | '::' @@ -1711,22 +1717,16 @@ punctuation_token | '' ; +xml_c_data_section + : '' + ; + xml_element : xml_element_start_tag xml_node* xml_element_end_tag ; @@ -1429,11 +1429,7 @@ formatting_character ; keyword - : '__arglist' - | '__makeref' - | '__reftype' - | '__refvalue' - | 'as' + : 'as' | 'base' | 'bool' | 'break' @@ -1493,6 +1489,10 @@ keyword | 'using' | 'void' | 'while' + | '__arglist' + | '__makeref' + | '__reftype' + | '__refvalue' | modifier ; @@ -1525,16 +1525,16 @@ decimal_digit integer_type_suffix : 'L' - | 'LU' - | 'Lu' - | 'U' - | 'UL' - | 'Ul' | 'l' + | 'LU' | 'lU' + | 'Lu' | 'lu' + | 'U' | 'u' + | 'UL' | 'uL' + | 'Ul' | 'ul' ; @@ -1544,16 +1544,16 @@ hexadecimal_integer_literal_token hexadecimal_digit : 'A' - | 'B' - | 'C' - | 'D' - | 'E' - | 'F' | 'a' + | 'B' | 'b' + | 'C' | 'c' + | 'D' | 'd' + | 'E' | 'e' + | 'F' | 'f' | decimal_digit ; @@ -1571,10 +1571,10 @@ exponent_part real_type_suffix : 'D' - | 'F' - | 'M' | 'd' + | 'F' | 'f' + | 'M' | 'm' ; @@ -1596,8 +1596,6 @@ hexadecimal_escape_sequence simple_escape_sequence : '\\"' | '\\0' - | '\\\'' - | '\\\\' | '\\a' | '\\b' | '\\f' @@ -1605,6 +1603,8 @@ simple_escape_sequence | '\\r' | '\\t' | '\\v' + | '\\\'' + | '\\\\' ; single_character @@ -1612,8 +1612,8 @@ single_character ; unicode_escape_sequence - : '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit - | '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + : '\\u' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit + | '\\U' hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit hexadecimal_digit ; string_literal_token @@ -1685,10 +1685,10 @@ operator_token | '>>>=' | '??' | '??=' - | '^' - | '^=' | 'as' | 'is' + | '^' + | '^=' | '|' | '|=' | '||' diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index f82e0b646880b..aa28793cc4955 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -370,7 +370,7 @@ internal readonly struct Production( public readonly ImmutableArray ReferencedRules = referencedRules?.ToImmutableArray() ?? ImmutableArray.Empty; public override string ToString() => Text; - public int CompareTo(Production other) => StringComparer.Ordinal.Compare(this.Text, other.Text); + public int CompareTo(Production other) => StringComparer.OrdinalIgnoreCase.Compare(this.Text, other.Text); public Production Prefix(string prefix) => new Production(prefix + this, ReferencedRules); public Production Suffix(string suffix, bool when = true) => when ? new Production(this + suffix, ReferencedRules) : this; public Production Parenthesize(bool when = true) => when ? Prefix("(").Suffix(")") : this; From 6047067ee86f4b669810d1170e57fc576ca78609 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 17:28:07 -0700 Subject: [PATCH 070/423] simplify --- .../Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index aa28793cc4955..092dc85268556 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -298,7 +298,7 @@ private static List JoinWords(params string[] strings) => strings.Select(s => new Production($"""'{Escape(s)}'""")).ToList(); private static string Escape(string s) - => s.Replace("""\""", """\\""").Replace("'", """\'"""); + => s.Replace(@"\", @"\\").Replace("'", @"\'"); private static Production Join(string delim, IEnumerable productions) => new(string.Join(delim, productions.Where(p => p.Text.Length > 0)), productions.SelectMany(p => p.ReferencedRules)); From 37626b255984ce25a11dc81e3b9dd0acf4e237a8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 17:30:58 -0700 Subject: [PATCH 071/423] simplify --- .../CSharp/Portable/Generated/CSharp.Generated.g4 | 11 +++-------- .../CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 10 ++++------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index bbf51b5ced676..9869a698e4c87 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -968,15 +968,15 @@ literal_expression ; utf8_multi_line_raw_string_literal_token - : multi_line_raw_string_literal_token utf8_suffix + : multi_line_raw_string_literal_token ('u8' | 'U8') ; utf8_single_line_raw_string_literal_token - : single_line_raw_string_literal_token utf8_suffix + : single_line_raw_string_literal_token ('u8' | 'U8') ; utf8_string_literal_token - : string_literal_token utf8_suffix + : string_literal_token ('u8' | 'U8') ; make_ref_expression @@ -1725,11 +1725,6 @@ punctuation_token | '}' ; -utf8_suffix - : 'U8' - | 'u8' - ; - base_argument_list : argument_list | bracketed_argument_list diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs index 092dc85268556..8a70375d3cebd 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs @@ -90,8 +90,7 @@ public static string Run(List types) "StructuredTriviaSyntax", // Place all syntax tokens at the end to keep them out of the way. "SyntaxToken", - .. rules["SyntaxToken"].SelectMany(r => r.ReferencedRules), - "Utf8Suffix"]; + .. rules["SyntaxToken"].SelectMany(r => r.ReferencedRules)]; var result = "// " + Environment.NewLine + "grammar csharp;" + Environment.NewLine; @@ -134,10 +133,9 @@ private static void AddLexicalRules(Dictionary> rules) void addUtf8Rules() { - rules.Add("Utf8StringLiteralToken", [Join(" ", [RuleReference("StringLiteralToken"), RuleReference("Utf8Suffix")])]); - rules.Add("Utf8MultiLineRawStringLiteralToken", [Join(" ", [RuleReference("MultiLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); - rules.Add("Utf8SingleLineRawStringLiteralToken", [Join(" ", [RuleReference("SingleLineRawStringLiteralToken"), RuleReference("Utf8Suffix")])]); - rules.Add("Utf8Suffix", [.. permuteCasing("U8")]); + rules.Add("Utf8StringLiteralToken", [Join(" ", [RuleReference("StringLiteralToken"), new("('u8' | 'U8')")])]); + rules.Add("Utf8MultiLineRawStringLiteralToken", [Join(" ", [RuleReference("MultiLineRawStringLiteralToken"), new("('u8' | 'U8')")])]); + rules.Add("Utf8SingleLineRawStringLiteralToken", [Join(" ", [RuleReference("SingleLineRawStringLiteralToken"), new("('u8' | 'U8')")])]); } void addTokenRules() From 4986799bc541e1cc2a5ef8dfdb7e1895a9773356 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sat, 4 May 2024 17:33:56 -0700 Subject: [PATCH 072/423] Remove xml --- src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 | 6 ------ .../CSharpSyntaxGenerator/Grammar/GrammarGenerator.cs | 5 ++++- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 9869a698e4c87..3ff88a7cf2461 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -1701,7 +1701,6 @@ punctuation_token | '(' | ')' | ',' - | '-->' | '->' | '.' | '..' @@ -1709,18 +1708,13 @@ punctuation_token | ':' | '::' | ';' - | ' - - - - - - diff --git a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj index d62e1c2961bef..6ec1177df567e 100644 --- a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj +++ b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj @@ -49,7 +49,6 @@ - diff --git a/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj b/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj index d3e2de0ff5753..e76457a1358fc 100644 --- a/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj +++ b/src/Tools/AnalyzerRunner/AnalyzerRunner.csproj @@ -22,7 +22,6 @@ - diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index d739438f4952f..7b49bf81a0c9a 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -167,17 +167,6 @@ TargetFramework=net472 BindingRedirect - - Workspaces.MSBuild - BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup - true - TargetFramework=net472 - BindingRedirect - - false - CSharpWorkspace BuiltProjectOutputGroup;SatelliteDllsProjectOutputGroup diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs b/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs index 0cba06c47234c..05ad5789c365a 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Build/ProjectBuildManager.cs @@ -11,11 +11,10 @@ using System.Threading.Tasks; using System.Xml; using Microsoft.Build.Framework; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.MSBuild.Build +namespace Microsoft.CodeAnalysis.MSBuild { internal class ProjectBuildManager { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs b/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs index 4bfd90f760c37..bc1e0320e7e40 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/BuildHost.cs @@ -4,7 +4,6 @@ extern alias workspaces; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -13,26 +12,22 @@ using Microsoft.Build.Construction; using Microsoft.Build.Locator; using Microsoft.Build.Logging; -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.Extensions.Logging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; +namespace Microsoft.CodeAnalysis.MSBuild; -internal sealed class BuildHost +internal sealed class BuildHost : IBuildHost { - private readonly ILogger _logger; + private readonly BuildHostLogger _logger; private readonly ImmutableDictionary _globalMSBuildProperties; private readonly string? _binaryLogPath; private readonly RpcServer _server; private readonly object _gate = new object(); private ProjectBuildManager? _buildManager; - public BuildHost(ILoggerFactory loggerFactory, ImmutableDictionary globalMSBuildProperties, string? binaryLogPath, RpcServer server) + public BuildHost(BuildHostLogger logger, ImmutableDictionary globalMSBuildProperties, string? binaryLogPath, RpcServer server) { - _logger = loggerFactory.CreateLogger(); + _logger = logger; _globalMSBuildProperties = globalMSBuildProperties; _binaryLogPath = binaryLogPath; _server = server; @@ -109,7 +104,7 @@ private bool TryEnsureMSBuildLoaded(string projectOrSolutionFilePath) #if NET472 || NET6_0 // If we're compiling against net472 or net6.0, we get our MemberNotNull from the workspaces assembly. It has it in the net6.0 case since we're consuming the netstandard2.0 version of Workspaces. [workspaces::System.Diagnostics.CodeAnalysis.MemberNotNull(nameof(_buildManager))] #else // If we're compiling against net7.0 or higher, then we're getting it staright from the framework. - [MemberNotNull(nameof(_buildManager))] + [System.Diagnostics.CodeAnalysis.MemberNotNull(nameof(_buildManager))] #endif [MethodImpl(MethodImplOptions.NoInlining)] // Do not inline this, since this creates MSBuild types which are being loaded by the caller private void CreateBuildManager() @@ -177,8 +172,8 @@ public async Task LoadProjectFileAsync(string projectFilePath, string langu ProjectFileLoader projectLoader = languageName switch { - LanguageNames.CSharp => new CSharp.CSharpProjectFileLoader(), - LanguageNames.VisualBasic => new VisualBasic.VisualBasicProjectFileLoader(), + LanguageNames.CSharp => new CSharpProjectFileLoader(), + LanguageNames.VisualBasic => new VisualBasicProjectFileLoader(), _ => throw ExceptionUtilities.UnexpectedValue(languageName) }; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/BuildHostLogger.cs b/src/Workspaces/Core/MSBuild.BuildHost/BuildHostLogger.cs new file mode 100644 index 0000000000000..c58341779c7f8 --- /dev/null +++ b/src/Workspaces/Core/MSBuild.BuildHost/BuildHostLogger.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; + +namespace Microsoft.CodeAnalysis.MSBuild; + +internal sealed class BuildHostLogger(TextWriter output) +{ + public void LogInformation(string message) + => output.WriteLine(message); + + public void LogCritical(string message) + => output.WriteLine(message); +} diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs index 5908aa94455a3..35bd8d2b0a204 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpCommandLineArgumentReader.cs @@ -3,10 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.CSharp +namespace Microsoft.CodeAnalysis.MSBuild { internal class CSharpCommandLineArgumentReader : CommandLineArgumentReader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs index 91846935a3ae5..cc81848d8f3e4 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFile.cs @@ -5,11 +5,9 @@ using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.CSharp +namespace Microsoft.CodeAnalysis.MSBuild { internal class CSharpProjectFile : ProjectFile { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs index b578b9d1b0ae9..9f7400d847110 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/CSharp/CSharpProjectFileLoader.cs @@ -2,12 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.CSharp +namespace Microsoft.CodeAnalysis.MSBuild { internal partial class CSharpProjectFileLoader : ProjectFileLoader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs index 9d30cf894f6a3..aa616bc5e3f5e 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLog.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Linq; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { internal class DiagnosticLog : IEnumerable { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs index b0a3710cf91b5..b1edc6c883208 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogItem.cs @@ -4,7 +4,7 @@ using System; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { internal class MSBuildDiagnosticLogItem : DiagnosticLogItem { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs index 0454566bdb2f8..43f720925310d 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/MSBuildDiagnosticLogger.cs @@ -6,7 +6,7 @@ using Roslyn.Utilities; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { internal class MSBuildDiagnosticLogger : MSB.Framework.ILogger { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs index 92fb55d25e1b1..788b90134bd08 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFile.cs @@ -11,14 +11,12 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; using MSB = Microsoft.Build; namespace Microsoft.CodeAnalysis.MSBuild { - internal abstract class ProjectFile + internal abstract class ProjectFile : IProjectFile { private readonly ProjectFileLoader _loader; private readonly MSB.Evaluation.Project? _loadedProject; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs index d63ccabff51d7..382d61e014f62 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileLoader.cs @@ -5,8 +5,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; namespace Microsoft.CodeAnalysis.MSBuild diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs index 4264b0dc46efc..829829239183d 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicCommandLineArgumentReader.cs @@ -4,11 +4,10 @@ using System; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild; using Roslyn.Utilities; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.VisualBasic +namespace Microsoft.CodeAnalysis.MSBuild { internal class VisualBasicCommandLineArgumentReader : CommandLineArgumentReader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs index d8f195f136e76..2acfd35e517e1 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFile.cs @@ -4,12 +4,9 @@ using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.VisualBasic +namespace Microsoft.CodeAnalysis.MSBuild { internal class VisualBasicProjectFile : ProjectFile { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs index 99f82fa28e1e6..0ccc774e167f4 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs @@ -2,12 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Build; -using Microsoft.CodeAnalysis.MSBuild.Logging; using MSB = Microsoft.Build; -namespace Microsoft.CodeAnalysis.VisualBasic +namespace Microsoft.CodeAnalysis.MSBuild { internal partial class VisualBasicProjectFileLoader : ProjectFileLoader { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index 9fe22620bdb1c..c895b665da4dc 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -29,7 +29,6 @@ - @@ -40,8 +39,8 @@ - + diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Program.cs b/src/Workspaces/Core/MSBuild.BuildHost/Program.cs index 973f7d5151535..18b67f5d7e705 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Program.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Program.cs @@ -6,11 +6,9 @@ using System.Collections.Immutable; using System.CommandLine; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.Extensions.Logging; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class Program { @@ -31,26 +29,13 @@ internal static async Task Main(string[] args) propertiesBuilder.Add(propertyParts[0], propertyParts[1]); } - // Create a console logger that logs everything to standard error instead of standard out; by setting the threshold to Trace - // everything will go to standard error. - var loggerFactory = LoggerFactory.Create(builder => - builder.AddConsole(configure => - { - // DisableColors is deprecated in favor of us moving to simple console, but that loses the LogToStandardErrorThreshold - // which we also need -#pragma warning disable CS0618 - configure.DisableColors = true; -#pragma warning restore CS0618 - configure.LogToStandardErrorThreshold = LogLevel.Trace; - })); - - var logger = loggerFactory.CreateLogger(typeof(Program)); + var logger = new BuildHostLogger(Console.Error); logger.LogInformation($"BuildHost Runtime Version: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}"); var server = new RpcServer(sendingStream: Console.OpenStandardOutput(), receivingStream: Console.OpenStandardInput()); - var targetObject = server.AddTarget(new BuildHost(loggerFactory, propertiesBuilder.ToImmutable(), binaryLogPath, server)); + var targetObject = server.AddTarget(new BuildHost(logger, propertiesBuilder.ToImmutable(), binaryLogPath, server)); Contract.ThrowIfFalse(targetObject == 0, "The first object registered should have target 0, which is assumed by the client."); await server.RunAsync().ConfigureAwait(false); diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLogItem.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DiagnosticLogItem.cs similarity index 95% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLogItem.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DiagnosticLogItem.cs index 5be36343b592d..7b9d8897001f5 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/Logging/DiagnosticLogItem.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DiagnosticLogItem.cs @@ -5,7 +5,7 @@ using System; using System.Runtime.Serialization; -namespace Microsoft.CodeAnalysis.MSBuild.Logging +namespace Microsoft.CodeAnalysis.MSBuild { [DataContract] internal class DiagnosticLogItem diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/DocumentFileInfo.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DocumentFileInfo.cs similarity index 100% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/DocumentFileInfo.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/DocumentFileInfo.cs diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IBuildHost.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IBuildHost.cs new file mode 100644 index 0000000000000..dcc0e48ef2f5d --- /dev/null +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IBuildHost.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.MSBuild; + +/// +/// RPC methods. +/// +internal interface IBuildHost +{ + bool HasUsableMSBuild(string projectOrSolutionFilePath); + ImmutableArray<(string ProjectPath, string ProjectGuid)> GetProjectsInSolution(string solutionFilePath); + Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken); + Task TryGetProjectOutputPathAsync(string projectFilePath, CancellationToken cancellationToken); + Task ShutdownAsync(); +} diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IProjectFile.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IProjectFile.cs new file mode 100644 index 0000000000000..7d93476b118ef --- /dev/null +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/IProjectFile.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.MSBuild; + +/// +/// RPC methods. +/// +internal interface IProjectFile +{ + ImmutableArray GetDiagnosticLogItems(); + string GetDocumentExtension(SourceCodeKind kind); + Task> GetProjectFileInfosAsync(CancellationToken cancellationToken); + void AddDocument(string filePath, string? logicalPath); + void RemoveDocument(string filePath); + void AddMetadataReference(string metadataReferenceIdentity, MetadataReferenceProperties properties, string? hintPath); + void RemoveMetadataReference(string shortAssemblyName, string fullAssemblyName, string filePath); + void AddProjectReference(string projectName, ProjectFileReference reference); + void RemoveProjectReference(string projectName, string projectFilePath); + void AddAnalyzerReference(string fullPath); + void RemoveAnalyzerReference(string fullPath); + void Save(); +} diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/JsonSettings.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/JsonSettings.cs similarity index 96% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/JsonSettings.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/JsonSettings.cs index a4dee42f83fcf..ee75ca9bf1ca5 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/JsonSettings.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/JsonSettings.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class JsonSettings { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MonoMSBuildDiscovery.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/MonoMSBuildDiscovery.cs similarity index 98% rename from src/Workspaces/Core/MSBuild.BuildHost/MonoMSBuildDiscovery.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/MonoMSBuildDiscovery.cs index e8f11292cb262..99a376391a654 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MonoMSBuildDiscovery.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/MonoMSBuildDiscovery.cs @@ -9,7 +9,7 @@ using System.Runtime.InteropServices; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class MonoMSBuildDiscovery { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/PackageReference.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/PackageReference.cs similarity index 74% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/PackageReference.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/PackageReference.cs index a337cfded8bfc..f637b25ff8b32 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/PackageReference.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/PackageReference.cs @@ -1,9 +1,7 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.Diagnostics; using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.MSBuild; diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileInfo.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileInfo.cs similarity index 99% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileInfo.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileInfo.cs index bd5fbd6371bf6..e5ac67ffd9721 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileInfo.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileInfo.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Diagnostics; using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MSBuild diff --git a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileReference.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileReference.cs similarity index 94% rename from src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileReference.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileReference.cs index fb8d3a216e32a..7a27a20ea17c1 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/MSBuild/ProjectFile/ProjectFileReference.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/ProjectFileReference.cs @@ -28,7 +28,7 @@ internal sealed class ProjectFileReference public ImmutableArray Aliases { get; } /// - /// The value of . + /// The value of "ReferenceOutputAssembly" metadata. /// [DataMember(Order = 2)] public bool ReferenceOutputAssembly { get; } diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Request.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Request.cs similarity index 94% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/Request.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Request.cs index 0a7d7beae83e9..4b27b8c022e8a 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Request.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Request.cs @@ -5,7 +5,7 @@ using System.Collections.Immutable; using Newtonsoft.Json.Linq; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class Request { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Response.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Response.cs similarity index 89% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/Response.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Response.cs index 2fa7d6e3efe61..ff8a604b80a02 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Response.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/Response.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json.Linq; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class Response { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/TextReaderExtensions.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/TextReaderExtensions.cs similarity index 97% rename from src/Workspaces/Core/MSBuild.BuildHost/Rpc/TextReaderExtensions.cs rename to src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/TextReaderExtensions.cs index 8778e50b619ea..b476ad5a1a105 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/TextReaderExtensions.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/Contracts/TextReaderExtensions.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal static class TextReaderExtensions { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs index 3390c68f5f450..a6a08b8ed3050 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs +++ b/src/Workspaces/Core/MSBuild.BuildHost/Rpc/RpcServer.cs @@ -13,7 +13,7 @@ using Newtonsoft.Json.Linq; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; /// /// Implements the server side of the RPC channel used to communicate with the build host. diff --git a/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs b/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs index 0bdbcd428bba0..928ff407b8b67 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/BuildHostProcessManager.cs @@ -14,8 +14,6 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; using Microsoft.Extensions.Logging; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs b/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs index b32d95b27ad91..450024049fb33 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/DiagnosticReporter.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.MSBuild.Logging; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MSBuild diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs index 19c234bb6c285..8a89561728484 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildProjectLoader.Worker.cs @@ -14,7 +14,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs index 35e8f99bf2704..92b03542374ce 100644 --- a/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs +++ b/src/Workspaces/Core/MSBuild/MSBuild/MSBuildWorkspace.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.MSBuild.Rpc; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj index 6fb4366c78c83..9b3e73b619771 100644 --- a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj +++ b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj @@ -24,10 +24,7 @@ - - - + @@ -38,7 +35,7 @@ for it. PrivateAssets="all" is needed to prevent this reference from becoming a package reference in the package, as a workaround for https://github.com/NuGet/Home/issues/3891. --> - + true @@ -50,12 +47,7 @@ InternalUtilities\GlobalAssemblyCache.cs - - - - - - + @@ -63,6 +55,12 @@ + + + + + + diff --git a/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs b/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs index 808d2885e646a..5a457f05da7e8 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RemoteBuildHost.cs @@ -6,9 +6,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RemoteBuildHost { @@ -23,22 +22,22 @@ public RemoteBuildHost(RpcClient client) } public Task HasUsableMSBuildAsync(string projectOrSolutionFilePath, CancellationToken cancellationToken) - => _client.InvokeAsync(BuildHostTargetObject, nameof(BuildHost.HasUsableMSBuild), parameters: [projectOrSolutionFilePath], cancellationToken); + => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.HasUsableMSBuild), parameters: [projectOrSolutionFilePath], cancellationToken); public Task> GetProjectsInSolutionAsync(string solutionFilePath, CancellationToken cancellationToken) - => _client.InvokeAsync>(BuildHostTargetObject, nameof(BuildHost.GetProjectsInSolution), parameters: [solutionFilePath], cancellationToken); + => _client.InvokeAsync>(BuildHostTargetObject, nameof(IBuildHost.GetProjectsInSolution), parameters: [solutionFilePath], cancellationToken); public async Task LoadProjectFileAsync(string projectFilePath, string languageName, CancellationToken cancellationToken) { - var remoteProjectFileTargetObject = await _client.InvokeAsync(BuildHostTargetObject, nameof(BuildHost.LoadProjectFileAsync), parameters: [projectFilePath, languageName], cancellationToken).ConfigureAwait(false); + var remoteProjectFileTargetObject = await _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.LoadProjectFileAsync), parameters: [projectFilePath, languageName], cancellationToken).ConfigureAwait(false); return new RemoteProjectFile(_client, remoteProjectFileTargetObject); } public Task TryGetProjectOutputPathAsync(string projectFilePath, CancellationToken cancellationToken) - => _client.InvokeNullableAsync(BuildHostTargetObject, nameof(BuildHost.TryGetProjectOutputPathAsync), parameters: [projectFilePath], cancellationToken); + => _client.InvokeNullableAsync(BuildHostTargetObject, nameof(IBuildHost.TryGetProjectOutputPathAsync), parameters: [projectFilePath], cancellationToken); public Task ShutdownAsync(CancellationToken cancellationToken) - => _client.InvokeAsync(BuildHostTargetObject, nameof(BuildHost.ShutdownAsync), parameters: [], cancellationToken); + => _client.InvokeAsync(BuildHostTargetObject, nameof(IBuildHost.ShutdownAsync), parameters: [], cancellationToken); } diff --git a/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs b/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs index 0e3d803737ceb..61fde83b8a63c 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RemoteInvocationException.cs @@ -4,7 +4,7 @@ using System; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RemoteInvocationException : Exception { diff --git a/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs b/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs index a2ce680237812..59b91fbb346c2 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RemoteProjectFile.cs @@ -5,10 +5,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.CodeAnalysis.MSBuild.Logging; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; internal sealed class RemoteProjectFile { @@ -22,38 +20,38 @@ public RemoteProjectFile(RpcClient client, int remoteProjectFileTargetObject) } public Task> GetDiagnosticLogItemsAsync(CancellationToken cancellationToken) - => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(ProjectFile.GetDiagnosticLogItems), parameters: [], cancellationToken); + => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(IProjectFile.GetDiagnosticLogItems), parameters: [], cancellationToken); public Task GetDocumentExtensionAsync(SourceCodeKind sourceCodeKind, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.GetDocumentExtension), parameters: [sourceCodeKind], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.GetDocumentExtension), parameters: [sourceCodeKind], cancellationToken); public Task> GetProjectFileInfosAsync(CancellationToken cancellationToken) - => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(ProjectFile.GetProjectFileInfosAsync), parameters: [], cancellationToken); + => _client.InvokeAsync>(_remoteProjectFileTargetObject, nameof(IProjectFile.GetProjectFileInfosAsync), parameters: [], cancellationToken); public Task AddDocumentAsync(string filePath, string? logicalPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddDocument), parameters: [filePath, logicalPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddDocument), parameters: [filePath, logicalPath], cancellationToken); public Task RemoveDocumentAsync(string filePath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveDocument), parameters: [filePath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveDocument), parameters: [filePath], cancellationToken); public Task AddMetadataReferenceAsync(string metadataReferenceIdentity, MetadataReferenceProperties properties, string? hintPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddMetadataReference), parameters: [metadataReferenceIdentity, properties, hintPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddMetadataReference), parameters: [metadataReferenceIdentity, properties, hintPath], cancellationToken); public Task RemoveMetadataReferenceAsync(string shortAssemblyName, string fullAssemblyName, string filePath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveMetadataReference), parameters: [shortAssemblyName, fullAssemblyName, filePath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveMetadataReference), parameters: [shortAssemblyName, fullAssemblyName, filePath], cancellationToken); public Task AddProjectReferenceAsync(string projectName, ProjectFileReference projectFileReference, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddProjectReference), parameters: [projectName, projectFileReference], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddProjectReference), parameters: [projectName, projectFileReference], cancellationToken); public Task RemoveProjectReferenceAsync(string projectName, string projectFilePath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveProjectReference), parameters: [projectName, projectFilePath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveProjectReference), parameters: [projectName, projectFilePath], cancellationToken); public Task AddAnalyzerReferenceAsync(string fullPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.AddAnalyzerReference), parameters: [fullPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.AddAnalyzerReference), parameters: [fullPath], cancellationToken); public Task RemoveAnalyzerReferenceAsync(string fullPath, CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.RemoveAnalyzerReference), parameters: [fullPath], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.RemoveAnalyzerReference), parameters: [fullPath], cancellationToken); public Task SaveAsync(CancellationToken cancellationToken) - => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(ProjectFile.Save), parameters: [], cancellationToken); + => _client.InvokeAsync(_remoteProjectFileTargetObject, nameof(IProjectFile.Save), parameters: [], cancellationToken); } diff --git a/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs b/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs index 76873433c4c38..83d1a7b43c068 100644 --- a/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs +++ b/src/Workspaces/Core/MSBuild/Rpc/RpcClient.cs @@ -13,10 +13,10 @@ using Newtonsoft.Json.Linq; using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.MSBuild.Rpc; +namespace Microsoft.CodeAnalysis.MSBuild; /// -/// Implements the client side of the RPC channel used to communicate with the build host, which is using . +/// Implements the client side of the RPC channel used to communicate with the build host, which is using RpcServer. /// /// /// The RPC system implemented here is pretty close to something like JSON-RPC; however since we need the Build Host to be usable in Source Build diff --git a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj index 0908804fdba4a..fb8224323dc55 100644 --- a/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj +++ b/src/Workspaces/MSBuildTest/Microsoft.CodeAnalysis.Workspaces.MSBuild.UnitTests.csproj @@ -7,29 +7,16 @@ $(NetRoslyn);net472 - - - - - - - - - - - - - - + diff --git a/src/Workspaces/MSBuildTest/NetCoreTests.cs b/src/Workspaces/MSBuildTest/NetCoreTests.cs index d58776c25f9d9..1af40888ed5e5 100644 --- a/src/Workspaces/MSBuildTest/NetCoreTests.cs +++ b/src/Workspaces/MSBuildTest/NetCoreTests.cs @@ -6,15 +6,11 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.UnitTests; using Microsoft.CodeAnalysis.UnitTests.TestFiles; using Microsoft.CodeAnalysis.VisualBasic; using Roslyn.Test.Utilities; @@ -443,7 +439,7 @@ public async Task TestOpenProject_OverrideTFM() DotNetRestore(@"Library\Library.csproj"); // Override the TFM properties defined in the file - using (var workspace = CreateMSBuildWorkspace((PropertyNames.TargetFramework, ""), (PropertyNames.TargetFrameworks, "net6;net5"))) + using (var workspace = CreateMSBuildWorkspace(("TargetFramework", ""), ("TargetFrameworks", "net6;net5"))) { await workspace.OpenProjectAsync(projectFilePath); diff --git a/src/Workspaces/MSBuildTest/RpcTests.cs b/src/Workspaces/MSBuildTest/RpcTests.cs index ef05081a1ddd9..fd0e2a55f18ae 100644 --- a/src/Workspaces/MSBuildTest/RpcTests.cs +++ b/src/Workspaces/MSBuildTest/RpcTests.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.MSBuild.Rpc; -using Microsoft.VisualStudio.Telemetry; using Nerdbank.Streams; using Xunit; diff --git a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs index 230b95db8ef1b..ea9cf6ad02c76 100644 --- a/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuildTest/VisualStudioMSBuildWorkspaceTests.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.MSBuild.Build; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; @@ -3116,7 +3115,6 @@ public async Task TestOpenProject_CommandLineArgsHaveNoErrors() CreateFiles(GetSimpleCSharpSolutionFiles()); using var workspace = CreateMSBuildWorkspace(); - var loader = new CSharpProjectFileLoader(); var projectFilePath = GetSolutionFileName(@"CSharpProject\CSharpProject.csproj"); @@ -3127,7 +3125,7 @@ public async Task TestOpenProject_CommandLineArgsHaveNoErrors() var projectFileInfo = (await projectFile.GetProjectFileInfosAsync(CancellationToken.None)).Single(); var commandLineParser = workspace.Services - .GetLanguageServices(loader.Language) + .GetLanguageServices(LanguageNames.CSharp) .GetRequiredService(); var projectDirectory = Path.GetDirectoryName(projectFilePath); From 877cd52f0622d5e9bed4174220fbc1ab86415f7c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 16:46:58 -0700 Subject: [PATCH 187/423] Share code --- .../Solution/SolutionCompilationState.cs | 52 +++++++------------ 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 5d9f6b3dd4961..064de29697353 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -714,6 +714,19 @@ public SolutionCompilationState WithDocumentText(DocumentId documentId, SourceTe internal SolutionCompilationState WithDocumentTexts( ImmutableArray<(DocumentId documentId, SourceText text, PreservationMode mode)> texts) + { + return WithDocumentContents( + texts, IsUnchanged, + static (documentState, text, mode) => documentState.UpdateText(text, mode)); + + static bool IsUnchanged(DocumentState oldDocument, SourceText text) + => oldDocument.TryGetText(out var oldText) && text == oldText; + } + + private SolutionCompilationState WithDocumentContents( + ImmutableArray<(DocumentId documentId, TContent content, PreservationMode mode)> texts, + Func isUnchanged, + Func updateContent) { return UpdateDocumentsInMultipleProjects( texts.GroupBy(d => d.documentId.ProjectId).Select(g => @@ -722,13 +735,13 @@ internal SolutionCompilationState WithDocumentTexts( var projectState = this.SolutionState.GetRequiredProjectState(projectId); using var _ = ArrayBuilder.GetInstance(out var newDocumentStates); - foreach (var (documentId, text, mode) in g) + foreach (var (documentId, content, mode) in g) { var documentState = projectState.DocumentStates.GetRequiredState(documentId); - if (IsUnchanged(documentState, text)) + if (isUnchanged(documentState, content)) continue; - newDocumentStates.Add(documentState.UpdateText(text, mode)); + newDocumentStates.Add(updateContent(documentState, content, mode)); } return (projectId, newDocumentStates.ToImmutableAndClear()); @@ -740,11 +753,6 @@ internal SolutionCompilationState WithDocumentTexts( projectState.UpdateDocuments(newDocumentStates, contentChanged: true), newDocumentStates); }); - - static bool IsUnchanged(DocumentState oldDocument, SourceText text) - { - return oldDocument.TryGetText(out var oldText) && text == oldText; - } } public SolutionCompilationState WithDocumentState( @@ -796,31 +804,9 @@ public SolutionCompilationState WithAnalyzerConfigDocumentText( /// public SolutionCompilationState WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root, PreservationMode mode)> syntaxRoots) { - return UpdateDocumentsInMultipleProjects( - syntaxRoots.GroupBy(d => d.documentId.ProjectId).Select(g => - { - var projectId = g.Key; - var projectState = this.SolutionState.GetRequiredProjectState(projectId); - - using var _ = ArrayBuilder.GetInstance(out var newDocumentStates); - foreach (var (documentId, root, mode) in g) - { - var documentState = projectState.DocumentStates.GetRequiredState(documentId); - if (IsUnchanged(documentState, root)) - continue; - - newDocumentStates.Add(documentState.UpdateTree(root, mode)); - } - - return (projectId, newDocumentStates.ToImmutableAndClear()); - }), - static (projectState, newDocumentStates) => - { - return TranslationAction.TouchDocumentsAction.Create( - projectState, - projectState.UpdateDocuments(newDocumentStates, contentChanged: true), - newDocumentStates); - }); + return WithDocumentContents( + syntaxRoots, IsUnchanged, + static (documentState, root, mode) => documentState.UpdateTree(root, mode)); static bool IsUnchanged(DocumentState oldDocument, SyntaxNode root) { From 40e0a1ca787d5b7bc4d17916d1a999998fd2885e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 16:58:34 -0700 Subject: [PATCH 188/423] use new helper --- .../AbstractChangeNamespaceService.cs | 70 ++++++++----------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index 7825177bc0424..db4bceeeeafb3 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -24,6 +24,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -487,29 +488,25 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n var refLocationGroups = refLocationsInSolution.GroupBy(loc => loc.Document.Id); - var fixedDocuments = await Task.WhenAll(refLocationGroups.Select(async refInOneDocument => - { - var result = await FixReferencingDocumentAsync( - solutionWithChangedNamespace.GetRequiredDocument(refInOneDocument.Key), - refInOneDocument, - newNamespace, - fallbackOptions, - cancellationToken).ConfigureAwait(false); - return (result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); - })).ConfigureAwait(false); - - var solutionWithFixedReferences = MergeDocumentChanges(solutionWithChangedNamespace, fixedDocuments); + var fixedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode preservationMode)>.RunParallelAsync( + source: refLocationGroups, + produceItems: static async (refInOneDocument, callback, args, cancellationToken) => + { + var result = await FixReferencingDocumentAsync( + args.solutionWithChangedNamespace.GetRequiredDocument(refInOneDocument.Key), + refInOneDocument, + args.newNamespace, + args.fallbackOptions, + cancellationToken).ConfigureAwait(false); + callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false), PreservationMode.PreserveValue)); + }, + args: (solutionWithChangedNamespace, newNamespace, fallbackOptions), + cancellationToken).ConfigureAwait(false); + var solutionWithFixedReferences = solutionWithChangedNamespace.WithDocumentSyntaxRoots(fixedDocuments); return (solutionWithFixedReferences, refLocationGroups.SelectAsArray(g => g.Key)); } - private static Solution MergeDocumentChanges( - Solution originalSolution, (DocumentId documentId, SyntaxNode newRoot)[] changedDocuments) - { - return originalSolution.WithDocumentSyntaxRoots( - changedDocuments.SelectAsArray(t => (t.documentId, t.newRoot, PreservationMode.PreserveValue))); - } - private readonly struct LocationForAffectedSymbol(ReferenceLocation location, bool isReferenceToExtensionMethod) { public ReferenceLocation ReferenceLocation { get; } = location; @@ -770,44 +767,39 @@ private static async Task RemoveUnnecessaryImportsAsync( CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) { - using var _ = PooledHashSet.GetInstance(out var linkedDocumentsToSkip); - var documentsToProcessBuilder = ArrayBuilder.GetInstance(); + using var _1 = PooledHashSet.GetInstance(out var linkedDocumentsToSkip); + using var _2 = ArrayBuilder.GetInstance(out var documentsToProcess); foreach (var id in ids) { if (linkedDocumentsToSkip.Contains(id)) - { continue; - } var document = solution.GetRequiredDocument(id); linkedDocumentsToSkip.AddRange(document.GetLinkedDocumentIds()); - documentsToProcessBuilder.Add(document); - - document = await RemoveUnnecessaryImportsWorkerAsync( - document, - CreateImports(document, names, withFormatterAnnotation: false), - cancellationToken).ConfigureAwait(false); - solution = document.Project.Solution; + documentsToProcess.Add(document); } - var documentsToProcess = documentsToProcessBuilder.ToImmutableAndFree(); - - var changeDocuments = await Task.WhenAll(documentsToProcess.Select( - async doc => + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode preservationMode)>.RunParallelAsync( + source: documentsToProcess, + produceItems: static async (doc, callback, args, cancellationToken) => { var result = await RemoveUnnecessaryImportsWorkerAsync( doc, - CreateImports(doc, names, withFormatterAnnotation: false), + CreateImports(doc, args.names, withFormatterAnnotation: false), + args.fallbackOptions, cancellationToken).ConfigureAwait(false); - return (result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); - })).ConfigureAwait(false); + callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false), PreservationMode.PreserveValue)); + }, + args: (names, fallbackOptions), + cancellationToken).ConfigureAwait(false); - return MergeDocumentChanges(solution, changeDocuments); + return solution.WithDocumentSyntaxRoots(changedDocuments); - async Task RemoveUnnecessaryImportsWorkerAsync( + async static Task RemoveUnnecessaryImportsWorkerAsync( Document doc, IEnumerable importsToRemove, + CodeCleanupOptionsProvider fallbackOptions, CancellationToken token) { var removeImportService = doc.GetRequiredLanguageService(); From d83aace24563e7b7bff42ae4340519867552c5b1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:04:39 -0700 Subject: [PATCH 189/423] use new helper --- .../AbstractChangeSignatureService.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index 99322262f53c0..d34fe3b1d562d 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -24,6 +24,7 @@ using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -414,17 +415,23 @@ private static async Task> FindChangeSignatureR } // Update the documents using the updated syntax trees - foreach (var docId in nodesToUpdate.Keys) - { - var updatedDoc = currentSolution.GetRequiredDocument(docId).WithSyntaxRoot(updatedRoots[docId]); - var cleanupOptions = await updatedDoc.GetCodeCleanupOptionsAsync(context.FallbackOptions, cancellationToken).ConfigureAwait(false); + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + source: nodesToUpdate.Keys, + produceItems: static async (docId, callback, args, cancellationToken) => + { + var updatedDoc = args.currentSolution.GetRequiredDocument(docId).WithSyntaxRoot(args.updatedRoots[docId]); + var cleanupOptions = await updatedDoc.GetCodeCleanupOptionsAsync(args.context.FallbackOptions, cancellationToken).ConfigureAwait(false); - var docWithImports = await ImportAdder.AddImportsFromSymbolAnnotationAsync(updatedDoc, cleanupOptions.AddImportOptions, cancellationToken).ConfigureAwait(false); - var reducedDoc = await Simplifier.ReduceAsync(docWithImports, Simplifier.Annotation, cleanupOptions.SimplifierOptions, cancellationToken: cancellationToken).ConfigureAwait(false); - var formattedDoc = await Formatter.FormatAsync(reducedDoc, SyntaxAnnotation.ElasticAnnotation, cleanupOptions.FormattingOptions, cancellationToken).ConfigureAwait(false); + var docWithImports = await ImportAdder.AddImportsFromSymbolAnnotationAsync(updatedDoc, cleanupOptions.AddImportOptions, cancellationToken).ConfigureAwait(false); + var reducedDoc = await Simplifier.ReduceAsync(docWithImports, Simplifier.Annotation, cleanupOptions.SimplifierOptions, cancellationToken: cancellationToken).ConfigureAwait(false); + var formattedDoc = await Formatter.FormatAsync(reducedDoc, SyntaxAnnotation.ElasticAnnotation, cleanupOptions.FormattingOptions, cancellationToken).ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentSyntaxRoot(docId, (await formattedDoc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false))!); - } + callback((formattedDoc.Id, await formattedDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false), PreservationMode.PreserveValue)); + }, + args: (currentSolution, updatedRoots, context), + cancellationToken).ConfigureAwait(false); + + currentSolution = currentSolution.WithDocumentSyntaxRoots(changedDocuments); telemetryTimer.Stop(); ChangeSignatureLogger.LogCommitInformation(telemetryNumberOfDeclarationsToUpdate, telemetryNumberOfReferencesToUpdate, telemetryTimer.Elapsed); From 293671a85f62e79081bfc9f1f08ec98807bfa8cf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:10:22 -0700 Subject: [PATCH 190/423] use new helper --- ...troduceParameterCodeRefactoringProvider.cs | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs index 9ad70f7081ced..45266894293ef 100644 --- a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.IntroduceParameter; @@ -241,17 +242,26 @@ private async Task IntroduceParameterAsync(Document originalDocument, var rewriter = new IntroduceParameterDocumentRewriter(this, originalDocument, expression, methodSymbol, containingMethod, selectedCodeAction, fallbackOptions, allOccurrences); - foreach (var (project, projectCallSites) in methodCallSites.GroupBy(kvp => kvp.Key.Project)) - { - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - foreach (var (document, invocations) in projectCallSites) + var changedRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + source: methodCallSites.GroupBy(kvp => kvp.Key.Project), + produceItems: static async (tuple, callback, rewriter, cancellationToken) => { - var newRoot = await rewriter.RewriteDocumentAsync(compilation, document, invocations, cancellationToken).ConfigureAwait(false); - modifiedSolution = modifiedSolution.WithDocumentSyntaxRoot(document.Id, newRoot); - } - } - - return modifiedSolution; + var (project, projectCallSites) = tuple; + var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + await RoslynParallel.ForEachAsync( + projectCallSites, + cancellationToken, + async (tuple, cancellationToken) => + { + var (document, invocations) = tuple; + var newRoot = await rewriter.RewriteDocumentAsync(compilation, document, invocations, cancellationToken).ConfigureAwait(false); + callback((document.Id, newRoot, PreservationMode.PreserveValue)); + }).ConfigureAwait(false); + }, + args: rewriter, + cancellationToken).ConfigureAwait(false); + + return modifiedSolution.WithDocumentSyntaxRoots(changedRoots); } /// From f0cae2cfc62026d0f0f8ee6b057df7157086a065 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:13:01 -0700 Subject: [PATCH 191/423] use new helper --- ...vertTupleToStructCodeRefactoringService.cs | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs index d7cdc206882f5..a0933292be07e 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -79,18 +80,22 @@ private static async Task CleanupAsync(Solution oldSolution, Solution var changes = newSolution.GetChangedDocuments(oldSolution); var final = newSolution; - foreach (var docId in changes) - { - var document = newSolution.GetRequiredDocument(docId); + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + source: changes, + produceItems: static async (docId, callback, args, cancellationToken) => + { + var document = args.newSolution.GetRequiredDocument(docId); - var options = await document.GetCodeCleanupOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); - var cleaned = await CodeAction.CleanupDocumentAsync(document, options, cancellationToken).ConfigureAwait(false); + var options = await document.GetCodeCleanupOptionsAsync(args.fallbackOptions, cancellationToken).ConfigureAwait(false); + var cleaned = await CodeAction.CleanupDocumentAsync(document, options, cancellationToken).ConfigureAwait(false); - var cleanedRoot = await cleaned.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - final = final.WithDocumentSyntaxRoot(docId, cleanedRoot); - } + var cleanedRoot = await cleaned.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + callback((docId, cleanedRoot, PreservationMode.PreserveValue)); + }, + args: (newSolution, fallbackOptions), + cancellationToken).ConfigureAwait(false); - return final; + return newSolution.WithDocumentSyntaxRoots(changedDocuments); } } } From c07e1c7d1a7967f35674b0e8d8d9fa9ce1f0e4d8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:26:19 -0700 Subject: [PATCH 192/423] simplify --- .../AbstractMakeMethodSynchronousCodeFixProvider.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index cbecadb7094d4..b64d63941b497 100644 --- a/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -177,18 +177,14 @@ private static async Task RemoveAwaitFromCallersAsync( var editor = new SyntaxEditor(root, currentSolution.Services); foreach (var location in group) - { - RemoveAwaitFromCallerIfPresent(editor, syntaxFactsService, root, location, cancellationToken); - } + RemoveAwaitFromCallerIfPresent(editor, syntaxFactsService, location, cancellationToken); var newRoot = editor.GetChangedRoot(); return currentSolution.WithDocumentSyntaxRoot(document.Id, newRoot); } private static void RemoveAwaitFromCallerIfPresent( - SyntaxEditor editor, ISyntaxFactsService syntaxFacts, - SyntaxNode root, ReferenceLocation referenceLocation, - CancellationToken cancellationToken) + SyntaxEditor editor, ISyntaxFactsService syntaxFacts, ReferenceLocation referenceLocation, CancellationToken cancellationToken) { if (referenceLocation.IsImplicit) { From f8b5f1529936d7d51e3c03b32d28ab2e90accd6f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:32:26 -0700 Subject: [PATCH 193/423] simplify --- .../EnableNullableCodeRefactoringProvider.cs | 4 ++-- .../ChangeSignature/AbstractChangeSignatureService.cs | 4 ++-- .../SyncNamespace/AbstractChangeNamespaceService.cs | 8 ++++---- .../AbstractIntroduceParameterCodeRefactoringProvider.cs | 4 ++-- .../Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs | 2 +- .../Core/Portable/CodeActions/CodeAction_Cleanup.cs | 2 +- .../Core/Portable/Workspace/Solution/Solution.cs | 4 ++++ .../Workspace/Solution/SolutionCompilationState.cs | 2 +- .../RemoteConvertTupleToStructCodeRefactoringService.cs | 4 ++-- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs index e19a2b6c70dbc..03b08066988bc 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/EnableNullable/EnableNullableCodeRefactoringProvider.cs @@ -68,7 +68,7 @@ private static async Task EnableNullableReferenceTypesAsync( Project project, CodeActionPurpose purpose, CodeActionOptionsProvider fallbackOptions, IProgress _, CancellationToken cancellationToken) { var solution = project.Solution; - var updatedDocumentRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + var updatedDocumentRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: project.Documents, produceItems: static async (document, callback, fallbackOptions, cancellationToken) => { @@ -77,7 +77,7 @@ private static async Task EnableNullableReferenceTypesAsync( var updatedDocumentRoot = await EnableNullableReferenceTypesAsync( document, fallbackOptions, cancellationToken).ConfigureAwait(false); - callback((document.Id, updatedDocumentRoot, PreservationMode.PreserveValue)); + callback((document.Id, updatedDocumentRoot)); }, args: fallbackOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index d34fe3b1d562d..260baa4eb4047 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -415,7 +415,7 @@ private static async Task> FindChangeSignatureR } // Update the documents using the updated syntax trees - var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: nodesToUpdate.Keys, produceItems: static async (docId, callback, args, cancellationToken) => { @@ -426,7 +426,7 @@ private static async Task> FindChangeSignatureR var reducedDoc = await Simplifier.ReduceAsync(docWithImports, Simplifier.Annotation, cleanupOptions.SimplifierOptions, cancellationToken: cancellationToken).ConfigureAwait(false); var formattedDoc = await Formatter.FormatAsync(reducedDoc, SyntaxAnnotation.ElasticAnnotation, cleanupOptions.FormattingOptions, cancellationToken).ConfigureAwait(false); - callback((formattedDoc.Id, await formattedDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false), PreservationMode.PreserveValue)); + callback((formattedDoc.Id, await formattedDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); }, args: (currentSolution, updatedRoots, context), cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index db4bceeeeafb3..9630a575205ff 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -488,7 +488,7 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n var refLocationGroups = refLocationsInSolution.GroupBy(loc => loc.Document.Id); - var fixedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode preservationMode)>.RunParallelAsync( + var fixedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: refLocationGroups, produceItems: static async (refInOneDocument, callback, args, cancellationToken) => { @@ -498,7 +498,7 @@ private static SyntaxNode CreateImport(SyntaxGenerator syntaxGenerator, string n args.newNamespace, args.fallbackOptions, cancellationToken).ConfigureAwait(false); - callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false), PreservationMode.PreserveValue)); + callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); }, args: (solutionWithChangedNamespace, newNamespace, fallbackOptions), cancellationToken).ConfigureAwait(false); @@ -780,7 +780,7 @@ private static async Task RemoveUnnecessaryImportsAsync( documentsToProcess.Add(document); } - var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode preservationMode)>.RunParallelAsync( + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: documentsToProcess, produceItems: static async (doc, callback, args, cancellationToken) => { @@ -789,7 +789,7 @@ private static async Task RemoveUnnecessaryImportsAsync( CreateImports(doc, args.names, withFormatterAnnotation: false), args.fallbackOptions, cancellationToken).ConfigureAwait(false); - callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false), PreservationMode.PreserveValue)); + callback((result.Id, await result.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false))); }, args: (names, fallbackOptions), cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs index 45266894293ef..03f44f9dc54bd 100644 --- a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs @@ -242,7 +242,7 @@ private async Task IntroduceParameterAsync(Document originalDocument, var rewriter = new IntroduceParameterDocumentRewriter(this, originalDocument, expression, methodSymbol, containingMethod, selectedCodeAction, fallbackOptions, allOccurrences); - var changedRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + var changedRoots = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: methodCallSites.GroupBy(kvp => kvp.Key.Project), produceItems: static async (tuple, callback, rewriter, cancellationToken) => { @@ -255,7 +255,7 @@ await RoslynParallel.ForEachAsync( { var (document, invocations) = tuple; var newRoot = await rewriter.RewriteDocumentAsync(compilation, document, invocations, cancellationToken).ConfigureAwait(false); - callback((document.Id, newRoot, PreservationMode.PreserveValue)); + callback((document.Id, newRoot)); }).ConfigureAwait(false); }, args: rewriter, diff --git a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs index 7ea407ef0afca..3f422f9f713b9 100644 --- a/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs +++ b/src/VisualStudio/Core/Def/CodeCleanup/AbstractCodeCleanUpFixer.cs @@ -249,7 +249,7 @@ await RoslynParallel.ForEachAsync( args: (globalOptions, solution, enabledFixIds, progressTracker), cancellationToken).ConfigureAwait(false); - return solution.WithDocumentSyntaxRoots(changedRoots.SelectAsArray(t => (t.documentId, t.newRoot, PreservationMode.PreserveValue))); + return solution.WithDocumentSyntaxRoots(changedRoots); } private static async Task FixDocumentAsync( diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.cs index 33e3ebc44bbcc..0e1a0cebd1f9f 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction_Cleanup.cs @@ -168,7 +168,7 @@ async Task RunParallelCleanupPassAsync( cancellationToken).ConfigureAwait(false); // Grab all the cleaned roots and produce the new solution snapshot from that. - return solution.WithDocumentSyntaxRoots(changedRoots.SelectAsArray(t => (t.documentId, t.newRoot, PreservationMode.PreserveValue))); + return solution.WithDocumentSyntaxRoots(changedRoots); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 5d07e98103010..9eb60f0a85dbb 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1311,6 +1311,10 @@ public Solution WithAnalyzerConfigDocumentText(DocumentId documentId, TextAndVer public Solution WithDocumentSyntaxRoot(DocumentId documentId, SyntaxNode root, PreservationMode mode = PreservationMode.PreserveValue) => WithDocumentSyntaxRoots([(documentId, root, mode)]); + /// . + internal Solution WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root)> syntaxRoots) + => WithDocumentSyntaxRoots(syntaxRoots.SelectAsArray(t => (t.documentId, t.root, PreservationMode.PreserveValue))); + /// . internal Solution WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root, PreservationMode mode)> syntaxRoots) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 064de29697353..fa242b07820b4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -801,7 +801,7 @@ public SolutionCompilationState WithAnalyzerConfigDocumentText( this.SolutionState.WithAnalyzerConfigDocumentText(documentId, textAndVersion, mode)); } - /// + /// public SolutionCompilationState WithDocumentSyntaxRoots(ImmutableArray<(DocumentId documentId, SyntaxNode root, PreservationMode mode)> syntaxRoots) { return WithDocumentContents( diff --git a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs index a0933292be07e..c1d32bb09e5f3 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs @@ -80,7 +80,7 @@ private static async Task CleanupAsync(Solution oldSolution, Solution var changes = newSolution.GetChangedDocuments(oldSolution); var final = newSolution; - var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot, PreservationMode mode)>.RunParallelAsync( + var changedDocuments = await ProducerConsumer<(DocumentId documentId, SyntaxNode newRoot)>.RunParallelAsync( source: changes, produceItems: static async (docId, callback, args, cancellationToken) => { @@ -90,7 +90,7 @@ private static async Task CleanupAsync(Solution oldSolution, Solution var cleaned = await CodeAction.CleanupDocumentAsync(document, options, cancellationToken).ConfigureAwait(false); var cleanedRoot = await cleaned.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - callback((docId, cleanedRoot, PreservationMode.PreserveValue)); + callback((docId, cleanedRoot)); }, args: (newSolution, fallbackOptions), cancellationToken).ConfigureAwait(false); From 1ef30bd0055674a7975a068d3f05b5caa8a1936c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:35:15 -0700 Subject: [PATCH 194/423] use helper --- .../MoveToNamespace/AbstractMoveToNamespaceService.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs index 0c45abfa34f00..b92b83f040385 100644 --- a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs @@ -288,12 +288,8 @@ private static async Task PropagateChangeToLinkedDocumentsAsync(Docume var formattedText = await formattedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var solution = formattedDocument.Project.Solution; - foreach (var documentId in formattedDocument.GetLinkedDocumentIds()) - { - solution = solution.WithDocumentText(documentId, formattedText); - } - - return solution; + solution = solution.WithDocumentTexts( + formattedDocument.GetLinkedDocumentIds().SelectAsArray(id => (id, formattedText, PreservationMode.PreserveValue))); } private static string GetNewSymbolName(ISymbol symbol, string targetNamespace) From 26231b92e36c3f7fbc693b548f3bc6d1c64c96c2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:35:32 -0700 Subject: [PATCH 195/423] use helper --- .../Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs index b92b83f040385..f277e94b22930 100644 --- a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs @@ -288,8 +288,9 @@ private static async Task PropagateChangeToLinkedDocumentsAsync(Docume var formattedText = await formattedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var solution = formattedDocument.Project.Solution; - solution = solution.WithDocumentTexts( + var finalSolution = solution.WithDocumentTexts( formattedDocument.GetLinkedDocumentIds().SelectAsArray(id => (id, formattedText, PreservationMode.PreserveValue))); + return finalSolution; } private static string GetNewSymbolName(ISymbol symbol, string targetNamespace) From 3f861040b572cd48f28fc6104760a35c98dee34f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:37:41 -0700 Subject: [PATCH 196/423] helper --- ...enameTrackingTaggerProvider.RenameTrackingCommitter.cs | 8 +++----- .../MoveToNamespace/AbstractMoveToNamespaceService.cs | 2 +- .../Core/Portable/Workspace/Solution/Solution.cs | 3 +++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs index dcc3fe3db63ed..1168c4eced621 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingCommitter.cs @@ -196,12 +196,10 @@ private async Task CreateSolutionWithOriginalNameAsync( // Apply the original name to all linked documents to construct a consistent solution var solution = document.Project.Solution; - foreach (var documentId in document.GetLinkedDocumentIds().Add(document.Id)) - { - solution = solution.WithDocumentText(documentId, newFullText); - } + var finalSolution = solution.WithDocumentTexts( + document.GetLinkedDocumentIds().Add(document.Id).SelectAsArray(id => (id, newFullText))); - return solution; + return finalSolution; } private async Task TryGetSymbolAsync(Solution solutionWithOriginalName, DocumentId documentId, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs index f277e94b22930..a967f30ae4c55 100644 --- a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs @@ -289,7 +289,7 @@ private static async Task PropagateChangeToLinkedDocumentsAsync(Docume var solution = formattedDocument.Project.Solution; var finalSolution = solution.WithDocumentTexts( - formattedDocument.GetLinkedDocumentIds().SelectAsArray(id => (id, formattedText, PreservationMode.PreserveValue))); + formattedDocument.GetLinkedDocumentIds().SelectAsArray(id => (id, formattedText))); return finalSolution; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 9eb60f0a85dbb..978f704c4154c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1183,6 +1183,9 @@ public Solution WithDocumentFilePath(DocumentId documentId, string filePath) public Solution WithDocumentText(DocumentId documentId, SourceText text, PreservationMode mode = PreservationMode.PreserveValue) => WithDocumentTexts([(documentId, text, mode)]); + internal Solution WithDocumentTexts(ImmutableArray<(DocumentId documentId, SourceText text)> texts) + => WithDocumentTexts(texts.SelectAsArray(t => (t.documentId, t.text, PreservationMode.PreserveValue))); + internal Solution WithDocumentTexts(ImmutableArray<(DocumentId documentId, SourceText text, PreservationMode mode)> texts) { foreach (var (documentId, text, mode) in texts) From d1260da369a46a043b4094d070cb54e7a9936f4c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:40:06 -0700 Subject: [PATCH 197/423] use helper --- .../AbstractSuppressionBatchFixAllProvider.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs index 7fb1b6b5b54ab..78d6303864837 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs @@ -240,11 +240,8 @@ private static async Task TryMergeFixesAsync( // Finally, apply the changes to each document to the solution, producing the // new solution. - var currentSolution = oldSolution; - foreach (var (documentId, finalText) in documentIdToFinalText) - currentSolution = currentSolution.WithDocumentText(documentId, finalText); - - return currentSolution; + var finalSolution = oldSolution.WithDocumentTexts(documentIdToFinalText); + return finalSolution; } private static async Task>> GetDocumentIdToChangedDocumentsAsync( @@ -269,7 +266,7 @@ private static async Task TryMergeFixesAsync( return documentIdToChangedDocuments; } - private static async Task> GetDocumentIdToFinalTextAsync( + private static async Task> GetDocumentIdToFinalTextAsync( Solution oldSolution, IReadOnlyDictionary> documentIdToChangedDocuments, ImmutableArray<(Diagnostic diagnostic, CodeAction action)> diagnosticsAndCodeActions, @@ -291,7 +288,7 @@ private static async Task> GetDocume } await Task.WhenAll(getFinalDocumentTasks).ConfigureAwait(false); - return documentIdToFinalText; + return documentIdToFinalText.SelectAsArray(kvp => (kvp.Key, kvp.Value)); } private static async Task GetFinalDocumentTextAsync( From efc2a1506a966afc1b08929d56bb3edc347b04ec Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:41:50 -0700 Subject: [PATCH 198/423] use helper --- .../FixAllOccurrences/BatchFixAllProvider.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs index a7e5192a9bdf0..c5fccb6afbcfb 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs @@ -253,15 +253,10 @@ private static Action> GetRegisterCodeFix private static async Task ApplyChangesAsync( Solution currentSolution, - ImmutableArray<(DocumentId, TextChangeMerger)> docIdsAndMerger, + ImmutableArray<(DocumentId documentId, TextChangeMerger merger)> docIdsAndMerger, CancellationToken cancellationToken) { - foreach (var (documentId, textMerger) in docIdsAndMerger) - { - var newText = await textMerger.GetFinalMergedTextAsync(cancellationToken).ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentText(documentId, newText); - } - - return currentSolution; + var docIdsAndTexts = await docIdsAndMerger.SelectAsArrayAsync(async t => (t.documentId, await t.merger.GetFinalMergedTextAsync(cancellationToken).ConfigureAwait(false))).ConfigureAwait(false); + return currentSolution.WithDocumentTexts(docIdsAndTexts); } } From 47f4a96622ccb7ed3d65d59b22b4fb0e0ba65b79 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:43:48 -0700 Subject: [PATCH 199/423] use helper --- .../DocumentBasedFixAllProviderHelpers.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs index 24589e3884540..a58b5618efa87 100644 --- a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs +++ b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/DocumentBasedFixAllProviderHelpers.cs @@ -52,17 +52,14 @@ internal static class DocumentBasedFixAllProviderHelpers progressTracker, cancellationToken).ConfigureAwait(false); - // Once we clean the document, we get the text of it and insert that back into the final solution. This way - // we can release both the original fixed tree, and the cleaned tree (both of which can be much more - // expensive than just text). - var finalSolution = cleanedSolution; - foreach (var documentId in CodeAction.GetAllChangedOrAddedDocumentIds(originalSolution, finalSolution)) - { - var cleanedDocument = finalSolution.GetRequiredDocument(documentId); - var cleanedText = await cleanedDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - finalSolution = finalSolution.WithDocumentText(documentId, cleanedText); - } - + // Once we clean the document, we get the text of it and insert that back into the final solution. This way we + // can release both the original fixed tree, and the cleaned tree (both of which can be much more expensive than + // just text). + var cleanedTexts = await CodeAction.GetAllChangedOrAddedDocumentIds(originalSolution, cleanedSolution) + .SelectAsArrayAsync(async documentId => (documentId, await cleanedSolution.GetRequiredDocument(documentId).GetTextAsync(cancellationToken).ConfigureAwait(false))) + .ConfigureAwait(false); + + var finalSolution = cleanedSolution.WithDocumentTexts(cleanedTexts); return finalSolution; async Task GetInitialUncleanedSolutionAsync(Solution originalSolution) From cbc0df086f484cd0441ccee63767948551e88dcf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:44:28 -0700 Subject: [PATCH 200/423] use helper --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 1f7fb23a853f3..59464d6c0f4fb 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -54,10 +54,8 @@ internal async Task MergeDiffsAsync(IMergeConflict mergedText = await newSolution.GetDocument(linkedDocumentsWithChanges.Single()).GetValueTextAsync(cancellationToken).ConfigureAwait(false); } - foreach (var documentId in allLinkedDocuments) - { - updatedSolution = updatedSolution.WithDocumentText(documentId, mergedText); - } + updatedSolution = updatedSolution.WithDocumentTexts( + allLinkedDocuments.SelectAsArray(documentId => (documentId, mergedText))); } return new LinkedFileMergeSessionResult(updatedSolution, linkedFileMergeResults); From a870326caa00591f365db11ca767bbf4a5c3976d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 17:48:27 -0700 Subject: [PATCH 201/423] use helper --- .../Core/Portable/Remote/RemoteUtilities.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs b/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs index 31746fb54306c..ee221fb20114f 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteUtilities.cs @@ -47,15 +47,21 @@ internal static class RemoteUtilities /// a solution textually equivalent to the newSolution passed to . /// public static async Task UpdateSolutionAsync( - Solution oldSolution, ImmutableArray<(DocumentId, ImmutableArray)> documentTextChanges, CancellationToken cancellationToken) + Solution oldSolution, + ImmutableArray<(DocumentId documentId, ImmutableArray textChanges)> documentTextChanges, + CancellationToken cancellationToken) { var currentSolution = oldSolution; - foreach (var (docId, textChanges) in documentTextChanges) - { - var text = await oldSolution.GetDocument(docId).GetValueTextAsync(cancellationToken).ConfigureAwait(false); - currentSolution = currentSolution.WithDocumentText(docId, text.WithChanges(textChanges)); - } - return currentSolution; + var documentIdsAndTexts = await documentTextChanges + .SelectAsArrayAsync(async (tuple, cancellationToken) => + { + var oldText = await oldSolution.GetDocument(tuple.documentId).GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var newText = oldText.WithChanges(tuple.textChanges); + return (tuple.documentId, newText); + }, cancellationToken) + .ConfigureAwait(false); + + return oldSolution.WithDocumentTexts(documentIdsAndTexts); } } From 4d8b8a6f39f725627af661bfc87a20fe0a976ffc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 8 May 2024 19:04:02 -0700 Subject: [PATCH 202/423] Merge touch document actions --- ...pilationState.TranslationAction_Actions.cs | 87 +++++-------------- .../Solution/SolutionCompilationState.cs | 8 +- 2 files changed, 24 insertions(+), 71 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 572aaa4cb1824..cb8e4c37c351b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; @@ -15,24 +16,29 @@ internal partial class SolutionCompilationState { private abstract partial class TranslationAction { - internal sealed class TouchDocumentAction( + internal sealed class TouchDocumentsAction( ProjectState oldProjectState, ProjectState newProjectState, - DocumentState oldState, - DocumentState newState) - : TranslationAction(oldProjectState, newProjectState) + ImmutableArray newStates) : TranslationAction(oldProjectState, newProjectState) { - private readonly DocumentState _oldState = oldState; - private readonly DocumentState _newState = newState; + private readonly ImmutableArray _oldStates = newStates.SelectAsArray(s => oldProjectState.DocumentStates.GetRequiredState(s.Id)); + private readonly ImmutableArray _newStates = newStates; public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) { - return oldCompilation.ReplaceSyntaxTree( - await _oldState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false), - await _newState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); - } + var finalCompilation = oldCompilation; + for (int i = 0, n = _newStates.Length; i < n; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + var newState = _newStates[i]; + var oldState = _oldStates[i]; + finalCompilation = finalCompilation.ReplaceSyntaxTree( + await oldState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false), + await newState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); + } - public DocumentId DocumentId => _newState.Attributes.Id; + return finalCompilation; + } /// /// Replacing a single tree doesn't impact the generated trees in a compilation, so we can use this against @@ -45,71 +51,18 @@ public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generat public override TranslationAction? TryMergeWithPrior(TranslationAction priorAction) { - if (priorAction is TouchDocumentAction priorTouchAction && - priorTouchAction._newState == _oldState) + if (priorAction is TouchDocumentsAction priorTouchAction && + priorTouchAction._newStates.SequenceEqual(_oldStates)) { // As we're merging ourselves with the prior touch action, we want to keep the old project state // that we are translating from. - return new TouchDocumentAction(priorAction.OldProjectState, this.NewProjectState, priorTouchAction._oldState, _newState); + return new TouchDocumentsAction(priorAction.OldProjectState, this.NewProjectState, _newStates); } return null; } } - internal sealed class TouchDocumentsAction : TranslationAction - { - private readonly ImmutableArray _newStates; - - private TouchDocumentsAction( - ProjectState oldProjectState, - ProjectState newProjectState, - ImmutableArray newStates) - : base(oldProjectState, newProjectState) - { - _newStates = newStates; - } - - public static TranslationAction Create( - ProjectState oldProjectState, - ProjectState newProjectState, - ImmutableArray newStates) - { - // Special case when we're only updating a single document. This case can be optimized more, and - // corresponds to the common case of a single file being edited. - return newStates.Length == 1 - ? new TouchDocumentAction(oldProjectState, newProjectState, oldProjectState.DocumentStates.GetRequiredState(newStates[0].Id), newStates[0]) - : new TouchDocumentsAction(oldProjectState, newProjectState, newStates); - } - - public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) - { - var finalCompilation = oldCompilation; - for (int i = 0, n = _newStates.Length; i < n; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - var newState = _newStates[i]; - var oldState = this.OldProjectState.DocumentStates.GetRequiredState(newState.Id); - finalCompilation = finalCompilation.ReplaceSyntaxTree( - await oldState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false), - await newState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); - } - - return finalCompilation; - } - - /// - public override bool CanUpdateCompilationWithStaleGeneratedTreesIfGeneratorsGiveSameOutput - => true; - - /// - public override GeneratorDriver TransformGeneratorDriver(GeneratorDriver generatorDriver) - => generatorDriver; - - public override TranslationAction? TryMergeWithPrior(TranslationAction priorAction) - => null; - } - internal sealed class TouchAdditionalDocumentAction( ProjectState oldProjectState, ProjectState newProjectState, diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 779dda124c3f3..bac6a494cd712 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -735,7 +735,7 @@ internal SolutionCompilationState WithDocumentTexts( }), static (projectState, newDocumentStates) => { - return TranslationAction.TouchDocumentsAction.Create( + return new TranslationAction.TouchDocumentsAction( projectState, projectState.UpdateDocuments(newDocumentStates, contentChanged: true), newDocumentStates); @@ -816,7 +816,7 @@ public SolutionCompilationState WithDocumentSyntaxRoots(ImmutableArray<(Document }), static (projectState, newDocumentStates) => { - return TranslationAction.TouchDocumentsAction.Create( + return new TranslationAction.TouchDocumentsAction( projectState, projectState.UpdateDocuments(newDocumentStates, contentChanged: true), newDocumentStates); @@ -899,10 +899,10 @@ private SolutionCompilationState UpdateDocumentState(StateChange stateChange, Do // This function shouldn't have been called if the document has not changed Debug.Assert(stateChange.OldProjectState != stateChange.NewProjectState); - var oldDocument = stateChange.OldProjectState.DocumentStates.GetRequiredState(documentId); var newDocument = stateChange.NewProjectState.DocumentStates.GetRequiredState(documentId); - return new TranslationAction.TouchDocumentAction(stateChange.OldProjectState, stateChange.NewProjectState, oldDocument, newDocument); + return new TranslationAction.TouchDocumentsAction( + stateChange.OldProjectState, stateChange.NewProjectState, [newDocument]); }, forkTracker: true, arg: documentId); From c23414e1365f21c6bd34ab5b007546066f25b5d4 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 8 May 2024 19:42:19 -0700 Subject: [PATCH 203/423] Fix overload resolution regression around params parameters (#73373) Fixes #73346. --- .../Portable/Binder/Binder_Expressions.cs | 2 +- .../Portable/Binder/Binder_Invocation.cs | 2 +- .../MemberAnalysisResult.cs | 13 +- .../OverloadResolution/OverloadResolution.cs | 121 ++++++++++-------- .../Emit2/Semantics/ParamsCollectionTests.cs | 112 ++++++++++++++++ 5 files changed, 191 insertions(+), 59 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 3fb4a0c6d552d..cb4fa6f2bf9e3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -10627,7 +10627,7 @@ bool satisfiesConstraintChecks(MethodSymbol method) parameters.SelectAsArray(p => p.ExplicitDefaultConstantValue) : default; - var hasParams = OverloadResolution.IsValidParams(this, methodSymbol); + var hasParams = OverloadResolution.IsValidParams(this, methodSymbol, out _); Debug.Assert(ContainingMemberOrLambda is { }); Debug.Assert(parameterRefKinds.IsDefault || parameterRefKinds.Length == parameterTypes.Length); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index e7c92744a4381..6ff5e6c47c6ba 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -882,7 +882,7 @@ private void ReportDynamicInvocationWarnings(SyntaxNode syntax, BoundMethodGroup private bool IsAmbiguousDynamicParamsArgument(ArrayBuilder arguments, MemberResolutionResult candidate, out SyntaxNode argumentSyntax) where TMethodOrPropertySymbol : Symbol { - if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember) && + if (OverloadResolution.IsValidParams(this, candidate.LeastOverriddenMember, out _) && candidate.Result.Kind == MemberResolutionKind.ApplicableInNormalForm) { var parameters = candidate.Member.GetParameters(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index f0a6c46065c3a..d60aa1ae31254 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -105,6 +105,7 @@ private init public readonly int BadParameter; public readonly MemberResolutionKind Kind; + public readonly TypeWithAnnotations DefinitionParamsElementTypeOpt; public readonly TypeWithAnnotations ParamsElementTypeOpt; /// @@ -121,11 +122,14 @@ private MemberAnalysisResult( int missingParameter = -1, bool hasAnyRefOmittedArgument = false, ImmutableArray constraintFailureDiagnosticsOpt = default, + TypeWithAnnotations definitionParamsElementTypeOpt = default, TypeWithAnnotations paramsElementTypeOpt = default) { + Debug.Assert(kind != MemberResolutionKind.ApplicableInExpandedForm || definitionParamsElementTypeOpt.HasType); Debug.Assert(kind != MemberResolutionKind.ApplicableInExpandedForm || paramsElementTypeOpt.HasType); this.Kind = kind; + this.DefinitionParamsElementTypeOpt = definitionParamsElementTypeOpt; this.ParamsElementTypeOpt = paramsElementTypeOpt; this.BadArgumentsOpt = badArgumentsOpt; this.ArgsToParamsOpt = argsToParamsOpt; @@ -314,7 +318,7 @@ public static MemberAnalysisResult UnsupportedMetadata() return new MemberAnalysisResult(MemberResolutionKind.UnsupportedMetadata); } - public static MemberAnalysisResult BadArgumentConversions(ImmutableArray argsToParamsOpt, BitVector badArguments, ImmutableArray conversions, TypeWithAnnotations paramsElementTypeOpt) + public static MemberAnalysisResult BadArgumentConversions(ImmutableArray argsToParamsOpt, BitVector badArguments, ImmutableArray conversions, TypeWithAnnotations definitionParamsElementTypeOpt, TypeWithAnnotations paramsElementTypeOpt) { Debug.Assert(conversions.Length != 0); Debug.Assert(badArguments.TrueBits().Any()); @@ -323,6 +327,7 @@ public static MemberAnalysisResult BadArgumentConversions(ImmutableArray ar badArguments, argsToParamsOpt, conversions, + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, paramsElementTypeOpt: paramsElementTypeOpt); } @@ -373,9 +378,11 @@ public static MemberAnalysisResult NormalForm(ImmutableArray argsToParamsOp return new MemberAnalysisResult(MemberResolutionKind.ApplicableInNormalForm, BitVector.Null, argsToParamsOpt, conversions, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument); } - public static MemberAnalysisResult ExpandedForm(ImmutableArray argsToParamsOpt, ImmutableArray conversions, bool hasAnyRefOmittedArgument, TypeWithAnnotations paramsElementType) + public static MemberAnalysisResult ExpandedForm(ImmutableArray argsToParamsOpt, ImmutableArray conversions, bool hasAnyRefOmittedArgument, TypeWithAnnotations definitionParamsElementType, TypeWithAnnotations paramsElementType) { - return new MemberAnalysisResult(MemberResolutionKind.ApplicableInExpandedForm, BitVector.Null, argsToParamsOpt, conversions, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, paramsElementTypeOpt: paramsElementType); + return new MemberAnalysisResult( + MemberResolutionKind.ApplicableInExpandedForm, BitVector.Null, argsToParamsOpt, conversions, + hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, definitionParamsElementTypeOpt: definitionParamsElementType, paramsElementTypeOpt: paramsElementType); } public static MemberAnalysisResult Worse() diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 385db1686b016..5acde0dfa8f0a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -817,9 +817,9 @@ private void AddConstructorToCandidateSet(MethodSymbol constructor, ArrayBuilder var result = normalResult; if (!normalResult.IsValid) { - if (IsValidParams(_binder, constructor)) + if (IsValidParams(_binder, constructor, out TypeWithAnnotations definitionElementType)) { - var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, completeResults, ref useSiteInfo); + var expandedResult = IsConstructorApplicableInExpandedForm(constructor, arguments, definitionElementType, completeResults, ref useSiteInfo); if (expandedResult.IsValid || completeResults) { result = expandedResult; @@ -864,6 +864,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( return IsApplicable( constructor, effectiveParameters, + definitionParamsElementTypeOpt: default, isExpanded: false, arguments, argumentAnalysis.ArgsToParamsOpt, @@ -878,6 +879,7 @@ private MemberAnalysisResult IsConstructorApplicableInNormalForm( private MemberAnalysisResult IsConstructorApplicableInExpandedForm( MethodSymbol constructor, AnalyzedArguments arguments, + TypeWithAnnotations definitionParamsElementType, bool completeResults, ref CompoundUseSiteInfo useSiteInfo) { @@ -906,6 +908,7 @@ private MemberAnalysisResult IsConstructorApplicableInExpandedForm( var result = IsApplicable( constructor, effectiveParameters, + definitionParamsElementTypeOpt: definitionParamsElementType, isExpanded: true, arguments, argumentAnalysis.ArgsToParamsOpt, @@ -1038,7 +1041,7 @@ private void AddMemberToCandidateSet( // Second, we need to determine if the method is applicable in its normal form or its expanded form. - var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember)) + var normalResult = ((options & Options.IgnoreNormalFormIfHasValidParamsParameter) != 0 && IsValidParams(_binder, leastOverriddenMember, out _)) ? default(MemberResolutionResult) : IsMemberApplicableInNormalForm( member, @@ -1057,13 +1060,14 @@ private void AddMemberToCandidateSet( // tricks you can pull to make overriding methods [indexers] inconsistent with overridden // methods [indexers] (or implementing methods [indexers] inconsistent with interfaces). - if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember)) + if ((options & Options.IsMethodGroupConversion) == 0 && IsValidParams(_binder, leastOverriddenMember, out TypeWithAnnotations definitionElementType)) { var expandedResult = IsMemberApplicableInExpandedForm( member, leastOverriddenMember, typeArguments, arguments, + definitionElementType, allowRefOmittedArguments: (options & Options.AllowRefOmittedArguments) != 0, completeResults: completeResults, dynamicConvertsToAnything: (options & Options.DynamicConvertsToAnything) != 0, @@ -1156,17 +1160,19 @@ static bool haveBadArgumentForLastParameter(MemberResolutionResult resu // We need to know if this is a valid formal parameter list with a parameter array // as the final formal parameter. We might be in an error recovery scenario // where the params array is not an array type. - public static bool IsValidParams(Binder binder, Symbol member) + public static bool IsValidParams(Binder binder, Symbol member, out TypeWithAnnotations definitionElementType) { // A varargs method is never a valid params method. if (member.GetIsVararg()) { + definitionElementType = default; return false; } int paramCount = member.GetParameterCount(); if (paramCount == 0) { + definitionElementType = default; return false; } @@ -1175,9 +1181,10 @@ public static bool IsValidParams(Binder binder, Symbol member) (final.IsParamsCollection && !final.Type.IsSZArray() && (binder.Compilation.LanguageVersion > LanguageVersion.CSharp12 || member.ContainingModule == binder.Compilation.SourceModule))) { - return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out _); + return TryInferParamsCollectionIterationType(binder, final.OriginalDefinition.Type, out definitionElementType); } + definitionElementType = default; return false; } @@ -1837,25 +1844,6 @@ private void RemoveWorseMembers(ArrayBuilder - /// Returns the parameter type (considering params). - /// - private static TypeSymbol GetParameterType(ParameterSymbol parameter, MemberAnalysisResult result, int parameterCount) - { - var type = parameter.Type; - if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && - parameter.Ordinal == parameterCount - 1) - { - Debug.Assert(result.ParamsElementTypeOpt.HasType); - Debug.Assert(result.ParamsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); - return result.ParamsElementTypeOpt.Type; - } - else - { - return type; - } - } - /// /// Returns the parameter corresponding to the given argument index. /// @@ -1955,11 +1943,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var type1 = GetParameterType(parameter1, m1.Result, m1LeastOverriddenParameters.Length); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out RefKind parameter1RefKind); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); - var type2 = GetParameterType(parameter2, m2.Result, m2LeastOverriddenParameters.Length); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out RefKind parameter2RefKind); bool okToDowngradeToNeither; BetterResult r; @@ -1967,10 +1953,10 @@ private BetterResult BetterFunctionMember( r = BetterConversionFromExpression(arguments[i], type1, m1.Result.ConversionForArg(i), - parameter1.RefKind, + parameter1RefKind, type2, m2.Result.ConversionForArg(i), - parameter2.RefKind, + parameter2RefKind, considerRefKinds, ref useSiteInfo, out okToDowngradeToNeither); @@ -2099,11 +2085,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var type1 = GetParameterType(parameter1, m1.Result, m1LeastOverriddenParameters.Length); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out _); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); - var type2 = GetParameterType(parameter2, m2.Result, m2LeastOverriddenParameters.Length); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out _); var type1Normalized = type1; var type2Normalized = type2; @@ -2248,8 +2232,8 @@ private BetterResult BetterFunctionMember( using (var uninst1 = TemporaryArray.Empty) using (var uninst2 = TemporaryArray.Empty) { - var m1Original = m1.LeastOverriddenMember.OriginalDefinition.GetParameters(); - var m2Original = m2.LeastOverriddenMember.OriginalDefinition.GetParameters(); + var m1DefinitionParameters = m1.LeastOverriddenMember.OriginalDefinition.GetParameters(); + var m2DefinitionParameters = m2.LeastOverriddenMember.OriginalDefinition.GetParameters(); for (i = 0; i < arguments.Count; ++i) { // If these are both applicable varargs methods and we're looking at the __arglist argument @@ -2261,11 +2245,9 @@ private BetterResult BetterFunctionMember( continue; } - var parameter1 = GetParameter(i, m1.Result, m1Original); - uninst1.Add(GetParameterType(parameter1, m1.Result, m1Original.Length)); + uninst1.Add(getParameterTypeAndRefKind(i, m1.Result, m1DefinitionParameters, m1.Result.DefinitionParamsElementTypeOpt, out _)); - var parameter2 = GetParameter(i, m2.Result, m2Original); - uninst2.Add(GetParameterType(parameter2, m2.Result, m2Original.Length)); + uninst2.Add(getParameterTypeAndRefKind(i, m2.Result, m2DefinitionParameters, m2.Result.DefinitionParamsElementTypeOpt, out _)); } result = MoreSpecificType(ref uninst1.AsRef(), ref uninst2.AsRef(), ref useSiteInfo); @@ -2353,6 +2335,26 @@ private BetterResult BetterFunctionMember( } return BetterResult.Neither; + + // Returns the parameter type (considering params). + static TypeSymbol getParameterTypeAndRefKind(int i, MemberAnalysisResult result, ImmutableArray parameters, TypeWithAnnotations paramsElementTypeOpt, out RefKind parameter1RefKind) + { + var parameter = GetParameter(i, result, parameters); + parameter1RefKind = parameter.RefKind; + + var type = parameter.Type; + if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && + parameter.Ordinal == parameters.Length - 1) + { + Debug.Assert(paramsElementTypeOpt.HasType); + Debug.Assert(paramsElementTypeOpt.Type != (object)ErrorTypeSymbol.EmptyParamsCollectionElementTypeSentinel); + return paramsElementTypeOpt.Type; + } + else + { + return type; + } + } } /// @@ -3691,7 +3693,7 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( TMember leastOverriddenMemberConstructedFrom = GetConstructedFrom(leastOverriddenMember); bool hasAnyRefOmittedArgument; - EffectiveParameters originalEffectiveParameters = GetEffectiveParametersInNormalForm( + EffectiveParameters constructedFromEffectiveParameters = GetEffectiveParametersInNormalForm( leastOverriddenMemberConstructedFrom, arguments.Arguments.Count, argumentAnalysis.ArgsToParamsOpt, @@ -3706,7 +3708,8 @@ private MemberResolutionResult IsMemberApplicableInNormalForm( // The applicability is checked based on effective parameters passed in. var applicableResult = IsApplicable( member, leastOverriddenMemberConstructedFrom, - typeArguments, arguments, originalEffectiveParameters, + typeArguments, arguments, constructedFromEffectiveParameters, + definitionParamsElementTypeOpt: default, isExpanded: false, argumentAnalysis.ArgsToParamsOpt, hasAnyRefOmittedArgument: hasAnyRefOmittedArgument, @@ -3730,6 +3733,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm typeArguments, AnalyzedArguments arguments, + TypeWithAnnotations definitionParamsElementType, bool allowRefOmittedArguments, bool completeResults, bool dynamicConvertsToAnything, @@ -3754,7 +3758,7 @@ private MemberResolutionResult IsMemberApplicableInExpandedForm IsMemberApplicableInExpandedForm IsApplicable( TMember leastOverriddenMember, // method or property ArrayBuilder typeArgumentsBuilder, AnalyzedArguments arguments, - EffectiveParameters originalEffectiveParameters, + EffectiveParameters constructedFromEffectiveParameters, + TypeWithAnnotations definitionParamsElementTypeOpt, bool isExpanded, ImmutableArray argsToParamsMap, bool hasAnyRefOmittedArgument, @@ -3836,7 +3842,7 @@ private MemberResolutionResult IsApplicable( typeArguments = InferMethodTypeArguments(method, leastOverriddenMethod.ConstructedFrom.TypeParameters, arguments, - originalEffectiveParameters, + constructedFromEffectiveParameters, out hasTypeArgumentsInferredFromFunctionType, out inferenceError, ref useSiteInfo); @@ -3892,19 +3898,20 @@ private MemberResolutionResult IsApplicable( var map = new TypeMap(leastOverriddenMethod.TypeParameters, typeArguments, allowAlpha: true); constructedEffectiveParameters = new EffectiveParameters( - map.SubstituteTypes(originalEffectiveParameters.ParameterTypes), - originalEffectiveParameters.ParameterRefKinds, - originalEffectiveParameters.FirstParamsElementIndex); + map.SubstituteTypes(constructedFromEffectiveParameters.ParameterTypes), + constructedFromEffectiveParameters.ParameterRefKinds, + constructedFromEffectiveParameters.FirstParamsElementIndex); } else { - constructedEffectiveParameters = originalEffectiveParameters; + constructedEffectiveParameters = constructedFromEffectiveParameters; ignoreOpenTypes = false; } var applicableResult = IsApplicable( member, constructedEffectiveParameters, + definitionParamsElementTypeOpt, isExpanded, arguments, argsToParamsMap, @@ -3975,6 +3982,7 @@ private ImmutableArray InferMethodTypeArguments( private MemberAnalysisResult IsApplicable( Symbol candidate, // method or property EffectiveParameters parameters, + TypeWithAnnotations definitionParamsElementTypeOpt, bool isExpanded, AnalyzedArguments arguments, ImmutableArray argsToParameters, @@ -4113,7 +4121,8 @@ private MemberAnalysisResult IsApplicable( // of overloads for the semantic model. Debug.Assert(badArguments.IsNull); Debug.Assert(conversions == null); - return MemberAnalysisResult.BadArgumentConversions(argsToParameters, MemberAnalysisResult.CreateBadArgumentsWithPosition(argumentPosition), ImmutableArray.Create(conversion), paramsElementTypeOpt); + return MemberAnalysisResult.BadArgumentConversions(argsToParameters, MemberAnalysisResult.CreateBadArgumentsWithPosition(argumentPosition), ImmutableArray.Create(conversion), + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, paramsElementTypeOpt: paramsElementTypeOpt); } if (!conversion.Exists) @@ -4148,12 +4157,16 @@ private MemberAnalysisResult IsApplicable( var conversionsArray = conversions != null ? conversions.ToImmutableAndFree() : default(ImmutableArray); if (!badArguments.IsNull) { - result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments, conversionsArray, paramsElementTypeOpt); + result = MemberAnalysisResult.BadArgumentConversions(argsToParameters, badArguments, conversionsArray, + definitionParamsElementTypeOpt: definitionParamsElementTypeOpt, + paramsElementTypeOpt: paramsElementTypeOpt); } else if (isExpanded) { Debug.Assert(paramsElementTypeOpt.HasType); - result = MemberAnalysisResult.ExpandedForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument, paramsElementTypeOpt); + result = MemberAnalysisResult.ExpandedForm(argsToParameters, conversionsArray, hasAnyRefOmittedArgument, + definitionParamsElementType: definitionParamsElementTypeOpt, + paramsElementType: paramsElementTypeOpt); } else { diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs index 81467ecb958c5..b512928223c16 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/ParamsCollectionTests.cs @@ -15650,5 +15650,117 @@ static void Main() Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("1", "int", "params MyCollection").WithLocation(19, 14) ); } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_01() + { + string source = """ +using System; + +namespace OverloadResolutionRepro +{ + public class C + { + public void Method(params Func[] projections) => Console.Write(1); + public void Method(params Func>[] projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C().Method(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_02() + { + string source = """ +using System; + +namespace OverloadResolutionRepro +{ + public class C + { + public C(params Func[] projections) => Console.Write(1); + public C(params Func>[] projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C>(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/73346")] + public void ParameterTypeSpecificity_03() + { + string source = """ +using System; +using System.Collections.Generic; + +namespace OverloadResolutionRepro +{ + public class C + { + public void Method(params IEnumerable> projections) => Console.Write(1); + public void Method(params IEnumerable>> projections) => Console.Write(2); + } + + public class Bar + { + public Wrapper WrappedValue { get; set; } = new Wrapper(); + } + + public struct Wrapper + { + } + + public class EntryPoint + { + static void Main() + { + new C().Method(x => x.WrappedValue); + } + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + CompileAndVerify(comp, expectedOutput: "2").VerifyDiagnostics(); + } } } From 8b7d08047ac3d69e8af7dde671f5b96393bae856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 9 May 2024 08:19:44 -0700 Subject: [PATCH 204/423] EnC: workaround for empty display class (#73366) --- .../Core/Portable/Emit/EditAndContinue/DefinitionMap.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs index 03aebde7eba92..dfdd6d5956f06 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs @@ -588,7 +588,14 @@ void recurse(ImmutableArray members, DebugId? containingDisplay Debug.Assert(!containingDisplayClassId.HasValue); var displayClass = (INamedTypeSymbolInternal)member; - var displayClassMembers = (synthesizedMemberMap != null) ? synthesizedMemberMap[displayClass] : displayClass.GetMembers(); + + // Synthesized member map, if given, contains all the synthesized members that implement lambdas and closures. + // If not given (for PE symbols) the members are defined on the type symbol directly. + // If the display class doesn't have any synthesized members it won't be present in the map. + // See https://github.com/dotnet/roslyn/issues/73365 + var displayClassMembers = synthesizedMemberMap != null + ? (synthesizedMemberMap.TryGetValue(displayClass, out var m) ? m : []) + : displayClass.GetMembers(); if (displayClass.TypeKind == TypeKind.Struct) { From 325dedb8c271c2c603f9cf414fa56873fffb3b74 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 9 May 2024 09:38:06 -0700 Subject: [PATCH 205/423] Add variable group --- azure-pipelines-official.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 72fb4cfb5b958..731a818a6f6c7 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -58,6 +58,7 @@ variables: - name: ArtifactServices.Drop.PAT value: $(dn-bot-devdiv-drop-rw-code-rw) - group: DotNet-Roslyn-Insertion-Variables + - group: AzureDevOps-Artifact-Feeds-Pats - name: BuildConfiguration value: release From 8cd5d5ea8bcba307dbe298f4f1ccf3ff0e60a650 Mon Sep 17 00:00:00 2001 From: Michael Liu Date: Thu, 9 May 2024 09:48:28 -0700 Subject: [PATCH 206/423] Fix Double-to-Long overflow check (#73016) --- .../Semantics/CompileTimeCalculations.vb | 2 +- .../Test/Semantic/Semantics/Conversions.vb | 47 ++++++++++++++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb b/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb index d30639cf12746..79d119f11bf1f 100644 --- a/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb +++ b/src/Compilers/VisualBasic/Portable/Semantics/CompileTimeCalculations.vb @@ -603,7 +603,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic If sourceValue > &H7000000000000000L Then Dim temporary As Double = (sourceValue - &H7000000000000000L) - If temporary < &H7000000000000000L AndAlso UncheckedCLng(temporary) > &H1000000000000000L Then + If temporary < &H7000000000000000L AndAlso UncheckedCLng(temporary) < &H1000000000000000L Then Return False End If Else diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb index d7d6b1d6070b9..25b3ddd15a495 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/Conversions.vb @@ -605,7 +605,8 @@ End Class New TypeAndValue(uint64Type, CULng(18)), New TypeAndValue(decimalType, CDec(-11.3)), New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), - New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), + New TypeAndValue(doubleType, CDbl(&H8000000000000000L)), + New TypeAndValue(doubleType, CDbl(&H7FFFFFFFFFFFFC00L)), New TypeAndValue(typeCodeType, Int32.MinValue), New TypeAndValue(typeCodeType, Int32.MaxValue), New TypeAndValue(typeCodeType, CInt(-3)), @@ -1165,7 +1166,8 @@ End Class New TypeAndValue(uint64Type, CULng(18)), New TypeAndValue(decimalType, CDec(-11.3)), New TypeAndValue(doubleType, CDbl(&HF000000000000000UL)), - New TypeAndValue(doubleType, CDbl(&H70000000000000F0L)), + New TypeAndValue(doubleType, CDbl(&H8000000000000000L)), + New TypeAndValue(doubleType, CDbl(&H7FFFFFFFFFFFFC00L)), New TypeAndValue(typeCodeType, Int32.MinValue), New TypeAndValue(typeCodeType, Int32.MaxValue), New TypeAndValue(typeCodeType, CInt(-3)), @@ -5095,5 +5097,46 @@ End Module ]]>) End Sub + + + Public Sub ConvertLargeDoubleConstantsAndLiteralsToLong() + Dim compilation = CreateCompilationWithMscorlib40AndVBRuntime( + + + , options:=TestOptions.ReleaseExe.WithOverflowChecks(True)) + + Dim expectedOutput = + + CompileAndVerify(compilation, expectedOutput:=expectedOutput).VerifyDiagnostics() + End Sub + End Class End Namespace From da2b98733c9b213374f88bf4697c71f5d0831d59 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 9 May 2024 19:14:24 +0000 Subject: [PATCH 207/423] Failed to perform coherency update for one or more dependencies. From bf406095703ebdc95b74fa320b9df5d6a53bac46 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 9 May 2024 13:38:18 -0700 Subject: [PATCH 208/423] Improve fix-all progress bar experience --- .../SuggestedActions/SuggestedAction.cs | 1 + .../IUIThreadOperationContextExtensions.cs | 19 +++++++++++++------ .../Core/Portable/Workspace/Workspace.cs | 2 ++ .../Core/Portable/WorkspacesResources.resx | 3 +++ .../Portable/xlf/WorkspacesResources.cs.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.de.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.es.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.fr.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.it.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.ja.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.ko.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.pl.xlf | 5 +++++ .../xlf/WorkspacesResources.pt-BR.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.ru.xlf | 5 +++++ .../Portable/xlf/WorkspacesResources.tr.xlf | 5 +++++ .../xlf/WorkspacesResources.zh-Hans.xlf | 5 +++++ .../xlf/WorkspacesResources.zh-Hant.xlf | 5 +++++ 17 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs index a89df6cd37880..799e31259d1c3 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs @@ -165,6 +165,7 @@ private async Task InvokeWorkerAsync(IProgress progressTra // Clear the progress we showed while computing the action. // We'll now show progress as we apply the action. progressTracker.Report(CodeAnalysisProgress.Clear()); + progressTracker.Report(CodeAnalysisProgress.Description(EditorFeaturesResources.Applying_changes)); using (Logger.LogBlock( FunctionId.CodeFixes_ApplyChanges, KeyValueLogMessage.Create(LogType.UserAction, m => CreateLogProperties(m)), cancellationToken)) diff --git a/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs b/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs index 95b63bb13b892..5344392f3694f 100644 --- a/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs +++ b/src/EditorFeatures/Core/CodeActions/IUIThreadOperationContextExtensions.cs @@ -27,14 +27,21 @@ public void Report(CodeAnalysisProgress value) if (value.DescriptionValue != null) scope.Description = value.DescriptionValue; - if (value.IncompleteItemsValue != null) - Interlocked.Add(ref _totalItems, value.IncompleteItemsValue.Value); - - if (value.CompleteItemValue != null) - Interlocked.Add(ref _completedItems, value.CompleteItemValue.Value); + if (value.ClearValue) + { + Interlocked.Exchange(ref _totalItems, 0); + Interlocked.Exchange(ref _completedItems, 0); + } + else + { + if (value.IncompleteItemsValue != null) + Interlocked.Add(ref _totalItems, value.IncompleteItemsValue.Value); + + if (value.CompleteItemValue != null) + Interlocked.Add(ref _completedItems, value.CompleteItemValue.Value); + } scope.Progress.Report(new ProgressInfo(_completedItems, _totalItems)); - } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 201d28946e432..a68324a694c30 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host; @@ -1466,6 +1467,7 @@ internal virtual bool TryApplyChanges(Solution newSolution, IProgress Running code cleanup on fixed documents + + Applying changes to {0} + \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf index 60a243b017cfe..c0c7da7547edc 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf @@ -17,6 +17,11 @@ Došlo k chybě při čtení zadaného konfiguračního souboru: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Soubory C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf index f5a183f74084a..c8e0e89c76867 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf @@ -17,6 +17,11 @@ Beim Lesen der angegebenen Konfigurationsdatei ist ein Fehler aufgetreten: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C#-Dateien diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf index 66c1cf566b81a..688d652b9a210 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf @@ -17,6 +17,11 @@ Error al leer el archivo de configuración especificado: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Archivos de C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf index 910f067fea546..9cf1df47b76d2 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf @@ -17,6 +17,11 @@ Une erreur s'est produite lors de la lecture du fichier de configuration spécifié : {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Fichiers C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf index 131b63119784b..8132baa38dafd 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf @@ -17,6 +17,11 @@ Si è verificato un errore durante la lettura del file di configurazione specificato: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files File C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf index f6cc2a2be97c6..fb0c1c676b2e5 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf @@ -17,6 +17,11 @@ 指定した構成ファイルの読み取り中にエラーが発生しました: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C# ファイル diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf index 574613a48d02b..f00171fca507c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf @@ -17,6 +17,11 @@ 지정한 구성 파일을 읽는 동안 오류가 발생했습니다({0}). + + Applying changes to {0} + Applying changes to {0} + + C# files C# 파일 diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf index 89229d1e54580..baef2a6e60e1c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf @@ -17,6 +17,11 @@ Wystąpił błąd podczas odczytywania określonego pliku konfiguracji: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Pliki C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf index 7b0d07ee6716c..5d21e0d33d94c 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf @@ -17,6 +17,11 @@ Ocorreu um erro ao ler o arquivo de configuração especificado: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Arquivos C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf index 643fa036d905b..9f26b33ad0eec 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf @@ -17,6 +17,11 @@ Произошла ошибка при чтении указанного файла конфигурации: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files Файлы C# diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf index 31523544c5c26..a80f53668d945 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf @@ -17,6 +17,11 @@ Belirtilen yapılandırma dosyası okunurken bir hata oluştu: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C# dosyalarını diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf index 1a8ed118e7826..d3a06528147db 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf @@ -17,6 +17,11 @@ 读取指定的配置文件时出错: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files c# 文件 diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf index 9fd5e7497d743..bffa74e3d2aa5 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf @@ -17,6 +17,11 @@ 讀取指定的組態檔時發生錯誤: {0} + + Applying changes to {0} + Applying changes to {0} + + C# files C# 檔案 From 095bde2cc74d862d865bd0ff5a2e7d4fb2183753 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Thu, 9 May 2024 16:34:09 -0700 Subject: [PATCH 209/423] Enable hosts to provide custom assembly resolution (#73185) Allow a host to override how the AnalyzerAssemblyLoader resolves assemblies: - Have the loader take a set of 'IAnalyzerAssemblyResolvers' which can provide an already loaded assembly - Re-implement compiler specific ALC loading as a built-in resolver - Add MEF imports for the resolvers in the IDE layer and pass them into the loader - Add a specific Razor assembly resolver in external access - Add empty resolver arrays for various test scenarios - Add tests --- .../AnalyzerAssemblyLoaderTests.cs | 121 +++++++++++++++++- .../Core/CodeAnalysisTest/InvokeUtil.cs | 14 +- .../AnalyzerAssemblyLoader.Core.cs | 50 +++++--- .../AnalyzerAssemblyLoader.Desktop.cs | 8 +- .../AnalyzerAssemblyLoader.cs | 36 ++++++ .../DefaultAnalyzerAssemblyLoader.cs | 16 ++- .../IAnalyzerAssemblyResolver.cs | 21 +++ .../ShadowCopyAnalyzerAssemblyLoader.cs | 13 +- .../Razor/RazorAnalyzerAssemblyResolver.cs | 51 ++++++++ .../Remote/SnapshotSerializationTests.cs | 2 +- .../DefaultAnalyzerAssemblyLoaderService.cs | 16 ++- .../Microsoft.CodeAnalysis.Workspaces.csproj | 1 + .../Host/RemoteAnalyzerAssemblyLoader.cs | 4 +- .../RemoteAnalyzerAssemblyLoaderService.cs | 9 +- 14 files changed, 310 insertions(+), 52 deletions(-) create mode 100644 src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs diff --git a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs index 5c47171fb8a6f..4f8554738f2b2 100644 --- a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs @@ -54,7 +54,7 @@ public enum AnalyzerTestKind /// /// Limitation 1: .NET Framework probing path. /// - /// The .NET Framework assembly loader will only call AppDomain.AssemblyResolve when it cannot satifisfy a load + /// The .NET Framework assembly loader will only call AppDomain.AssemblyResolve when it cannot satisfy a load /// request. One of the places the assembly loader will always consider when looking for dependencies of A.dll /// is the directory that A.dll was loading from (it's added to the probing path). That means if B.dll is in the /// same directory then the runtime will silently load it without a way for us to intervene. @@ -95,17 +95,19 @@ public AnalyzerAssemblyLoaderTests(ITestOutputHelper testOutputHelper, AssemblyL #if NETCOREAPP - private void Run(AnalyzerTestKind kind, Action testAction, [CallerMemberName] string? memberName = null) => + private void Run(AnalyzerTestKind kind, Action testAction, IAnalyzerAssemblyResolver[]? externalResolvers = null, [CallerMemberName] string? memberName = null) => Run( kind, static (_, _) => { }, testAction, + externalResolvers, memberName); private void Run( AnalyzerTestKind kind, Action prepLoadContextAction, Action testAction, + IAnalyzerAssemblyResolver[]? externalResolvers = null, [CallerMemberName] string? memberName = null) { var alc = new AssemblyLoadContext($"Test {memberName}", isCollectible: true); @@ -113,7 +115,7 @@ private void Run( { prepLoadContextAction(alc, TestFixture); var util = new InvokeUtil(); - util.Exec(TestOutputHelper, alc, TestFixture, kind, testAction.Method.DeclaringType!.FullName!, testAction.Method.Name); + util.Exec(TestOutputHelper, alc, TestFixture, kind, testAction.Method.DeclaringType!.FullName!, testAction.Method.Name, externalResolvers ?? []); } finally { @@ -126,6 +128,7 @@ private void Run( private void Run( AnalyzerTestKind kind, Action testAction, + IAnalyzerAssemblyResolver[]? externalResolvers = null, [CallerMemberName] string? memberName = null) { AppDomain? appDomain = null; @@ -135,7 +138,7 @@ private void Run( var testOutputHelper = new AppDomainTestOutputHelper(TestOutputHelper); var type = typeof(InvokeUtil); var util = (InvokeUtil)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName); - util.Exec(testOutputHelper, TestFixture, kind, testAction.Method.DeclaringType.FullName, testAction.Method.Name); + util.Exec(testOutputHelper, TestFixture, kind, testAction.Method.DeclaringType.FullName, testAction.Method.Name, externalResolvers ?? []); } finally { @@ -1421,5 +1424,115 @@ public void AssemblyLoadingInNonDefaultContext_AnalyzerReferencesSystemCollectio }); } #endif + + [Theory] + [CombinatorialData] + public void ExternalResolver_CanIntercept_ReturningNull(AnalyzerTestKind kind) + { + var resolver = new TestAnalyzerAssemblyResolver(n => null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + loader.AddDependencyLocation(testFixture.Delta1); + Assembly delta = loader.LoadFromPath(testFixture.Delta1); + Assert.NotNull(delta); + VerifyDependencyAssemblies(loader, testFixture.Delta1); + + }, externalResolvers: [resolver]); + Assert.Collection(resolver.CalledFor, (a => Assert.Equal("Delta", a.Name))); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_CanIntercept_ReturningAssembly(AnalyzerTestKind kind) + { + var resolver = new TestAnalyzerAssemblyResolver(n => GetType().Assembly); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + // net core assembly loader checks that the resolved assembly name is the same as the requested one + // so we use the assembly the tests are contained in as its already be loaded + var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + loader.AddDependencyLocation(thisAssembly.Location); + Assembly loaded = loader.LoadFromPath(thisAssembly.Location); + Assert.Equal(thisAssembly, loaded); + + }, externalResolvers: [resolver]); + Assert.Collection(resolver.CalledFor, (a => Assert.Equal(GetType().Assembly.GetName().Name, a.Name))); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_CanIntercept_ReturningAssembly_Or_Null(AnalyzerTestKind kind) + { + var thisAssemblyName = GetType().Assembly.GetName(); + var resolver = new TestAnalyzerAssemblyResolver(n => n == thisAssemblyName ? GetType().Assembly : null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + + loader.AddDependencyLocation(testFixture.Alpha); + Assembly alpha = loader.LoadFromPath(testFixture.Alpha); + Assert.NotNull(alpha); + + loader.AddDependencyLocation(thisAssembly.Location); + Assembly loaded = loader.LoadFromPath(thisAssembly.Location); + Assert.Equal(thisAssembly, loaded); + + loader.AddDependencyLocation(testFixture.Delta1); + Assembly delta = loader.LoadFromPath(testFixture.Delta1); + Assert.NotNull(delta); + + }, externalResolvers: [resolver]); + Assert.Collection(resolver.CalledFor, (a => Assert.Equal("Alpha", a.Name)), a => Assert.Equal(thisAssemblyName.Name, a.Name), a => Assert.Equal("Delta", a.Name)); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_MultipleResolvers_CanIntercept_ReturningNull(AnalyzerTestKind kind) + { + var resolver1 = new TestAnalyzerAssemblyResolver(n => null); + var resolver2 = new TestAnalyzerAssemblyResolver(n => null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + loader.AddDependencyLocation(testFixture.Delta1); + Assembly delta = loader.LoadFromPath(testFixture.Delta1); + Assert.NotNull(delta); + VerifyDependencyAssemblies(loader, testFixture.Delta1); + + }, externalResolvers: [resolver1, resolver2]); + Assert.Collection(resolver1.CalledFor, (a => Assert.Equal("Delta", a.Name))); + Assert.Collection(resolver2.CalledFor, (a => Assert.Equal("Delta", a.Name))); + } + + [Theory] + [CombinatorialData] + public void ExternalResolver_MultipleResolvers_ResolutionStops_AfterFirstResolve(AnalyzerTestKind kind) + { + var resolver1 = new TestAnalyzerAssemblyResolver(n => GetType().Assembly); + var resolver2 = new TestAnalyzerAssemblyResolver(n => null); + Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + loader.AddDependencyLocation(thisAssembly.Location); + Assembly loaded = loader.LoadFromPath(thisAssembly.Location); + Assert.Equal(thisAssembly, loaded); + + }, externalResolvers: [resolver1, resolver2]); + Assert.Collection(resolver1.CalledFor, (a => Assert.Equal(GetType().Assembly.GetName().Name, a.Name))); + Assert.Empty(resolver2.CalledFor); + } + + [Serializable] + private class TestAnalyzerAssemblyResolver(Func func) : MarshalByRefObject, IAnalyzerAssemblyResolver + { + private readonly Func _func = func; + + public List CalledFor { get; } = []; + + public Assembly? ResolveAssembly(AssemblyName assemblyName) + { + CalledFor.Add(assemblyName); + return _func(assemblyName); + } + } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs b/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs index 3dc9fec60ed45..ef1aae7ebd9d8 100644 --- a/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs +++ b/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs @@ -35,7 +35,7 @@ namespace Microsoft.CodeAnalysis.UnitTests public sealed class InvokeUtil { - public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compilerContext, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName) + internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compilerContext, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName, IAnalyzerAssemblyResolver[] externalResolvers) { // Ensure that the test did not load any of the test fixture assemblies into // the default load context. That should never happen. Assemblies should either @@ -48,9 +48,9 @@ public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compile using var tempRoot = new TempRoot(); AnalyzerAssemblyLoader loader = kind switch { - AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromDisk), - AnalyzerTestKind.LoadStream => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromStream), - AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(compilerContext, tempRoot.CreateDirectory().Path), + AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromDisk, externalResolvers.ToImmutableArray()), + AnalyzerTestKind.LoadStream => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromStream, externalResolvers.ToImmutableArray()), + AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(compilerContext, tempRoot.CreateDirectory().Path, externalResolvers.ToImmutableArray()), _ => throw ExceptionUtilities.Unreachable() }; @@ -93,13 +93,13 @@ public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compile public sealed class InvokeUtil : MarshalByRefObject { - public void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName) + internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName, IAnalyzerAssemblyResolver[] externalResolvers) { using var tempRoot = new TempRoot(); AnalyzerAssemblyLoader loader = kind switch { - AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(), - AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(tempRoot.CreateDirectory().Path), + AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(externalResolvers.ToImmutableArray()), + AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(tempRoot.CreateDirectory().Path, externalResolvers.ToImmutableArray()), _ => throw ExceptionUtilities.Unreachable() }; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs index 5e2a1d99f08dc..c18b863c37250 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs @@ -43,15 +43,16 @@ internal partial class AnalyzerAssemblyLoader internal AssemblyLoadContext CompilerLoadContext => _compilerLoadContext; internal AnalyzerLoadOption AnalyzerLoadOption => _loadOption; - internal AnalyzerAssemblyLoader() - : this(null, AnalyzerLoadOption.LoadFromDisk) + internal AnalyzerAssemblyLoader(ImmutableArray externalResolvers) + : this(null, AnalyzerLoadOption.LoadFromDisk, externalResolvers) { } - internal AnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, AnalyzerLoadOption loadOption) + internal AnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, AnalyzerLoadOption loadOption, ImmutableArray externalResolvers) { _loadOption = loadOption; _compilerLoadContext = compilerLoadContext ?? AssemblyLoadContext.GetLoadContext(typeof(AnalyzerAssemblyLoader).GetTypeInfo().Assembly)!; + _externalResolvers = [.. externalResolvers, new CompilerAnalyzerAssemblyResolver(_compilerLoadContext)]; } public bool IsHostAssembly(Assembly assembly) @@ -69,7 +70,7 @@ private partial Assembly Load(AssemblyName assemblyName, string assemblyOriginal { if (!_loadContextByDirectory.TryGetValue(fullDirectoryPath, out loadContext)) { - loadContext = new DirectoryLoadContext(fullDirectoryPath, this, _compilerLoadContext); + loadContext = new DirectoryLoadContext(fullDirectoryPath, this); _loadContextByDirectory[fullDirectoryPath] = loadContext; } } @@ -107,33 +108,23 @@ internal sealed class DirectoryLoadContext : AssemblyLoadContext { internal string Directory { get; } private readonly AnalyzerAssemblyLoader _loader; - private readonly AssemblyLoadContext _compilerLoadContext; - public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader, AssemblyLoadContext compilerLoadContext) + public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader) : base(isCollectible: true) { Directory = directory; _loader = loader; - _compilerLoadContext = compilerLoadContext; } protected override Assembly? Load(AssemblyName assemblyName) { - var simpleName = assemblyName.Name!; - try + if (_loader.ResolveAssemblyExternally(assemblyName) is { } externallyResolvedAssembly) { - if (_compilerLoadContext.LoadFromAssemblyName(assemblyName) is { } compilerAssembly) - { - return compilerAssembly; - } - } - catch - { - // Expected to happen when the assembly cannot be resolved in the compiler / host - // AssemblyLoadContext. + return externallyResolvedAssembly; } // Prefer registered dependencies in the same directory first. + var simpleName = assemblyName.Name!; var assemblyPath = Path.Combine(Directory, simpleName + ".dll"); if (_loader.IsAnalyzerDependencyPath(assemblyPath)) { @@ -147,7 +138,7 @@ public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader, Ass // Note: when loading from disk the .NET runtime has a fallback step that will handle // satellite assembly loading if the call to Load(satelliteAssemblyName) fails. This // loader has a mode where it loads from Stream though and the runtime will not handle - // that automatically. Rather than bifurate our loading behavior between Disk and + // that automatically. Rather than bifurcate our loading behavior between Disk and // Stream both modes just handle satellite loading directly if (assemblyName.CultureInfo is not null && simpleName.EndsWith(".resources", StringComparison.Ordinal)) { @@ -201,6 +192,27 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) return IntPtr.Zero; } } + + /// + /// A resolver which allows a passed in from the compiler + /// to control assembly resolution. This is important because there are many exchange types + /// that need to unify across the multiple analyzer ALCs. These include common types from + /// Microsoft.CodeAnalysis.dll etc, as well as platform assemblies provided by a + /// host such as visual studio. + /// + /// + /// This resolver essentially forces any assembly that was loaded as a 'core' part of the + /// compiler to be shared across analyzers, and not loaded multiple times into each individual + /// analyzer ALC, even if the analyzer itself shipped a copy of said assembly. + /// + /// The that the core + /// compiler assemblies are already loaded into. + internal sealed class CompilerAnalyzerAssemblyResolver(AssemblyLoadContext compilerContext) : IAnalyzerAssemblyResolver + { + private readonly AssemblyLoadContext _compilerAlc = compilerContext; + + public Assembly? ResolveAssembly(AssemblyName assemblyName) => _compilerAlc.LoadFromAssemblyName(assemblyName); + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs index b4d000e335a9d..75d6271100354 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs @@ -5,6 +5,7 @@ #if !NETCOREAPP using System; +using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Reflection; @@ -28,8 +29,9 @@ internal partial class AnalyzerAssemblyLoader { private bool _hookedAssemblyResolve; - internal AnalyzerAssemblyLoader() + internal AnalyzerAssemblyLoader(ImmutableArray externalResolvers) { + _externalResolvers = externalResolvers; } public bool IsHostAssembly(Assembly assembly) @@ -57,6 +59,10 @@ public bool IsHostAssembly(Assembly assembly) private partial Assembly? Load(AssemblyName assemblyName, string assemblyOriginalPath) { EnsureResolvedHooked(); + if (ResolveAssemblyExternally(assemblyName) is { } externallyResolvedAssembly) + { + return externallyResolvedAssembly; + } return AppDomain.CurrentDomain.Load(assemblyName); } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs index 7df1f5fb6f81c..9b35df57eee87 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs @@ -68,6 +68,14 @@ internal abstract partial class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader /// private readonly Dictionary> _knownAssemblyPathsBySimpleName = new(StringComparer.OrdinalIgnoreCase); + /// + /// A collection of s that can be used to override the assembly resolution process. + /// + /// + /// When multiple resolvers are present they are consulted in-order, with the first resolver to return a non-null + /// winning. + private readonly ImmutableArray _externalResolvers; + /// /// The implementation needs to load an with the specified . The /// parameter is the original path. It may be different than @@ -340,5 +348,33 @@ internal string GetRealAnalyzerLoadPath(string originalFullPath) .ToArray(); } } + + /// + /// Iterates the if any, to see if any of them can resolve + /// the given to an . + /// + /// The name of the assembly to resolve + /// An if one of the resolvers is successful, or + internal Assembly? ResolveAssemblyExternally(AssemblyName assemblyName) + { + if (!_externalResolvers.IsDefaultOrEmpty) + { + foreach (var resolver in _externalResolvers) + { + try + { + if (resolver.ResolveAssembly(assemblyName) is { } resolvedAssembly) + { + return resolvedAssembly; + } + } + catch + { + // Ignore if the external resolver throws + } + } + } + return null; + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs index 624768168d521..b6233d02a6d36 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs @@ -17,13 +17,19 @@ namespace Microsoft.CodeAnalysis internal sealed class DefaultAnalyzerAssemblyLoader : AnalyzerAssemblyLoader { internal DefaultAnalyzerAssemblyLoader() + : base([]) + { + } + + internal DefaultAnalyzerAssemblyLoader(ImmutableArray externalResolvers) + : base(externalResolvers) { } #if NETCOREAPP - internal DefaultAnalyzerAssemblyLoader(System.Runtime.Loader.AssemblyLoadContext? compilerLoadContext = null, AnalyzerLoadOption loadOption = AnalyzerLoadOption.LoadFromDisk) - : base(compilerLoadContext, loadOption) + internal DefaultAnalyzerAssemblyLoader(System.Runtime.Loader.AssemblyLoadContext? compilerLoadContext = null, AnalyzerLoadOption loadOption = AnalyzerLoadOption.LoadFromDisk, ImmutableArray? externalResolvers = null) + : base(compilerLoadContext, loadOption, externalResolvers ?? []) { } @@ -51,12 +57,12 @@ protected override string PrepareSatelliteAssemblyToLoad(string assemblyFilePath /// /// A shadow copy path will be created on Windows and this value /// will be the base directory where shadow copy assemblies are stored. - internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string windowsShadowPath) + internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string windowsShadowPath, ImmutableArray? externalResolvers = null) { #if NETCOREAPP if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return new DefaultAnalyzerAssemblyLoader(loadOption: AnalyzerLoadOption.LoadFromStream); + return new DefaultAnalyzerAssemblyLoader(loadOption: AnalyzerLoadOption.LoadFromStream, externalResolvers: externalResolvers); } #endif @@ -68,7 +74,7 @@ internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string wi throw new ArgumentException("Must be a full path.", nameof(windowsShadowPath)); } - return new ShadowCopyAnalyzerAssemblyLoader(windowsShadowPath); + return new ShadowCopyAnalyzerAssemblyLoader(windowsShadowPath, externalResolvers); } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs new file mode 100644 index 0000000000000..b6bf2b029de28 --- /dev/null +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; + +namespace Microsoft.CodeAnalysis +{ + /// + /// Allows a host to override how assembly resolution is performed by the . + /// + internal interface IAnalyzerAssemblyResolver + { + /// + /// Attempts to resolve an assembly by name. + /// + /// The assembly to resolve + /// The resolved assembly, or + Assembly? ResolveAssembly(AssemblyName assemblyName); + } +} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs index 40592947a8ac4..9cc9ed5b29a33 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Threading.Tasks; using Roslyn.Utilities; +using System.Collections.Immutable; +using System.Reflection; #if NETCOREAPP using System.Runtime.Loader; @@ -42,15 +44,16 @@ internal sealed class ShadowCopyAnalyzerAssemblyLoader : AnalyzerAssemblyLoader internal int CopyCount => _mvidPathMap.Count; #if NETCOREAPP - public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory) - : this(null, baseDirectory) + public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) + : this(null, baseDirectory, externalResolvers) { } - public ShadowCopyAnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, string baseDirectory) - : base(compilerLoadContext, AnalyzerLoadOption.LoadFromDisk) + public ShadowCopyAnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, string baseDirectory, ImmutableArray? externalResolvers = null) + : base(compilerLoadContext, AnalyzerLoadOption.LoadFromDisk, externalResolvers ?? []) #else - public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory) + public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) + : base(externalResolvers ?? []) #endif { if (baseDirectory is null) diff --git a/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs b/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs new file mode 100644 index 0000000000000..85c36e8c54a15 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Diagnostics; +using System.Reflection; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Composition; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + [Export(typeof(IAnalyzerAssemblyResolver)), Shared] + internal class RazorAnalyzerAssemblyResolver : IAnalyzerAssemblyResolver + { + private static Func? s_assemblyResolver; + + /// + /// We use this as a heuristic to catch a case where we set the resolver too + /// late and the resolver has already been asked to resolve a razor assembly. + /// + /// Note this isn't perfectly accurate but is only used to trigger an assert + /// in debug builds. + /// + private static bool s_razorRequested = false; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RazorAnalyzerAssemblyResolver() + { + } + + internal static Func? AssemblyResolver + { + get => s_assemblyResolver; + set + { + Debug.Assert(s_assemblyResolver == null, "Assembly resolver should not be set multiple times."); + Debug.Assert(!s_razorRequested, "A razor assembly has already been requested before setting the resolver."); + s_assemblyResolver = value; + } + } + + public Assembly? ResolveAssembly(AssemblyName assemblyName) + { + s_razorRequested |= assemblyName.FullName.Contains("Razor"); + return s_assemblyResolver?.Invoke(assemblyName); + } + } +} diff --git a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs index d638391df2977..c42102f1d18db 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs @@ -704,7 +704,7 @@ private static AnalyzerFileReference CreateShadowCopiedAnalyzerReference(TempRoo return new AnalyzerFileReference(original, new MockShadowCopyAnalyzerAssemblyLoader(ImmutableDictionary.Empty.Add(original, shadow.Path))); } - private class MissingAnalyzerLoader : AnalyzerAssemblyLoader + private class MissingAnalyzerLoader() : AnalyzerAssemblyLoader([]) { protected override string PreparePathToLoad(string fullPath) => throw new FileNotFoundException(fullPath); diff --git a/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs b/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs index db82f43f5df66..9443bf285ee4d 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DefaultAnalyzerAssemblyLoaderService.cs @@ -5,31 +5,35 @@ using System; using System.Composition; using System.IO; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; +using System.Collections.Immutable; +using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Host; [ExportWorkspaceServiceFactory(typeof(IAnalyzerAssemblyLoaderProvider)), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class DefaultAnalyzerAssemblyLoaderServiceFactory() : IWorkspaceServiceFactory +internal sealed class DefaultAnalyzerAssemblyLoaderServiceFactory([ImportMany] IEnumerable externalResolvers) : IWorkspaceServiceFactory { public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new DefaultAnalyzerAssemblyLoaderProvider(workspaceServices.Workspace.Kind ?? "default"); + => new DefaultAnalyzerAssemblyLoaderProvider(workspaceServices.Workspace.Kind ?? "default", [.. externalResolvers]); - private sealed class DefaultAnalyzerAssemblyLoaderProvider(string workspaceKind) : IAnalyzerAssemblyLoaderProvider + private sealed class DefaultAnalyzerAssemblyLoaderProvider(string workspaceKind, ImmutableArray externalResolvers) : IAnalyzerAssemblyLoaderProvider { - private readonly DefaultAnalyzerAssemblyLoader _loader = new(); + private readonly DefaultAnalyzerAssemblyLoader _loader = new(externalResolvers); /// /// We include the of the workspace in the path we produce. That way we don't /// collide in the common case of a normal host workspace and OOP workspace running together. This avoids an /// annoying exception as each will try to clean up this directory, throwing exceptions because the other is - /// locking it. The exception is fine, since the cleanup is just hygenic and isn't intended to be needed for + /// locking it. The exception is fine, since the cleanup is just hygienic and isn't intended to be needed for /// correctness. But it is annoying and does cause noise in our perf test harness. /// private readonly IAnalyzerAssemblyLoader _shadowCopyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader( - Path.Combine(Path.GetTempPath(), "CodeAnalysis", "WorkspacesAnalyzerShadowCopies", workspaceKind)); + Path.Combine(Path.GetTempPath(), "CodeAnalysis", "WorkspacesAnalyzerShadowCopies", workspaceKind), + externalResolvers: externalResolvers); public IAnalyzerAssemblyLoader GetLoader(in AnalyzerAssemblyLoaderOptions options) => options.ShadowCopy ? _shadowCopyLoader : _loader; diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 2ef8b99744fab..39c411bd46163 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -39,6 +39,7 @@ + diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs index 7417225aaebba..78d093bdb1e57 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO; +using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Remote.Diagnostics { @@ -15,7 +16,8 @@ internal sealed class RemoteAnalyzerAssemblyLoader : AnalyzerAssemblyLoader { private readonly string _baseDirectory; - public RemoteAnalyzerAssemblyLoader(string baseDirectory) + public RemoteAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) + : base(externalResolvers ?? []) { _baseDirectory = baseDirectory; } diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs index d1046f8960c44..52bac4993d404 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs @@ -4,11 +4,13 @@ using System; using System.Composition; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Reflection; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Remote.Diagnostics { @@ -23,13 +25,14 @@ internal sealed class RemoteAnalyzerAssemblyLoaderService : IAnalyzerAssemblyLoa [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RemoteAnalyzerAssemblyLoaderService() + public RemoteAnalyzerAssemblyLoaderService([ImportMany] IEnumerable externalResolvers) { var baseDirectory = Path.GetDirectoryName(Path.GetFullPath(typeof(RemoteAnalyzerAssemblyLoader).GetTypeInfo().Assembly.Location)); Debug.Assert(baseDirectory != null); - _loader = new(baseDirectory); - _shadowCopyLoader = new(Path.Combine(Path.GetTempPath(), "VS", "AnalyzerAssemblyLoader")); + var resolvers = externalResolvers.ToImmutableArray(); + _loader = new(baseDirectory, resolvers); + _shadowCopyLoader = new(Path.Combine(Path.GetTempPath(), "VS", "AnalyzerAssemblyLoader"), resolvers); } public IAnalyzerAssemblyLoader GetLoader(in AnalyzerAssemblyLoaderOptions options) From 715d05e588048477e6e923c27a7f54df01a99633 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 9 May 2024 19:06:52 -0700 Subject: [PATCH 210/423] Fix issues with JTF blocking in codemodel --- .../Core/Impl/CodeModel/RootCodeModel.cs | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs index c412f1813e751..e6c12f4fef608 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs @@ -7,11 +7,13 @@ using System; using System.IO; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.ExternalElements; using Microsoft.VisualStudio.LanguageServices.Implementation.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel @@ -39,8 +41,8 @@ private RootCodeModel(CodeModelState state, EnvDTE.Project parent, ProjectId pro private Project GetProject() => Workspace.CurrentSolution.GetProject(_projectId); - private Compilation GetCompilation() - => GetProject().GetCompilationAsync().Result; + private Task GetCompilationAsync() + => GetProject().GetCompilationAsync(); private ComHandle GetFileCodeModel(object location) { @@ -127,27 +129,34 @@ EnvDTE.CodeElements ICodeElementContainer.GetCollec => CodeElements; public EnvDTE.CodeElements CodeElements - { - get + => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - var compilation = GetCompilation(); + var compilation = await GetCompilationAsync().ConfigureAwait(true); + + // Need to ensure we're on the UI thread to make the ExternalCodeNamespace. It creates UI thread bound + // com aggregates. + await this.State.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); var rootNamespace = ExternalCodeNamespace.Create(this.State, _projectId, compilation.GlobalNamespace); return rootNamespace.Members; - } - } + }); public EnvDTE.CodeType CodeTypeFromFullName(string name) - { - var compilation = GetCompilation(); - var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(name, compilation); - if (typeSymbol == null || - typeSymbol.TypeKind is TypeKind.Error or TypeKind.Unknown) + => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - return null; - } + var compilation = await GetCompilationAsync().ConfigureAwait(false); + var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(name, compilation); + if (typeSymbol == null || + typeSymbol.TypeKind is TypeKind.Error or TypeKind.Unknown) + { + return null; + } - return (EnvDTE.CodeType)CodeModelService.CreateCodeType(this.State, _projectId, typeSymbol); - } + // Need to ensure we're on the UI thread to make the call to CreateCodeType. It creates UI thread + // bound com aggregates. + await this.State.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + + return (EnvDTE.CodeType)CodeModelService.CreateCodeType(this.State, _projectId, typeSymbol); + }); public EnvDTE.CodeTypeRef CreateCodeTypeRef(object type) => CodeModelService.CreateCodeTypeRef(this.State, _projectId, type); @@ -159,16 +168,15 @@ public void Remove(object element) => throw Exceptions.ThrowENotImpl(); public string DotNetNameFromLanguageSpecific(string languageName) - { - var compilation = GetCompilation(); - var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(languageName, compilation); - if (typeSymbol == null) + => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - throw Exceptions.ThrowEInvalidArg(); - } + var compilation = await GetCompilationAsync().ConfigureAwait(false); + var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(languageName, compilation); + if (typeSymbol == null) + throw Exceptions.ThrowEInvalidArg(); - return MetadataNameHelpers.GetMetadataName(typeSymbol); - } + return MetadataNameHelpers.GetMetadataName(typeSymbol); + }; public EnvDTE.CodeElement ElementFromID(string id) => throw Exceptions.ThrowENotImpl(); From d52ab04b8ada2587ca9908bbe8697c5fa792f581 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 9 May 2024 19:15:20 -0700 Subject: [PATCH 211/423] Update src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs --- src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs index e6c12f4fef608..4a01420cb66c1 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs @@ -133,7 +133,7 @@ public EnvDTE.CodeElements CodeElements { var compilation = await GetCompilationAsync().ConfigureAwait(true); - // Need to ensure we're on the UI thread to make the ExternalCodeNamespace. It creates UI thread bound + // Need to ensure we're on the UI thread to make the ExternalCodeNamespace. It creates UI thread bound // com aggregates. await this.State.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); var rootNamespace = ExternalCodeNamespace.Create(this.State, _projectId, compilation.GlobalNamespace); From f51227682b72692878b6232a1fc86b57429a77a2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 9 May 2024 19:19:13 -0700 Subject: [PATCH 212/423] CA(true) --- src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs index e6c12f4fef608..e289b8454ecbe 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs @@ -143,7 +143,7 @@ public EnvDTE.CodeElements CodeElements public EnvDTE.CodeType CodeTypeFromFullName(string name) => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - var compilation = await GetCompilationAsync().ConfigureAwait(false); + var compilation = await GetCompilationAsync().ConfigureAwait(true); var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(name, compilation); if (typeSymbol == null || typeSymbol.TypeKind is TypeKind.Error or TypeKind.Unknown) @@ -170,7 +170,7 @@ public void Remove(object element) public string DotNetNameFromLanguageSpecific(string languageName) => this.State.ThreadingContext.JoinableTaskFactory.Run(async () => { - var compilation = await GetCompilationAsync().ConfigureAwait(false); + var compilation = await GetCompilationAsync().ConfigureAwait(true); var typeSymbol = CodeModelService.GetTypeSymbolFromFullName(languageName, compilation); if (typeSymbol == null) throw Exceptions.ThrowEInvalidArg(); From d4febc4b8bdadb81ff95d672ecdd3e1f9ec86a48 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 9 May 2024 19:32:53 -0700 Subject: [PATCH 213/423] fix --- src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs index fad132a45d7e5..415f396d8f642 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/RootCodeModel.cs @@ -176,7 +176,7 @@ public string DotNetNameFromLanguageSpecific(string languageName) throw Exceptions.ThrowEInvalidArg(); return MetadataNameHelpers.GetMetadataName(typeSymbol); - }; + }); public EnvDTE.CodeElement ElementFromID(string id) => throw Exceptions.ThrowENotImpl(); From 18614bceb9d0a9eda5cfb47409c3058971cc6751 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 10 May 2024 10:44:49 +0200 Subject: [PATCH 214/423] Continue visiting bad named argument if not target-typed in NullableWalker (#73326) * Add a test * Continue visiting bad named argument if not target-typed * Simplify an assert --- .../Portable/FlowAnalysis/NullableWalker.cs | 44 ++++++++++++------- .../Semantics/NullableReferenceTypesTests.cs | 40 +++++++++++++++++ 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 2abba14887c5d..c139e3daf28c4 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6719,27 +6719,26 @@ private ImmutableArray VisitArguments( (ParameterSymbol? parameter, TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, bool isExpandedParamsArgument) = GetCorrespondingParameter(i, parametersOpt, argsToParamsOpt, expanded, ref paramsIterationType); - if (// This is known to happen for certain error scenarios, because - // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` - // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. - parameter is null || - // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type - // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. - node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true }) + // This is known to happen for certain error scenarios, because + // the parameter matching logic above is not as flexible as the one we use in `Binder.BuildArgumentsForErrorRecovery` + // so we may end up with a pending conversion completion for an argument apparently without a corresponding parameter. + if (parameter is null) { - if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) + if (tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) { - // We've done something wrong if we have a target-typed expression and registered an analysis continuation for it - // (we won't be able to complete that continuation) - // We flush the completion with a plausible/dummy type and remove it. - completion(TypeWithAnnotations.Create(argument.Type)); - TargetTypedAnalysisCompletion.Remove(argumentNoConversion); - - Debug.Assert(parameter is not null || method is ErrorMethodSymbol); + Debug.Assert(method is ErrorMethodSymbol); } continue; } + // In error recovery with named arguments, target-typing cannot work as we can get a different parameter type + // from our GetCorrespondingParameter logic than Binder.BuildArgumentsForErrorRecovery does. + if (node is BoundCall { HasErrors: true, ArgumentNamesOpt.IsDefaultOrEmpty: false, ArgsToParamsOpt.IsDefault: true } && + tryShortCircuitTargetTypedExpression(argument, argumentNoConversion)) + { + continue; + } + // We disable diagnostics when: // 1. the containing call has errors (to reduce cascading diagnostics) // 2. on implicit default arguments (since that's really only an issue with the declaration) @@ -6926,6 +6925,21 @@ static void expandParamsCollection(ref ImmutableArray arguments } } } + + bool tryShortCircuitTargetTypedExpression(BoundExpression argument, BoundExpression argumentNoConversion) + { + if (IsTargetTypedExpression(argumentNoConversion) && _targetTypedAnalysisCompletionOpt?.TryGetValue(argumentNoConversion, out var completion) is true) + { + // We've done something wrong if we have a target-typed expression and registered an analysis continuation for it + // (we won't be able to complete that continuation) + // We flush the completion with a plausible/dummy type and remove it. + completion(TypeWithAnnotations.Create(argument.Type)); + TargetTypedAnalysisCompletion.Remove(argumentNoConversion); + return true; + } + + return false; + } } private void ApplyMemberPostConditions(BoundExpression? receiverOpt, MethodSymbol? method) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index beb0e75552fa3..11f632233060e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -158691,5 +158691,45 @@ static void M() where T : struct, I // y.Value.Item.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y.Value.Item").WithLocation(15, 9)); } + + [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2045970")] + public void BadAsyncLambdaInNamedArgument() + { + var source = """ + #nullable enable + using System; + using System.Threading.Tasks; + + class D + { + void M() + { + var c1 = new C(); + var c2 = new C(); + C.M1(f: async () => + { + if (c2 != null) + { + c1. + await c2.M2(); + } + }); + } + } + + class C + { + public static void M1(Func f) { } + public async Task M2() => await Task.Yield(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (15,20): error CS1001: Identifier expected + // c1. + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(15, 20), + // (15,20): error CS1002: ; expected + // c1. + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(15, 20)); + } } } From e754fb91805b716a68c5c0096fcf5df86c7a18ef Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:10:41 -0700 Subject: [PATCH 215/423] Switch to immutable array --- .../LinkedFileDiffMergingSession.cs | 2 +- .../LinkedFileDiffMerging/LinkedFileMergeResult.cs | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 59464d6c0f4fb..e2e7a3a16bf30 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -62,7 +62,7 @@ internal async Task MergeDiffsAsync(IMergeConflict } private async Task MergeLinkedDocumentGroupAsync( - IEnumerable allLinkedDocuments, + ImmutableArray allLinkedDocuments, IEnumerable linkedDocumentGroup, LinkedFileDiffMergingSessionInfo sessionInfo, IMergeConflictHandler mergeConflictHandler, diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs index a6a2fdea585d9..2286c2799d633 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs @@ -2,18 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; -internal sealed class LinkedFileMergeResult(IEnumerable documentIds, SourceText mergedSourceText, IEnumerable mergeConflictResolutionSpans) +internal sealed class LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, IEnumerable mergeConflictResolutionSpans) { - public IEnumerable DocumentIds { get; internal set; } = documentIds; - public SourceText MergedSourceText { get; internal set; } = mergedSourceText; - public IEnumerable MergeConflictResolutionSpans { get; } = mergeConflictResolutionSpans; - public bool HasMergeConflicts { get { return MergeConflictResolutionSpans.Any(); } } + public ImmutableArray DocumentIds => documentIds; + public SourceText MergedSourceText => mergedSourceText; + public IEnumerable MergeConflictResolutionSpans => mergeConflictResolutionSpans; + public bool HasMergeConflicts => MergeConflictResolutionSpans.Any(); } From f876667674f8c9a0ed23ddffca1a96e6df10cff9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:23:27 -0700 Subject: [PATCH 216/423] Switch to line changes --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index e2e7a3a16bf30..9c8dbb49ab13a 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -73,7 +73,8 @@ private async Task MergeLinkedDocumentGroupAsync( // Automatically merge non-conflicting diffs while collecting the conflicting diffs var textDifferencingService = oldSolution.Services.GetRequiredService(); - var appliedChanges = await textDifferencingService.GetTextChangesAsync(oldSolution.GetDocument(linkedDocumentGroup.First()), newSolution.GetDocument(linkedDocumentGroup.First()), cancellationToken).ConfigureAwait(false); + var appliedChanges = await textDifferencingService.GetTextChangesAsync( + oldSolution.GetDocument(linkedDocumentGroup.First()), newSolution.GetDocument(linkedDocumentGroup.First()), TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); var unmergedChanges = new List(); foreach (var documentId in linkedDocumentGroup.Skip(1)) @@ -129,8 +130,9 @@ private static async Task> AddDocumentMergeChangesAsy var cumulativeChangeIndex = 0; - var textchanges = await textDiffService.GetTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); - foreach (var change in textchanges) + var textChanges = await textDiffService.GetTextChangesAsync( + oldDocument, newDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); + foreach (var change in textChanges) { while (cumulativeChangeIndex < cumulativeChanges.Count && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start) { From 9b3fe96d35717af6ca3db4365a26c4954df001a2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:24:17 -0700 Subject: [PATCH 217/423] Switch to list --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 9c8dbb49ab13a..656d26efb5663 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -95,9 +95,9 @@ private async Task MergeLinkedDocumentGroupAsync( // Add comments in source explaining diffs that could not be merged IEnumerable allChanges; - IList mergeConflictResolutionSpan = []; + var mergeConflictResolutionSpan = new List(); - if (unmergedChanges.Any()) + if (unmergedChanges.Count != 0) { mergeConflictHandler ??= oldSolution.GetDocument(linkedDocumentGroup.First()).GetLanguageService(); var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(originalSourceText, unmergedChanges); @@ -212,7 +212,7 @@ private static async Task> AddDocumentMergeChangesAsy private static IEnumerable MergeChangesWithMergeFailComments( IEnumerable mergedChanges, IEnumerable commentChanges, - IList mergeConflictResolutionSpans, + List mergeConflictResolutionSpans, LinkedFileGroupSessionInfo groupSessionInfo) { var mergedChangesList = NormalizeChanges(mergedChanges).ToList(); From 33b9685d62c2e4a4283cc0029aebac948cd4b975 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:24:59 -0700 Subject: [PATCH 218/423] Switch to list --- .../Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs | 6 +++--- .../LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs index 2286c2799d633..95f2981efafe3 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs @@ -9,10 +9,10 @@ namespace Microsoft.CodeAnalysis; -internal sealed class LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, IEnumerable mergeConflictResolutionSpans) +internal sealed class LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, List mergeConflictResolutionSpans) { public ImmutableArray DocumentIds => documentIds; public SourceText MergedSourceText => mergedSourceText; - public IEnumerable MergeConflictResolutionSpans => mergeConflictResolutionSpans; - public bool HasMergeConflicts => MergeConflictResolutionSpans.Any(); + public List MergeConflictResolutionSpans => mergeConflictResolutionSpans; + public bool HasMergeConflicts => MergeConflictResolutionSpans.Count != 0; } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs index fd532034cbb68..79c26734cabe9 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs @@ -16,7 +16,7 @@ internal sealed class LinkedFileMergeSessionResult private readonly Dictionary> _mergeConflictCommentSpans = []; public Dictionary> MergeConflictCommentSpans => _mergeConflictCommentSpans; - public LinkedFileMergeSessionResult(Solution mergedSolution, IEnumerable fileMergeResults) + public LinkedFileMergeSessionResult(Solution mergedSolution, List fileMergeResults) { this.MergedSolution = mergedSolution; From f9ac16baa4d920f35dd9d2b7cbc010b3617b1678 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:26:25 -0700 Subject: [PATCH 219/423] Simplify --- .../LinkedFileMergeSessionResult.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs index 79c26734cabe9..7a7df3860c01c 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; @@ -13,8 +11,7 @@ internal sealed class LinkedFileMergeSessionResult { public Solution MergedSolution { get; } - private readonly Dictionary> _mergeConflictCommentSpans = []; - public Dictionary> MergeConflictCommentSpans => _mergeConflictCommentSpans; + private readonly Dictionary> MergeConflictCommentSpans = []; public LinkedFileMergeSessionResult(Solution mergedSolution, List fileMergeResults) { @@ -23,9 +20,7 @@ public LinkedFileMergeSessionResult(Solution mergedSolution, List Date: Fri, 10 May 2024 09:27:35 -0700 Subject: [PATCH 220/423] Use array --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 +++--- .../LinkedFileDiffMerging/UnmergedDocumentChanges.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 656d26efb5663..a382064909d81 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -198,10 +198,10 @@ private static async Task> AddDocumentMergeChangesAsy groupSessionInfo.IsolatedDiffs++; } - if (unmergedDocumentChanges.Any()) + if (unmergedDocumentChanges.Count != 0) { unmergedChanges.Add(new UnmergedDocumentChanges( - unmergedDocumentChanges.AsEnumerable(), + unmergedDocumentChanges, oldDocument.Project.Name, oldDocument.Id)); } @@ -210,7 +210,7 @@ private static async Task> AddDocumentMergeChangesAsy } private static IEnumerable MergeChangesWithMergeFailComments( - IEnumerable mergedChanges, + ImmutableArray mergedChanges, IEnumerable commentChanges, List mergeConflictResolutionSpans, LinkedFileGroupSessionInfo groupSessionInfo) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs index a5586170c71fa..7eb76021e7667 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs @@ -9,9 +9,9 @@ namespace Microsoft.CodeAnalysis; -internal sealed class UnmergedDocumentChanges(IEnumerable unmergedChanges, string projectName, DocumentId documentId) +internal sealed class UnmergedDocumentChanges(List unmergedChanges, string projectName, DocumentId documentId) { - public IEnumerable UnmergedChanges { get; } = unmergedChanges; + public List UnmergedChanges { get; } = unmergedChanges; public string ProjectName { get; } = projectName; public DocumentId DocumentId { get; } = documentId; } From 7ece11ae4f01f586f8907010741daa6696ec1add Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:27:59 -0700 Subject: [PATCH 221/423] Stronger type --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index a382064909d81..0677224d4d3fe 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -63,7 +63,7 @@ internal async Task MergeDiffsAsync(IMergeConflict private async Task MergeLinkedDocumentGroupAsync( ImmutableArray allLinkedDocuments, - IEnumerable linkedDocumentGroup, + IGrouping linkedDocumentGroup, LinkedFileDiffMergingSessionInfo sessionInfo, IMergeConflictHandler mergeConflictHandler, CancellationToken cancellationToken) From 636542190d3a945751943ea920e583b93688e5f1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:28:33 -0700 Subject: [PATCH 222/423] builder --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 0677224d4d3fe..5322e76c03ed3 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -126,7 +126,7 @@ private static async Task> AddDocumentMergeChangesAsy CancellationToken cancellationToken) { var unmergedDocumentChanges = new List(); - var successfullyMergedChanges = ArrayBuilder.GetInstance(); + using var _ = ArrayBuilder.GetInstance(out var successfullyMergedChanges); var cumulativeChangeIndex = 0; @@ -206,7 +206,7 @@ private static async Task> AddDocumentMergeChangesAsy oldDocument.Id)); } - return successfullyMergedChanges.ToImmutableAndFree(); + return successfullyMergedChanges.ToImmutable(); } private static IEnumerable MergeChangesWithMergeFailComments( From 3219183f2d10b2dfb6fb1bf818a682901e4a7f9d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:32:35 -0700 Subject: [PATCH 223/423] Get off ienumerables --- ...FileMergeConflictCommentAdditionService.cs | 2 +- .../IMergeConflictHandler.cs | 4 +--- .../LinkedFileDiffMergingSession.cs | 20 +++++++++---------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs index 2d7adffa24ae2..196f50fa7a585 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs @@ -16,7 +16,7 @@ internal abstract class AbstractLinkedFileMergeConflictCommentAdditionService : { internal abstract string GetConflictCommentText(string header, string beforeString, string afterString); - public IEnumerable CreateEdits(SourceText originalSourceText, IEnumerable unmergedChanges) + public List CreateEdits(SourceText originalSourceText, IEnumerable unmergedChanges) { var commentChanges = new List(); diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs index e5c4f81e1d986..e6f12be528fa9 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; @@ -11,5 +9,5 @@ namespace Microsoft.CodeAnalysis; internal interface IMergeConflictHandler { - IEnumerable CreateEdits(SourceText originalSourceText, IEnumerable unmergedChanges); + List CreateEdits(SourceText originalSourceText, List unmergedChanges); } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 5322e76c03ed3..0fad1ef569d8e 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -94,7 +94,7 @@ private async Task MergeLinkedDocumentGroupAsync( // Add comments in source explaining diffs that could not be merged - IEnumerable allChanges; + ImmutableArray allChanges; var mergeConflictResolutionSpan = new List(); if (unmergedChanges.Count != 0) @@ -209,14 +209,14 @@ private static async Task> AddDocumentMergeChangesAsy return successfullyMergedChanges.ToImmutable(); } - private static IEnumerable MergeChangesWithMergeFailComments( + private static ImmutableArray MergeChangesWithMergeFailComments( ImmutableArray mergedChanges, - IEnumerable commentChanges, + List commentChanges, List mergeConflictResolutionSpans, LinkedFileGroupSessionInfo groupSessionInfo) { - var mergedChangesList = NormalizeChanges(mergedChanges).ToList(); - var commentChangesList = NormalizeChanges(commentChanges).ToList(); + var mergedChangesList = NormalizeChanges(mergedChanges); + var commentChangesList = NormalizeChanges(commentChanges); var combinedChanges = new List(); var insertedMergeConflictCommentsAtAdjustedLocation = 0; @@ -272,17 +272,15 @@ private static IEnumerable MergeChangesWithMergeFailComments( groupSessionInfo.InsertedMergeConflictComments = commentChanges.Count(); groupSessionInfo.InsertedMergeConflictCommentsAtAdjustedLocation = insertedMergeConflictCommentsAtAdjustedLocation; - return NormalizeChanges(combinedChanges); + return NormalizeChanges(combinedChanges).ToImmutableArray(); } - private static IEnumerable NormalizeChanges(IEnumerable changes) + private static IList NormalizeChanges(IList changes) { - if (changes.Count() <= 1) - { + if (changes.Count <= 1) return changes; - } - changes = changes.OrderBy(c => c.Span.Start); + var orderedChanges = changes.OrderBy(c => c.Span.Start).ToList(); var normalizedChanges = new List(); var currentChange = changes.First(); From 5c583598eb19923a7ad65676021f94537e9d89ba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:33:33 -0700 Subject: [PATCH 224/423] Downstream --- ...AbstractLinkedFileMergeConflictCommentAdditionService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs index 196f50fa7a585..e8599a8880c70 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs @@ -16,7 +16,7 @@ internal abstract class AbstractLinkedFileMergeConflictCommentAdditionService : { internal abstract string GetConflictCommentText(string header, string beforeString, string afterString); - public List CreateEdits(SourceText originalSourceText, IEnumerable unmergedChanges) + public List CreateEdits(SourceText originalSourceText, List unmergedChanges) { var commentChanges = new List(); @@ -31,9 +31,9 @@ public List CreateEdits(SourceText originalSourceText, IEnumerable> PartitionChangesForDocument(IEnumerable changes, SourceText originalSourceText) + private static List> PartitionChangesForDocument(IEnumerable changes, SourceText originalSourceText) { - var partitionedChanges = new List>(); + var partitionedChanges = new List>(); var currentPartition = new List { changes.First() From 138049f0803588139e8c38f94c6046ec3f91c2a0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 09:40:47 -0700 Subject: [PATCH 225/423] Fix accessibility --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 2 +- .../LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 0fad1ef569d8e..bf51b46cc269a 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -309,7 +309,7 @@ public void LogLinkedFileResult(LinkedFileGroupSessionInfo info) => LinkedFileGroups.Add(info); } - internal class LinkedFileGroupSessionInfo + internal sealed class LinkedFileGroupSessionInfo { public int LinkedDocuments; public int DocumentsWithChanges; diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs index 7a7df3860c01c..a1923f500c2c9 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs @@ -11,7 +11,7 @@ internal sealed class LinkedFileMergeSessionResult { public Solution MergedSolution { get; } - private readonly Dictionary> MergeConflictCommentSpans = []; + public readonly Dictionary> MergeConflictCommentSpans = []; public LinkedFileMergeSessionResult(Solution mergedSolution, List fileMergeResults) { From f754a6c94e6eddb14fa33d7829466b5c76e3c033 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 10:45:12 -0700 Subject: [PATCH 226/423] Avoid LOH allocation in text synchronizatin --- .../Core/Remote/SolutionChecksumUpdater.cs | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 805117ccedac8..8415d70192f11 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -205,45 +205,34 @@ private async ValueTask SynchronizeTextChangesAsync( async ValueTask SynchronizeTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { - // this pushes text changes to the remote side if it can. - // this is purely perf optimization. whether this pushing text change - // worked or not doesn't affect feature's functionality. + // this pushes text changes to the remote side if it can. this is purely perf optimization. whether this + // pushing text change worked or not doesn't affect feature's functionality. // - // this basically see whether it can cheaply find out text changes - // between 2 snapshots, if it can, it will send out that text changes to - // remote side. + // this basically see whether it can cheaply find out text changes between 2 snapshots, if it can, it will + // send out that text changes to remote side. // - // the remote side, once got the text change, will again see whether - // it can use that text change information without any high cost and - // create new snapshot from it. + // the remote side, once got the text change, will again see whether it can use that text change information + // without any high cost and create new snapshot from it. // - // otherwise, it will do the normal behavior of getting full text from - // VS side. this optimization saves times we need to do full text - // synchronization for typing scenario. + // otherwise, it will do the normal behavior of getting full text from VS side. this optimization saves + // times we need to do full text synchronization for typing scenario. - if ((oldDocument.TryGetText(out var oldText) == false) || - (newDocument.TryGetText(out var newText) == false)) + if (!oldDocument.TryGetText(out var oldText) || + !newDocument.TryGetText(out var newText)) { // we only support case where text already exist return; } - // get text changes - var textChanges = newText.GetTextChanges(oldText).AsImmutable(); - if (textChanges.Length == 0) - { - // no changes - return; - } + // Avoid allocating text before seeing if we can bail out. + var changeRanges = newText.GetChangeRanges(oldText).AsImmutable(); - // whole document case - if (textChanges.Length == 1 && textChanges[0].Span.Length == oldText.Length) - { - // no benefit here. pulling from remote host is more efficient + // no benefit here. pulling from remote host is more efficient + if (changeRanges is [{ Span.Length: var singleChangeLength }] && singleChangeLength == oldText.Length) return; - } - // only cancelled when remote host gets shutdown + var textChanges = newText.GetTextChanges(oldText).AsImmutable(); + var client = await RemoteHostClient.TryGetClientAsync(_workspace, cancellationToken).ConfigureAwait(false); if (client == null) return; From ddad6247f65294db09d1ad13c2ec8bc1f04885bc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 10:59:44 -0700 Subject: [PATCH 227/423] Add back length check --- src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs index 8415d70192f11..d0b1146c9362b 100644 --- a/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs +++ b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs @@ -226,6 +226,8 @@ async ValueTask SynchronizeTextChangesAsync(Document oldDocument, Document newDo // Avoid allocating text before seeing if we can bail out. var changeRanges = newText.GetChangeRanges(oldText).AsImmutable(); + if (changeRanges.Length == 0) + return; // no benefit here. pulling from remote host is more efficient if (changeRanges is [{ Span.Length: var singleChangeLength }] && singleChangeLength == oldText.Length) From 7c563f6526e225569e22ffe6ad9f9baa80f74ada Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:13:57 -0700 Subject: [PATCH 228/423] Update src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index bf51b46cc269a..fde4ab7c52329 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -206,7 +206,7 @@ private static async Task> AddDocumentMergeChangesAsy oldDocument.Id)); } - return successfullyMergedChanges.ToImmutable(); + return successfullyMergedChanges.ToImmutableAndClear(); } private static ImmutableArray MergeChangesWithMergeFailComments( From 6244809ce246d186b799d6860622798705ce2706 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:16:36 -0700 Subject: [PATCH 229/423] nrt --- .../Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs index 7eb76021e7667..f205b4ddf473f 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; From 6f8a46b3f6bfa16c45266ae0f4e62508408e75b2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:42:08 -0700 Subject: [PATCH 230/423] Fixup encapsulate field --- .../CSharpEncapsulateFieldService.cs | 2 +- .../AbstractEncapsulateFieldService.cs | 82 ++++++------------- .../VisualBasicEncapsulateFieldService.vb | 3 +- .../LinkedFileDiffMergingSession.cs | 2 +- 4 files changed, 30 insertions(+), 59 deletions(-) diff --git a/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs b/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs index f9c34dedd824e..d52d8ba83cd72 100644 --- a/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs +++ b/src/Features/CSharp/Portable/EncapsulateField/CSharpEncapsulateFieldService.cs @@ -203,6 +203,6 @@ protected static string MakeUnique(string baseName, INamedTypeSymbol containingT return NameGenerator.GenerateUniqueName(baseName, containingTypeMemberNames.ToSet(), StringComparer.Ordinal); } - internal override IEnumerable GetConstructorNodes(INamedTypeSymbol containingType) + protected override IEnumerable GetConstructorNodes(INamedTypeSymbol containingType) => containingType.Constructors.SelectMany(c => c.DeclaringSyntaxReferences.Select(d => d.GetSyntax())); } diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index 5715e2012bebf..dd691fdb404ac 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -21,7 +21,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Rename; -using Microsoft.CodeAnalysis.Rename.ConflictEngine; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; @@ -33,6 +32,7 @@ internal abstract partial class AbstractEncapsulateFieldService : ILanguageServi { protected abstract Task RewriteFieldNameAndAccessibilityAsync(string originalFieldName, bool makePrivate, Document document, SyntaxAnnotation declarationAnnotation, CodeAndImportGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken); protected abstract Task> GetFieldsAsync(Document document, TextSpan span, CancellationToken cancellationToken); + protected abstract IEnumerable GetConstructorNodes(INamedTypeSymbol containingType); public async Task EncapsulateFieldsInSpanAsync(Document document, TextSpan span, CleanCodeGenerationOptionsProvider fallbackOptions, bool useDefaultBehavior, CancellationToken cancellationToken) { @@ -74,19 +74,16 @@ public async Task> GetEncapsulateFieldCodeActionsAsyn } private ImmutableArray EncapsulateAllFields(Document document, ImmutableArray fields, CleanCodeGenerationOptionsProvider fallbackOptions) - { - return - [ + => [ CodeAction.Create( - FeaturesResources.Encapsulate_fields_and_use_property, - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), - nameof(FeaturesResources.Encapsulate_fields_and_use_property)), + FeaturesResources.Encapsulate_fields_and_use_property, + c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), + nameof(FeaturesResources.Encapsulate_fields_and_use_property)), CodeAction.Create( FeaturesResources.Encapsulate_fields_but_still_use_field, c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, c), nameof(FeaturesResources.Encapsulate_fields_but_still_use_field)), ]; - } private ImmutableArray EncapsulateOneField(Document document, IFieldSymbol field, CleanCodeGenerationOptionsProvider fallbackOptions) { @@ -94,9 +91,9 @@ private ImmutableArray EncapsulateOneField(Document document, IField return [ CodeAction.Create( - string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, field.Name), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), - nameof(FeaturesResources.Encapsulate_field_colon_0_and_use_property) + "_" + field.Name), + string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, field.Name), + c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), + nameof(FeaturesResources.Encapsulate_field_colon_0_and_use_property) + "_" + field.Name), CodeAction.Create( string.Format(FeaturesResources.Encapsulate_field_colon_0_but_still_use_field, field.Name), c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, c), @@ -182,23 +179,6 @@ private async Task EncapsulateFieldAsync( fieldDeclaration.GetSyntax(cancellationToken).WithAdditionalAnnotations(declarationAnnotation))); var solution = document.Project.Solution; - - foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) - { - var linkedDocument = solution.GetDocument(linkedDocumentId); - var linkedRoot = await linkedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var linkedFieldNode = linkedRoot.FindNode(fieldDeclaration.Span); - if (linkedFieldNode.Span != fieldDeclaration.Span) - { - continue; - } - - var updatedRoot = linkedRoot.ReplaceNode(linkedFieldNode, linkedFieldNode.WithAdditionalAnnotations(declarationAnnotation)); - solution = solution.WithDocumentSyntaxRoot(linkedDocumentId, updatedRoot); - } - - document = solution.GetDocument(document.Id); - // Resolve the annotated symbol and prepare for rename. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -220,18 +200,6 @@ private async Task EncapsulateFieldAsync( document = await Formatter.FormatAsync(document.WithSyntaxRoot(rewrittenFieldDeclaration), Formatter.Annotation, formattingOptions, cancellationToken).ConfigureAwait(false); - solution = document.Project.Solution; - foreach (var linkedDocumentId in document.GetLinkedDocumentIds()) - { - var linkedDocument = solution.GetDocument(linkedDocumentId); - var linkedDocumentFormattingOptions = await linkedDocument.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); - var updatedLinkedRoot = await RewriteFieldNameAndAccessibilityAsync(finalFieldName, markFieldPrivate, linkedDocument, declarationAnnotation, fallbackOptions, cancellationToken).ConfigureAwait(false); - var updatedLinkedDocument = await Formatter.FormatAsync(linkedDocument.WithSyntaxRoot(updatedLinkedRoot), Formatter.Annotation, linkedDocumentFormattingOptions, cancellationToken).ConfigureAwait(false); - solution = updatedLinkedDocument.Project.Solution; - } - - document = solution.GetDocument(document.Id); - semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var newRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -262,11 +230,13 @@ private async Task UpdateReferencesAsync( bool updateReferences, Solution solution, Document document, IFieldSymbol field, string finalFieldName, string generatedPropertyName, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) { if (!updateReferences) - { return solution; - } var projectId = document.Project.Id; + var linkedDocumentIds = document.GetLinkedDocumentIds(); + using var _ = PooledHashSet.GetInstance(out var linkedProjectIds); + linkedProjectIds.AddRange(linkedDocumentIds.Select(d => d.ProjectId)); + if (field.IsReadOnly) { // Inside the constructor we want to rename references the field to the final field name. @@ -274,9 +244,8 @@ private async Task UpdateReferencesAsync( if (finalFieldName != field.Name && constructorLocations.Count > 0) { solution = await RenameAsync( - solution, field, finalFieldName, - (docId, span) => IntersectsWithAny(docId, span, constructorLocations), - fallbackOptions, + solution, field, finalFieldName, fallbackOptions, linkedProjectIds, + filter: (docId, span) => IntersectsWithAny(docId, span, constructorLocations), cancellationToken).ConfigureAwait(false); document = solution.GetDocument(document.Id); @@ -288,16 +257,17 @@ private async Task UpdateReferencesAsync( // Outside the constructor we want to rename references to the field to final property name. return await RenameAsync( - solution, field, generatedPropertyName, - (documentId, span) => !IntersectsWithAny(documentId, span, constructorLocations), - fallbackOptions, + solution, field, generatedPropertyName, fallbackOptions, linkedProjectIds, + filter: (documentId, span) => !IntersectsWithAny(documentId, span, constructorLocations), cancellationToken).ConfigureAwait(false); } else { // Just rename everything. - return await Renamer.RenameSymbolAsync( - solution, field, new SymbolRenameOptions(), generatedPropertyName, cancellationToken).ConfigureAwait(false); + return await RenameAsync( + solution, field, generatedPropertyName, fallbackOptions, linkedProjectIds, + filter: static (documentId, span) => true, + cancellationToken).ConfigureAwait(false); } } @@ -305,8 +275,9 @@ private static async Task RenameAsync( Solution solution, IFieldSymbol field, string finalName, - Func filter, CodeCleanupOptionsProvider fallbackOptions, + HashSet linkedProjectIds, + Func filter, CancellationToken cancellationToken) { var options = new SymbolRenameOptions( @@ -318,8 +289,11 @@ private static async Task RenameAsync( var initialLocations = await Renamer.FindRenameLocationsAsync( solution, field, options, cancellationToken).ConfigureAwait(false); - var resolution = await initialLocations.Filter(filter).ResolveConflictsAsync( - field, finalName, nonConflictSymbolKeys: default, fallbackOptions, cancellationToken).ConfigureAwait(false); + // Ensure we don't update any files in projects linked to us. That will be taken care of automatically when we + // edit the files in the current project + var resolution = await initialLocations + .Filter((documentId, span) => !linkedProjectIds.Contains(documentId.ProjectId) && filter(documentId, span)) + .ResolveConflictsAsync(field, finalName, nonConflictSymbolKeys: default, fallbackOptions, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(resolution.IsSuccessful); @@ -343,8 +317,6 @@ private static bool IntersectsWithAny(DocumentId documentId, TextSpan span, ISet private ISet<(DocumentId documentId, TextSpan span)> GetConstructorLocations(Solution solution, INamedTypeSymbol containingType) => GetConstructorNodes(containingType).Select(n => (solution.GetRequiredDocument(n.SyntaxTree).Id, n.Span)).ToSet(); - internal abstract IEnumerable GetConstructorNodes(INamedTypeSymbol containingType); - protected static async Task AddPropertyAsync( Document document, Solution destinationSolution, diff --git a/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb b/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb index d34283af4824e..2c494217af6cb 100644 --- a/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb +++ b/src/Features/VisualBasic/Portable/EncapsulateField/VisualBasicEncapsulateFieldService.vb @@ -130,11 +130,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EncapsulateField Return NameGenerator.GenerateUniqueName(propertyName, containingTypeMemberNames.ToSet(), StringComparer.OrdinalIgnoreCase) End Function - Friend Overrides Function GetConstructorNodes(containingType As INamedTypeSymbol) As IEnumerable(Of SyntaxNode) + Protected Overrides Function GetConstructorNodes(containingType As INamedTypeSymbol) As IEnumerable(Of SyntaxNode) Return containingType.Constructors.SelectMany(Function(c As IMethodSymbol) Return c.DeclaringSyntaxReferences.Select(Function(d) d.GetSyntax().Parent) End Function) End Function - End Class End Namespace diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index fde4ab7c52329..7bf90b454fd39 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -301,7 +301,7 @@ private static IList NormalizeChanges(IList changes) return normalizedChanges; } - internal class LinkedFileDiffMergingSessionInfo + internal sealed class LinkedFileDiffMergingSessionInfo { public readonly List LinkedFileGroups = []; From 2e191c86327f81ea53d401874a47c1f313355d68 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:43:20 -0700 Subject: [PATCH 231/423] Cleanup --- .../AbstractEncapsulateFieldService.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index dd691fdb404ac..97b835d3caf3b 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -30,6 +30,8 @@ namespace Microsoft.CodeAnalysis.EncapsulateField; internal abstract partial class AbstractEncapsulateFieldService : ILanguageService { + private static readonly CultureInfo EnUSCultureInfo = new("en-US"); + protected abstract Task RewriteFieldNameAndAccessibilityAsync(string originalFieldName, bool makePrivate, Document document, SyntaxAnnotation declarationAnnotation, CodeAndImportGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken); protected abstract Task> GetFieldsAsync(Document document, TextSpan span, CancellationToken cancellationToken); protected abstract IEnumerable GetConstructorNodes(INamedTypeSymbol containingType); @@ -44,7 +46,7 @@ public async Task EncapsulateFieldsInSpanAsync(Document return new EncapsulateFieldResult( firstField.ToDisplayString(), firstField.GetGlyph(), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, useDefaultBehavior, c)); + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, useDefaultBehavior, cancellationToken)); } public async Task> GetEncapsulateFieldCodeActionsAsync(Document document, TextSpan span, CleanCodeGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken) @@ -77,11 +79,11 @@ private ImmutableArray EncapsulateAllFields(Document document, Immut => [ CodeAction.Create( FeaturesResources.Encapsulate_fields_and_use_property, - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, cancellationToken), nameof(FeaturesResources.Encapsulate_fields_and_use_property)), CodeAction.Create( FeaturesResources.Encapsulate_fields_but_still_use_field, - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, c), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, cancellationToken), nameof(FeaturesResources.Encapsulate_fields_but_still_use_field)), ]; @@ -92,11 +94,11 @@ private ImmutableArray EncapsulateOneField(Document document, IField [ CodeAction.Create( string.Format(FeaturesResources.Encapsulate_field_colon_0_and_use_property, field.Name), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, c), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: true, cancellationToken), nameof(FeaturesResources.Encapsulate_field_colon_0_and_use_property) + "_" + field.Name), CodeAction.Create( string.Format(FeaturesResources.Encapsulate_field_colon_0_but_still_use_field, field.Name), - c => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, c), + cancellationToken => EncapsulateFieldsAsync(document, fields, fallbackOptions, updateReferences: false, cancellationToken), nameof(FeaturesResources.Encapsulate_field_colon_0_but_still_use_field) + "_" + field.Name), ]; } @@ -437,6 +439,4 @@ protected static string GeneratePropertyName(string fieldName) var firstCharacter = EnUSCultureInfo.TextInfo.ToUpper(baseName[0]); return firstCharacter.ToString() + baseName[1..]; } - - private static readonly CultureInfo EnUSCultureInfo = new("en-US"); } From 450e0a9b8f11d0fe8ecc5970f52118cba9501ae5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:44:45 -0700 Subject: [PATCH 232/423] Cleanup --- .../AbstractEncapsulateFieldService.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index 97b835d3caf3b..27ccc3286f83c 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -31,6 +31,11 @@ namespace Microsoft.CodeAnalysis.EncapsulateField; internal abstract partial class AbstractEncapsulateFieldService : ILanguageService { private static readonly CultureInfo EnUSCultureInfo = new("en-US"); + private static SymbolRenameOptions s_symbolRenameOptions = new( + RenameOverloads: false, + RenameInStrings: false, + RenameInComments: false, + RenameFile: false); protected abstract Task RewriteFieldNameAndAccessibilityAsync(string originalFieldName, bool makePrivate, Document document, SyntaxAnnotation declarationAnnotation, CodeAndImportGenerationOptionsProvider fallbackOptions, CancellationToken cancellationToken); protected abstract Task> GetFieldsAsync(Document document, TextSpan span, CancellationToken cancellationToken); @@ -125,9 +130,7 @@ public async Task EncapsulateFieldsAsync( cancellationToken).ConfigureAwait(false); if (!result.HasValue) - { return solution; - } return await RemoteUtilities.UpdateSolutionAsync( solution, result.Value, cancellationToken).ConfigureAwait(false); @@ -282,14 +285,8 @@ private static async Task RenameAsync( Func filter, CancellationToken cancellationToken) { - var options = new SymbolRenameOptions( - RenameOverloads: false, - RenameInStrings: false, - RenameInComments: false, - RenameFile: false); - var initialLocations = await Renamer.FindRenameLocationsAsync( - solution, field, options, cancellationToken).ConfigureAwait(false); + solution, field, s_symbolRenameOptions, cancellationToken).ConfigureAwait(false); // Ensure we don't update any files in projects linked to us. That will be taken care of automatically when we // edit the files in the current project From 940ce0f23bbe5c786e98df8b5df2f1f3a4b78d97 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:53:19 -0700 Subject: [PATCH 233/423] Test fixes --- .../LinkedFileDiffMergingEditorTests.cs | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs index 9d9b0d488ef3b..3d1c3cb86b09f 100644 --- a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs +++ b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs @@ -2,14 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -19,13 +18,27 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.LinkedFiles public partial class LinkedFileDiffMergingEditorTests : AbstractCodeActionTest { private const string WorkspaceXml = @" - - - - - - - "; + + + + + + + "; + + private const string s_expectedCode = """ + internal class C + { + private class D + { + } + } + """; protected internal override string GetLanguage() => LanguageNames.CSharp; @@ -40,13 +53,11 @@ public async Task TestCodeActionPreviewAndApply() using var workspace = EditorTestWorkspace.Create(WorkspaceXml, composition: EditorTestCompositions.EditorFeaturesWpf); var codeIssueOrRefactoring = await GetCodeRefactoringAsync(workspace, new TestParameters()); - var expectedCode = "private class D { }"; - await TestActionOnLinkedFiles( workspace, - expectedText: expectedCode, + expectedText: s_expectedCode, action: codeIssueOrRefactoring.CodeActions[0].action, - expectedPreviewContents: expectedCode); + expectedPreviewContents: s_expectedCode); } [Fact] @@ -56,35 +67,38 @@ public async Task TestWorkspaceTryApplyChangesDirectCall() var solution = workspace.CurrentSolution; var documentId = workspace.Documents.Single(d => !d.IsLinkFile).Id; - var text = await workspace.CurrentSolution.GetDocument(documentId).GetTextAsync(); + var text = await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync(); var linkedDocumentId = workspace.Documents.Single(d => d.IsLinkFile).Id; - var linkedText = await workspace.CurrentSolution.GetDocument(linkedDocumentId).GetTextAsync(); + var linkedText = await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync(); + + var textString = linkedText.ToString(); var newSolution = solution - .WithDocumentText(documentId, text.Replace(13, 1, "D")) - .WithDocumentText(linkedDocumentId, linkedText.Replace(0, 6, "private")); + .WithDocumentText(documentId, text.Replace(textString.IndexOf("public"), "public".Length, "internal")) + .WithDocumentText(linkedDocumentId, linkedText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); workspace.TryApplyChanges(newSolution); - var expectedMergedText = "private class D { }"; - Assert.Equal(expectedMergedText, (await workspace.CurrentSolution.GetDocument(documentId).GetTextAsync()).ToString()); - Assert.Equal(expectedMergedText, (await workspace.CurrentSolution.GetDocument(linkedDocumentId).GetTextAsync()).ToString()); + Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync()).ToString()); + Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync()).ToString()); } protected override ParseOptions GetScriptOptions() => throw new NotSupportedException(); - private class TestCodeRefactoringProvider : CodeRefactorings.CodeRefactoringProvider + private class TestCodeRefactoringProvider : CodeRefactoringProvider { public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var linkedDocument = document.Project.Solution.Projects.Single(p => p != document.Project).Documents.Single(); + var sourceText = await linkedDocument.GetTextAsync(); + var textString = sourceText.ToString(); var newSolution = document.Project.Solution - .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(13, 1, "D")) - .WithDocumentText(linkedDocument.Id, (await linkedDocument.GetTextAsync()).Replace(0, 6, "private")); + .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(textString.IndexOf("public"), "public".Length, "internal")) + .WithDocumentText(linkedDocument.Id, sourceText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); #pragma warning disable RS0005 context.RegisterRefactoring(CodeAction.Create("Description", (ct) => Task.FromResult(newSolution)), context.Span); From 30674f450547592da7651d5d0c7e1260d91efcf9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:56:06 -0700 Subject: [PATCH 234/423] Test fixes --- .../LinkedFileDiffMergingEditorTests.cs | 143 +++++++++--------- 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs index 3d1c3cb86b09f..506436b11d540 100644 --- a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs +++ b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs @@ -13,97 +13,104 @@ using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.UnitTests.LinkedFiles -{ - public partial class LinkedFileDiffMergingEditorTests : AbstractCodeActionTest - { - private const string WorkspaceXml = @" - - + private const string WorkspaceXml = """ + + + - - + + - "; + + """; - private const string s_expectedCode = """ - internal class C + private const string s_expectedCode = """ + internal class C + { + private class D { - private class D - { - } } - """; + } + """; - protected internal override string GetLanguage() - => LanguageNames.CSharp; + protected internal override string GetLanguage() + => LanguageNames.CSharp; - protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters) - => new TestCodeRefactoringProvider(); + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(EditorTestWorkspace workspace, TestParameters parameters) + => new TestCodeRefactoringProvider(); - [WpfFact] - public async Task TestCodeActionPreviewAndApply() - { - // TODO: WPF required due to https://github.com/dotnet/roslyn/issues/46153 - using var workspace = EditorTestWorkspace.Create(WorkspaceXml, composition: EditorTestCompositions.EditorFeaturesWpf); - var codeIssueOrRefactoring = await GetCodeRefactoringAsync(workspace, new TestParameters()); - - await TestActionOnLinkedFiles( - workspace, - expectedText: s_expectedCode, - action: codeIssueOrRefactoring.CodeActions[0].action, - expectedPreviewContents: s_expectedCode); - } + [WpfFact] + public async Task TestCodeActionPreviewAndApply() + { + // TODO: WPF required due to https://github.com/dotnet/roslyn/issues/46153 + using var workspace = EditorTestWorkspace.Create(WorkspaceXml, composition: EditorTestCompositions.EditorFeaturesWpf); + var codeIssueOrRefactoring = await GetCodeRefactoringAsync(workspace, new TestParameters()); + + await TestActionOnLinkedFiles( + workspace, + expectedText: s_expectedCode, + action: codeIssueOrRefactoring.CodeActions[0].action, + expectedPreviewContents: """ + internal class C + { + private class D + { + ... + """); + } - [Fact] - public async Task TestWorkspaceTryApplyChangesDirectCall() - { - using var workspace = EditorTestWorkspace.Create(WorkspaceXml); - var solution = workspace.CurrentSolution; + [Fact] + public async Task TestWorkspaceTryApplyChangesDirectCall() + { + using var workspace = EditorTestWorkspace.Create(WorkspaceXml); + var solution = workspace.CurrentSolution; - var documentId = workspace.Documents.Single(d => !d.IsLinkFile).Id; - var text = await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync(); + var documentId = workspace.Documents.Single(d => !d.IsLinkFile).Id; + var text = await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync(); - var linkedDocumentId = workspace.Documents.Single(d => d.IsLinkFile).Id; - var linkedText = await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync(); + var linkedDocumentId = workspace.Documents.Single(d => d.IsLinkFile).Id; + var linkedText = await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync(); - var textString = linkedText.ToString(); + var textString = linkedText.ToString(); - var newSolution = solution - .WithDocumentText(documentId, text.Replace(textString.IndexOf("public"), "public".Length, "internal")) - .WithDocumentText(linkedDocumentId, linkedText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); + var newSolution = solution + .WithDocumentText(documentId, text.Replace(textString.IndexOf("public"), "public".Length, "internal")) + .WithDocumentText(linkedDocumentId, linkedText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); - workspace.TryApplyChanges(newSolution); + workspace.TryApplyChanges(newSolution); - Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync()).ToString()); - Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync()).ToString()); - } + Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(documentId).GetTextAsync()).ToString()); + Assert.Equal(s_expectedCode, (await workspace.CurrentSolution.GetRequiredDocument(linkedDocumentId).GetTextAsync()).ToString()); + } - protected override ParseOptions GetScriptOptions() - => throw new NotSupportedException(); + protected override ParseOptions GetScriptOptions() + => throw new NotSupportedException(); - private class TestCodeRefactoringProvider : CodeRefactoringProvider + private class TestCodeRefactoringProvider : CodeRefactoringProvider + { + public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { - public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) - { - var document = context.Document; - var linkedDocument = document.Project.Solution.Projects.Single(p => p != document.Project).Documents.Single(); - var sourceText = await linkedDocument.GetTextAsync(); - var textString = sourceText.ToString(); + var document = context.Document; + var linkedDocument = document.Project.Solution.Projects.Single(p => p != document.Project).Documents.Single(); + var sourceText = await linkedDocument.GetTextAsync(); + var textString = sourceText.ToString(); - var newSolution = document.Project.Solution - .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(textString.IndexOf("public"), "public".Length, "internal")) - .WithDocumentText(linkedDocument.Id, sourceText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); + var newSolution = document.Project.Solution + .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(textString.IndexOf("public"), "public".Length, "internal")) + .WithDocumentText(linkedDocument.Id, sourceText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); #pragma warning disable RS0005 - context.RegisterRefactoring(CodeAction.Create("Description", (ct) => Task.FromResult(newSolution)), context.Span); + context.RegisterRefactoring(CodeAction.Create("Description", (ct) => Task.FromResult(newSolution)), context.Span); #pragma warning restore RS0005 - } } } } From d446cb855bd297f36dee9450ded55ac573001d16 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 11:58:13 -0700 Subject: [PATCH 235/423] Cleanup --- .../Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs index 506436b11d540..47974f2cfb369 100644 --- a/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs +++ b/src/EditorFeatures/Test/LinkedFiles/LinkedFileDiffMergingEditorTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.LinkedFiles; -public partial class LinkedFileDiffMergingEditorTests : AbstractCodeActionTest +public sealed class LinkedFileDiffMergingEditorTests : AbstractCodeActionTest { private const string WorkspaceXml = """ @@ -95,7 +95,7 @@ public async Task TestWorkspaceTryApplyChangesDirectCall() protected override ParseOptions GetScriptOptions() => throw new NotSupportedException(); - private class TestCodeRefactoringProvider : CodeRefactoringProvider + private sealed class TestCodeRefactoringProvider : CodeRefactoringProvider { public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { @@ -108,9 +108,7 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex .WithDocumentText(document.Id, (await document.GetTextAsync()).Replace(textString.IndexOf("public"), "public".Length, "internal")) .WithDocumentText(linkedDocument.Id, sourceText.Replace(textString.LastIndexOf("public"), "public".Length, "private")); -#pragma warning disable RS0005 - context.RegisterRefactoring(CodeAction.Create("Description", (ct) => Task.FromResult(newSolution)), context.Span); -#pragma warning restore RS0005 + context.RegisterRefactoring(CodeAction.Create("Description", _ => Task.FromResult(newSolution)), context.Span); } } } From 531a9d55adb7edbd18ba6a3c685d2bcd5011846b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 12:21:20 -0700 Subject: [PATCH 236/423] Make readonly --- .../EncapsulateField/AbstractEncapsulateFieldService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index 27ccc3286f83c..82afe2421891b 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -31,7 +31,7 @@ namespace Microsoft.CodeAnalysis.EncapsulateField; internal abstract partial class AbstractEncapsulateFieldService : ILanguageService { private static readonly CultureInfo EnUSCultureInfo = new("en-US"); - private static SymbolRenameOptions s_symbolRenameOptions = new( + private static readonly SymbolRenameOptions s_symbolRenameOptions = new( RenameOverloads: false, RenameInStrings: false, RenameInComments: false, From b977b8138cb05acef02c6fd78f54acd08ee0f5e9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 12:38:47 -0700 Subject: [PATCH 237/423] Do less work computing diffs less work Simplify Fix test Add back Simplify Fix rename lint --- .../InlineTemporary/InlineTemporaryTests.cs | 4 +- .../LinkedFileDiffMergingSession.cs | 135 +++++++++++------- 2 files changed, 82 insertions(+), 57 deletions(-) diff --git a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs index 438aebd5db404..12a86d1e86b75 100644 --- a/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs +++ b/src/Features/CSharpTest/InlineTemporary/InlineTemporaryTests.cs @@ -5745,7 +5745,7 @@ public class Goo { public void Bar() { - var target = new List<object;gt>(); + var target = new List<object>(); var [||]newItems = new List<Goo>(); target.AddRange(newItems); } @@ -5769,7 +5769,7 @@ public class Goo { public void Bar() { - var target = new List<object;gt>(); + var target = new List<object>(); target.AddRange(new List<Goo>()); } } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 7bf90b454fd39..25f3bba85baa2 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -19,79 +17,103 @@ namespace Microsoft.CodeAnalysis; internal sealed class LinkedFileDiffMergingSession(Solution oldSolution, Solution newSolution, SolutionChanges solutionChanges) { - internal async Task MergeDiffsAsync(IMergeConflictHandler mergeConflictHandler, CancellationToken cancellationToken) + internal async Task MergeDiffsAsync(IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { var sessionInfo = new LinkedFileDiffMergingSessionInfo(); - var linkedDocumentGroupsWithChanges = solutionChanges - .GetProjectChanges() - .SelectMany(p => p.GetChangedDocuments()) - .GroupBy(d => oldSolution.GetDocument(d).FilePath, StringComparer.OrdinalIgnoreCase); + using var _ = PooledDictionary newContentHash)>>.GetInstance(out var filePathToNewDocumentsAndHashes); + foreach (var documentId in solutionChanges.GetProjectChanges().SelectMany(p => p.GetChangedDocuments())) + { + var newDocument = newSolution.GetRequiredDocument(documentId); + var filePath = newDocument.FilePath; + Contract.ThrowIfNull(filePath); - var linkedFileMergeResults = new List(); + if (!filePathToNewDocumentsAndHashes.TryGetValue(filePath, out var newDocumentsAndHashes)) + { + newDocumentsAndHashes = []; + filePathToNewDocumentsAndHashes.Add(filePath, newDocumentsAndHashes); + } + + var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var newContentHash = newText.GetContentHash(); + // Ignore any linked documents that we have the same contents as. + if (newDocumentsAndHashes.Any(t => t.newContentHash.SequenceEqual(newContentHash))) + continue; + + newDocumentsAndHashes.Add((newDocument, newContentHash)); + } var updatedSolution = newSolution; - foreach (var linkedDocumentsWithChanges in linkedDocumentGroupsWithChanges) + var linkedFileMergeResults = new List(); + + foreach (var (filePath, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) { - var documentInNewSolution = newSolution.GetDocument(linkedDocumentsWithChanges.First()); + Contract.ThrowIfTrue(newDocumentsAndHashes.Count == 0); - // Ensure the first document in the group is the first in the list of - var allLinkedDocuments = documentInNewSolution.GetLinkedDocumentIds().Add(documentInNewSolution.Id); - if (allLinkedDocuments.Length == 1) - { + // Don't need to do anything if this document has no linked siblings. + var firstNewDocument = newDocumentsAndHashes[0].newDocument; + var relatedDocuments = newSolution.GetRelatedDocumentIds(firstNewDocument.Id); + if (relatedDocuments.Length == 1) continue; - } - SourceText mergedText; - if (linkedDocumentsWithChanges.Count() > 1) + if (newDocumentsAndHashes.Count == 1) { - var mergeGroupResult = await MergeLinkedDocumentGroupAsync(allLinkedDocuments, linkedDocumentsWithChanges, sessionInfo, mergeConflictHandler, cancellationToken).ConfigureAwait(false); - linkedFileMergeResults.Add(mergeGroupResult); - mergedText = mergeGroupResult.MergedSourceText; + // The file has linked siblings, but we collapsed down to only one actual document change. Ensure that + // any linked files have that same content as well. + var firstSourceText = await firstNewDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + updatedSolution = updatedSolution.WithDocumentTexts( + relatedDocuments.SelectAsArray(d => (d, firstSourceText))); } else { - mergedText = await newSolution.GetDocument(linkedDocumentsWithChanges.Single()).GetValueTextAsync(cancellationToken).ConfigureAwait(false); + // Otherwise, merge the changes and set all the linked files to that merged content. + var mergeGroupResult = await MergeLinkedDocumentGroupAsync(newDocumentsAndHashes, sessionInfo, mergeConflictHandler, cancellationToken).ConfigureAwait(false); + linkedFileMergeResults.Add(mergeGroupResult); + updatedSolution = updatedSolution.WithDocumentTexts( + relatedDocuments.SelectAsArray(d => (d, mergeGroupResult.MergedSourceText))); } - - updatedSolution = updatedSolution.WithDocumentTexts( - allLinkedDocuments.SelectAsArray(documentId => (documentId, mergedText))); } return new LinkedFileMergeSessionResult(updatedSolution, linkedFileMergeResults); } private async Task MergeLinkedDocumentGroupAsync( - ImmutableArray allLinkedDocuments, - IGrouping linkedDocumentGroup, + List<(Document newDocument, ImmutableArray newContentHash)> newDocumentsAndHashes, LinkedFileDiffMergingSessionInfo sessionInfo, - IMergeConflictHandler mergeConflictHandler, + IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { + Contract.ThrowIfTrue(newDocumentsAndHashes.Count < 2); + var groupSessionInfo = new LinkedFileGroupSessionInfo(); // Automatically merge non-conflicting diffs while collecting the conflicting diffs var textDifferencingService = oldSolution.Services.GetRequiredService(); + + var firstNewDocument = newDocumentsAndHashes[0].newDocument; + var firstOldDocument = oldSolution.GetRequiredDocument(firstNewDocument.Id); + var firstOldSourceText = await firstOldDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var appliedChanges = await textDifferencingService.GetTextChangesAsync( - oldSolution.GetDocument(linkedDocumentGroup.First()), newSolution.GetDocument(linkedDocumentGroup.First()), TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); - var unmergedChanges = new List(); + firstOldDocument, firstNewDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); - foreach (var documentId in linkedDocumentGroup.Skip(1)) + var unmergedChanges = new List(); + for (int i = 1, n = newDocumentsAndHashes.Count; i < n; i++) { + var siblingNewDocument = newDocumentsAndHashes[i].newDocument; + var siblingOldDocument = oldSolution.GetRequiredDocument(siblingNewDocument.Id); + appliedChanges = await AddDocumentMergeChangesAsync( - oldSolution.GetDocument(documentId), - newSolution.GetDocument(documentId), - [.. appliedChanges], + siblingOldDocument, + siblingNewDocument, + appliedChanges, unmergedChanges, groupSessionInfo, textDifferencingService, cancellationToken).ConfigureAwait(false); } - var originalDocument = oldSolution.GetDocument(linkedDocumentGroup.First()); - var originalSourceText = await originalDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - // Add comments in source explaining diffs that could not be merged ImmutableArray allChanges; @@ -99,8 +121,8 @@ private async Task MergeLinkedDocumentGroupAsync( if (unmergedChanges.Count != 0) { - mergeConflictHandler ??= oldSolution.GetDocument(linkedDocumentGroup.First()).GetLanguageService(); - var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(originalSourceText, unmergedChanges); + mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); + var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); allChanges = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits, mergeConflictResolutionSpan, groupSessionInfo); } @@ -109,17 +131,20 @@ private async Task MergeLinkedDocumentGroupAsync( allChanges = appliedChanges; } - groupSessionInfo.LinkedDocuments = newSolution.GetDocumentIdsWithFilePath(originalDocument.FilePath).Length; - groupSessionInfo.DocumentsWithChanges = linkedDocumentGroup.Count(); + var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); + groupSessionInfo.LinkedDocuments = linkedDocuments.Length; + groupSessionInfo.DocumentsWithChanges = newDocumentsAndHashes.Count; sessionInfo.LogLinkedFileResult(groupSessionInfo); - return new LinkedFileMergeResult(allLinkedDocuments, originalSourceText.WithChanges(allChanges), mergeConflictResolutionSpan); + return new LinkedFileMergeResult( + linkedDocuments, + firstOldSourceText.WithChanges(allChanges), mergeConflictResolutionSpan); } private static async Task> AddDocumentMergeChangesAsync( Document oldDocument, Document newDocument, - List cumulativeChanges, + ImmutableArray cumulativeChanges, List unmergedChanges, LinkedFileGroupSessionInfo groupSessionInfo, IDocumentTextDifferencingService textDiffService, @@ -134,7 +159,7 @@ private static async Task> AddDocumentMergeChangesAsy oldDocument, newDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); foreach (var change in textChanges) { - while (cumulativeChangeIndex < cumulativeChanges.Count && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start) + while (cumulativeChangeIndex < cumulativeChanges.Length && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start) { // Existing change that does not overlap with the current change in consideration successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]); @@ -143,7 +168,7 @@ private static async Task> AddDocumentMergeChangesAsy groupSessionInfo.IsolatedDiffs++; } - if (cumulativeChangeIndex < cumulativeChanges.Count) + if (cumulativeChangeIndex < cumulativeChanges.Length) { var cumulativeChange = cumulativeChanges[cumulativeChangeIndex]; if (!cumulativeChange.Span.IntersectsWith(change.Span)) @@ -165,7 +190,7 @@ private static async Task> AddDocumentMergeChangesAsy if (change.Span == cumulativeChange.Span) { groupSessionInfo.OverlappingDistinctDiffsWithSameSpan++; - if (change.NewText.Contains(cumulativeChange.NewText) || cumulativeChange.NewText.Contains(change.NewText)) + if (change.NewText!.Contains(cumulativeChange.NewText!) || cumulativeChange.NewText!.Contains(change.NewText)) { groupSessionInfo.OverlappingDistinctDiffsWithSameSpanAndSubstringRelation++; } @@ -190,7 +215,7 @@ private static async Task> AddDocumentMergeChangesAsy } } - while (cumulativeChangeIndex < cumulativeChanges.Count) + while (cumulativeChangeIndex < cumulativeChanges.Length) { // Existing change that does not overlap with the current change in consideration successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]); @@ -230,8 +255,8 @@ private static ImmutableArray MergeChangesWithMergeFailComments( { // Add a comment change that does not conflict with any merge change combinedChanges.Add(commentChangesList[commentChangeIndex]); - mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length)); - currentPositionDelta += (commentChangesList[commentChangeIndex].NewText.Length - commentChangesList[commentChangeIndex].Span.Length); + mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); + currentPositionDelta += (commentChangesList[commentChangeIndex].NewText!.Length - commentChangesList[commentChangeIndex].Span.Length); commentChangeIndex++; } @@ -239,7 +264,7 @@ private static ImmutableArray MergeChangesWithMergeFailComments( { // Add a merge change that does not conflict with any comment change combinedChanges.Add(mergedChange); - currentPositionDelta += (mergedChange.NewText.Length - mergedChange.Span.Length); + currentPositionDelta += (mergedChange.NewText!.Length - mergedChange.Span.Length); continue; } @@ -247,25 +272,25 @@ private static ImmutableArray MergeChangesWithMergeFailComments( var conflictingCommentInsertionLocation = new TextSpan(mergedChange.Span.Start, 0); while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.Start < mergedChange.Span.End) { - combinedChanges.Add(new TextChange(conflictingCommentInsertionLocation, commentChangesList[commentChangeIndex].NewText)); - mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length)); - currentPositionDelta += commentChangesList[commentChangeIndex].NewText.Length; + combinedChanges.Add(new TextChange(conflictingCommentInsertionLocation, commentChangesList[commentChangeIndex].NewText!)); + mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); + currentPositionDelta += commentChangesList[commentChangeIndex].NewText!.Length; commentChangeIndex++; insertedMergeConflictCommentsAtAdjustedLocation++; } combinedChanges.Add(mergedChange); - currentPositionDelta += (mergedChange.NewText.Length - mergedChange.Span.Length); + currentPositionDelta += (mergedChange.NewText!.Length - mergedChange.Span.Length); } while (commentChangeIndex < commentChangesList.Count) { // Add a comment change that does not conflict with any merge change combinedChanges.Add(commentChangesList[commentChangeIndex]); - mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText.Length)); + mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); - currentPositionDelta += (commentChangesList[commentChangeIndex].NewText.Length - commentChangesList[commentChangeIndex].Span.Length); + currentPositionDelta += (commentChangesList[commentChangeIndex].NewText!.Length - commentChangesList[commentChangeIndex].Span.Length); commentChangeIndex++; } From c16b3706a8b09967da666d56c3e93e6acc5e654f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:02:13 -0700 Subject: [PATCH 238/423] use GetOrAdd helper --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 25f3bba85baa2..cf39c248c1d86 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -28,11 +28,7 @@ internal async Task MergeDiffsAsync(IMergeConflict var filePath = newDocument.FilePath; Contract.ThrowIfNull(filePath); - if (!filePathToNewDocumentsAndHashes.TryGetValue(filePath, out var newDocumentsAndHashes)) - { - newDocumentsAndHashes = []; - filePathToNewDocumentsAndHashes.Add(filePath, newDocumentsAndHashes); - } + var newDocumentsAndHashes = filePathToNewDocumentsAndHashes.GetOrAdd(filePath, static _ => []); var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var newContentHash = newText.GetContentHash(); From e56e938bb8ea40a0cdb55f6cc29db2b74e17c81f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:04:41 -0700 Subject: [PATCH 239/423] prefilter --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index cf39c248c1d86..0eb21577873db 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -24,7 +24,11 @@ internal async Task MergeDiffsAsync(IMergeConflict using var _ = PooledDictionary newContentHash)>>.GetInstance(out var filePathToNewDocumentsAndHashes); foreach (var documentId in solutionChanges.GetProjectChanges().SelectMany(p => p.GetChangedDocuments())) { + // Don't need to do any merging whatsoever for documents that are not linked files. var newDocument = newSolution.GetRequiredDocument(documentId); + if (newSolution.GetRelatedDocumentIds(newDocument.Id).Length == 1) + continue; + var filePath = newDocument.FilePath; Contract.ThrowIfNull(filePath); @@ -48,9 +52,9 @@ internal async Task MergeDiffsAsync(IMergeConflict // Don't need to do anything if this document has no linked siblings. var firstNewDocument = newDocumentsAndHashes[0].newDocument; + var relatedDocuments = newSolution.GetRelatedDocumentIds(firstNewDocument.Id); - if (relatedDocuments.Length == 1) - continue; + Contract.ThrowIfTrue(relatedDocuments.Length == 1, "We should have skipped non-linked files in the prior loop.") if (newDocumentsAndHashes.Count == 1) { From 278890b04c6651a4f4504a3a228c1493fcf3e62b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:06:00 -0700 Subject: [PATCH 240/423] Correct capacity --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 0eb21577873db..fc4ccab94b914 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -26,13 +26,14 @@ internal async Task MergeDiffsAsync(IMergeConflict { // Don't need to do any merging whatsoever for documents that are not linked files. var newDocument = newSolution.GetRequiredDocument(documentId); - if (newSolution.GetRelatedDocumentIds(newDocument.Id).Length == 1) + var relatedDocumentIds = newSolution.GetRelatedDocumentIds(newDocument.Id); + if (relatedDocumentIds.Length == 1) continue; var filePath = newDocument.FilePath; Contract.ThrowIfNull(filePath); - var newDocumentsAndHashes = filePathToNewDocumentsAndHashes.GetOrAdd(filePath, static _ => []); + var newDocumentsAndHashes = filePathToNewDocumentsAndHashes.GetOrAdd(filePath, static (_, capacity) => new(capacity), relatedDocumentIds.Length); var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var newContentHash = newText.GetContentHash(); From 4a11fe5f4aec9d4027fee72f76929ed12174467c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:06:28 -0700 Subject: [PATCH 241/423] fix --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index fc4ccab94b914..d821faa2c4d33 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -55,7 +55,7 @@ internal async Task MergeDiffsAsync(IMergeConflict var firstNewDocument = newDocumentsAndHashes[0].newDocument; var relatedDocuments = newSolution.GetRelatedDocumentIds(firstNewDocument.Id); - Contract.ThrowIfTrue(relatedDocuments.Length == 1, "We should have skipped non-linked files in the prior loop.") + Contract.ThrowIfTrue(relatedDocuments.Length == 1, "We should have skipped non-linked files in the prior loop."); if (newDocumentsAndHashes.Count == 1) { From 9ff8cf955d8a083e1abe8ce18e6d7c14b705fb91 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:14:36 -0700 Subject: [PATCH 242/423] Pooling --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 ++++-- .../LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index d821faa2c4d33..7874217d89d8a 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -21,7 +21,7 @@ internal async Task MergeDiffsAsync(IMergeConflict { var sessionInfo = new LinkedFileDiffMergingSessionInfo(); - using var _ = PooledDictionary newContentHash)>>.GetInstance(out var filePathToNewDocumentsAndHashes); + using var _1 = PooledDictionary newContentHash)>>.GetInstance(out var filePathToNewDocumentsAndHashes); foreach (var documentId in solutionChanges.GetProjectChanges().SelectMany(p => p.GetChangedDocuments())) { // Don't need to do any merging whatsoever for documents that are not linked files. @@ -45,7 +45,9 @@ internal async Task MergeDiffsAsync(IMergeConflict } var updatedSolution = newSolution; - var linkedFileMergeResults = new List(); + using var _ = ArrayBuilder.GetInstance( + filePathToNewDocumentsAndHashes.Count(static kvp => kvp.Value.Count > 1), + out var linkedFileMergeResults); foreach (var (filePath, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) { diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs index a1923f500c2c9..093b68bbf178e 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; @@ -13,7 +14,7 @@ internal sealed class LinkedFileMergeSessionResult public readonly Dictionary> MergeConflictCommentSpans = []; - public LinkedFileMergeSessionResult(Solution mergedSolution, List fileMergeResults) + public LinkedFileMergeSessionResult(Solution mergedSolution, ArrayBuilder fileMergeResults) { this.MergedSolution = mergedSolution; From 9105478b087340d2d23a8cecf06b8798e2875b11 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:18:17 -0700 Subject: [PATCH 243/423] arrays --- .../IMergeConflictHandler.cs | 4 ++- .../LinkedFileDiffMergingSession.cs | 29 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs index e6f12be528fa9..2e84348dbda34 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/IMergeConflictHandler.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; internal interface IMergeConflictHandler { - List CreateEdits(SourceText originalSourceText, List unmergedChanges); + ImmutableArray CreateEdits(SourceText originalSourceText, ArrayBuilder unmergedChanges); } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 7874217d89d8a..95e662d7ca3d7 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Runtime; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; @@ -101,7 +102,7 @@ private async Task MergeLinkedDocumentGroupAsync( var appliedChanges = await textDifferencingService.GetTextChangesAsync( firstOldDocument, firstNewDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); - var unmergedChanges = new List(); + using var _ = ArrayBuilder.GetInstance(out var unmergedChanges); for (int i = 1, n = newDocumentsAndHashes.Count; i < n; i++) { var siblingNewDocument = newDocumentsAndHashes[i].newDocument; @@ -148,7 +149,7 @@ private static async Task> AddDocumentMergeChangesAsy Document oldDocument, Document newDocument, ImmutableArray cumulativeChanges, - List unmergedChanges, + ArrayBuilder unmergedChanges, LinkedFileGroupSessionInfo groupSessionInfo, IDocumentTextDifferencingService textDiffService, CancellationToken cancellationToken) @@ -239,14 +240,14 @@ private static async Task> AddDocumentMergeChangesAsy private static ImmutableArray MergeChangesWithMergeFailComments( ImmutableArray mergedChanges, - List commentChanges, + ImmutableArray commentChanges, List mergeConflictResolutionSpans, LinkedFileGroupSessionInfo groupSessionInfo) { var mergedChangesList = NormalizeChanges(mergedChanges); var commentChangesList = NormalizeChanges(commentChanges); - var combinedChanges = new List(); + using var _ = ArrayBuilder.GetInstance(out var combinedChanges); var insertedMergeConflictCommentsAtAdjustedLocation = 0; var commentChangeIndex = 0; @@ -254,7 +255,7 @@ private static ImmutableArray MergeChangesWithMergeFailComments( foreach (var mergedChange in mergedChangesList) { - while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.End <= mergedChange.Span.Start) + while (commentChangeIndex < commentChangesList.Length && commentChangesList[commentChangeIndex].Span.End <= mergedChange.Span.Start) { // Add a comment change that does not conflict with any merge change combinedChanges.Add(commentChangesList[commentChangeIndex]); @@ -263,7 +264,7 @@ private static ImmutableArray MergeChangesWithMergeFailComments( commentChangeIndex++; } - if (commentChangeIndex >= commentChangesList.Count || mergedChange.Span.End <= commentChangesList[commentChangeIndex].Span.Start) + if (commentChangeIndex >= commentChangesList.Length || mergedChange.Span.End <= commentChangesList[commentChangeIndex].Span.Start) { // Add a merge change that does not conflict with any comment change combinedChanges.Add(mergedChange); @@ -273,7 +274,7 @@ private static ImmutableArray MergeChangesWithMergeFailComments( // The current comment insertion location conflicts with a merge diff location. Add the comment before the diff. var conflictingCommentInsertionLocation = new TextSpan(mergedChange.Span.Start, 0); - while (commentChangeIndex < commentChangesList.Count && commentChangesList[commentChangeIndex].Span.Start < mergedChange.Span.End) + while (commentChangeIndex < commentChangesList.Length && commentChangesList[commentChangeIndex].Span.Start < mergedChange.Span.End) { combinedChanges.Add(new TextChange(conflictingCommentInsertionLocation, commentChangesList[commentChangeIndex].NewText!)); mergeConflictResolutionSpans.Add(new TextSpan(commentChangesList[commentChangeIndex].Span.Start + currentPositionDelta, commentChangesList[commentChangeIndex].NewText!.Length)); @@ -287,7 +288,7 @@ private static ImmutableArray MergeChangesWithMergeFailComments( currentPositionDelta += (mergedChange.NewText!.Length - mergedChange.Span.Length); } - while (commentChangeIndex < commentChangesList.Count) + while (commentChangeIndex < commentChangesList.Length) { // Add a comment change that does not conflict with any merge change combinedChanges.Add(commentChangesList[commentChangeIndex]); @@ -297,19 +298,19 @@ private static ImmutableArray MergeChangesWithMergeFailComments( commentChangeIndex++; } - groupSessionInfo.InsertedMergeConflictComments = commentChanges.Count(); + groupSessionInfo.InsertedMergeConflictComments = commentChanges.Length; groupSessionInfo.InsertedMergeConflictCommentsAtAdjustedLocation = insertedMergeConflictCommentsAtAdjustedLocation; - return NormalizeChanges(combinedChanges).ToImmutableArray(); + return NormalizeChanges(combinedChanges.ToImmutableAndClear()); } - private static IList NormalizeChanges(IList changes) + private static ImmutableArray NormalizeChanges(ImmutableArray changes) { - if (changes.Count <= 1) + if (changes.Length <= 1) return changes; var orderedChanges = changes.OrderBy(c => c.Span.Start).ToList(); - var normalizedChanges = new List(); + using var _ = ArrayBuilder.GetInstance(changes.Length, out var normalizedChanges); var currentChange = changes.First(); foreach (var nextChange in changes.Skip(1)) @@ -326,7 +327,7 @@ private static IList NormalizeChanges(IList changes) } normalizedChanges.Add(currentChange); - return normalizedChanges; + return normalizedChanges.ToImmutableAndClear(); } internal sealed class LinkedFileDiffMergingSessionInfo From dd1929d48bd239e531053f002d131346b92bbcc4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:25:01 -0700 Subject: [PATCH 244/423] Simplify --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 95e662d7ca3d7..d0ca10e623552 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -309,11 +309,11 @@ private static ImmutableArray NormalizeChanges(ImmutableArray c.Span.Start).ToList(); + var orderedChanges = changes.OrderBy(c => c.Span.Start).ToImmutableArray(); using var _ = ArrayBuilder.GetInstance(changes.Length, out var normalizedChanges); - var currentChange = changes.First(); - foreach (var nextChange in changes.Skip(1)) + var currentChange = changes[0]; + foreach (var nextChange in changes.AsSpan()[1..]) { if (nextChange.Span.Start == currentChange.Span.End) { From 5f50d4085da95ed8eca6a812197c17ae49cd51ba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:26:34 -0700 Subject: [PATCH 245/423] Optimize --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index d0ca10e623552..db8e5612b21e9 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -309,7 +309,7 @@ private static ImmutableArray NormalizeChanges(ImmutableArray c.Span.Start).ToImmutableArray(); + var orderedChanges = changes.Sort(static (c1, c2) => c1.Span.Start - c2.Span.Start); using var _ = ArrayBuilder.GetInstance(changes.Length, out var normalizedChanges); var currentChange = changes[0]; From bfd0287e400e50215c1ac380e208dabb15379393 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:29:59 -0700 Subject: [PATCH 246/423] more immutable' --- .../LinkedFileDiffMergingSession.cs | 16 +++++++++------- .../LinkedFileMergeResult.cs | 8 +++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index db8e5612b21e9..d5273b257f6b9 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -121,18 +121,19 @@ private async Task MergeLinkedDocumentGroupAsync( // Add comments in source explaining diffs that could not be merged ImmutableArray allChanges; - var mergeConflictResolutionSpan = new List(); + ImmutableArray mergeConflictResolutionSpans; if (unmergedChanges.Count != 0) { mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); - allChanges = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits, mergeConflictResolutionSpan, groupSessionInfo); + (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits, groupSessionInfo); } else { allChanges = appliedChanges; + mergeConflictResolutionSpans = []; } var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); @@ -142,7 +143,8 @@ private async Task MergeLinkedDocumentGroupAsync( return new LinkedFileMergeResult( linkedDocuments, - firstOldSourceText.WithChanges(allChanges), mergeConflictResolutionSpan); + firstOldSourceText.WithChanges(allChanges), + mergeConflictResolutionSpans); } private static async Task> AddDocumentMergeChangesAsync( @@ -238,16 +240,16 @@ private static async Task> AddDocumentMergeChangesAsy return successfullyMergedChanges.ToImmutableAndClear(); } - private static ImmutableArray MergeChangesWithMergeFailComments( + private static (ImmutableArray mergeChanges, ImmutableArray mergeConflictResolutionSpans) MergeChangesWithMergeFailComments( ImmutableArray mergedChanges, ImmutableArray commentChanges, - List mergeConflictResolutionSpans, LinkedFileGroupSessionInfo groupSessionInfo) { var mergedChangesList = NormalizeChanges(mergedChanges); var commentChangesList = NormalizeChanges(commentChanges); - using var _ = ArrayBuilder.GetInstance(out var combinedChanges); + using var _1 = ArrayBuilder.GetInstance(out var combinedChanges); + using var _2 = ArrayBuilder.GetInstance(out var mergeConflictResolutionSpans); var insertedMergeConflictCommentsAtAdjustedLocation = 0; var commentChangeIndex = 0; @@ -301,7 +303,7 @@ private static ImmutableArray MergeChangesWithMergeFailComments( groupSessionInfo.InsertedMergeConflictComments = commentChanges.Length; groupSessionInfo.InsertedMergeConflictCommentsAtAdjustedLocation = insertedMergeConflictCommentsAtAdjustedLocation; - return NormalizeChanges(combinedChanges.ToImmutableAndClear()); + return (NormalizeChanges(combinedChanges.ToImmutableAndClear()), mergeConflictResolutionSpans.ToImmutableAndClear()); } private static ImmutableArray NormalizeChanges(ImmutableArray changes) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs index 95f2981efafe3..3a4791202ce41 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs @@ -2,17 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; -internal sealed class LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, List mergeConflictResolutionSpans) +internal sealed class LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, ImmutableArray mergeConflictResolutionSpans) { public ImmutableArray DocumentIds => documentIds; public SourceText MergedSourceText => mergedSourceText; - public List MergeConflictResolutionSpans => mergeConflictResolutionSpans; - public bool HasMergeConflicts => MergeConflictResolutionSpans.Count != 0; + public ImmutableArray MergeConflictResolutionSpans => mergeConflictResolutionSpans; + public bool HasMergeConflicts => !MergeConflictResolutionSpans.IsEmpty; } From bb7da340afcb2b76c89243b947e648b963812111 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:31:20 -0700 Subject: [PATCH 247/423] optimize --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index d5273b257f6b9..c29b6506b1a7c 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -329,6 +329,10 @@ private static ImmutableArray NormalizeChanges(ImmutableArray Date: Fri, 10 May 2024 14:36:15 -0700 Subject: [PATCH 248/423] free pooled stuff --- .../LinkedFileDiffMergingSession.cs | 102 ++++++++++-------- 1 file changed, 56 insertions(+), 46 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index c29b6506b1a7c..4cecd7a09a3db 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -16,73 +16,83 @@ namespace Microsoft.CodeAnalysis; +using DocumentAndHashBuilder = ArrayBuilder<(Document newDocument, ImmutableArray newContentHash)>; + internal sealed class LinkedFileDiffMergingSession(Solution oldSolution, Solution newSolution, SolutionChanges solutionChanges) { internal async Task MergeDiffsAsync(IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { var sessionInfo = new LinkedFileDiffMergingSessionInfo(); - using var _1 = PooledDictionary newContentHash)>>.GetInstance(out var filePathToNewDocumentsAndHashes); - foreach (var documentId in solutionChanges.GetProjectChanges().SelectMany(p => p.GetChangedDocuments())) + using var _1 = PooledDictionary.GetInstance(out var filePathToNewDocumentsAndHashes); + try { - // Don't need to do any merging whatsoever for documents that are not linked files. - var newDocument = newSolution.GetRequiredDocument(documentId); - var relatedDocumentIds = newSolution.GetRelatedDocumentIds(newDocument.Id); - if (relatedDocumentIds.Length == 1) - continue; + foreach (var documentId in solutionChanges.GetProjectChanges().SelectMany(p => p.GetChangedDocuments())) + { + // Don't need to do any merging whatsoever for documents that are not linked files. + var newDocument = newSolution.GetRequiredDocument(documentId); + var relatedDocumentIds = newSolution.GetRelatedDocumentIds(newDocument.Id); + if (relatedDocumentIds.Length == 1) + continue; - var filePath = newDocument.FilePath; - Contract.ThrowIfNull(filePath); + var filePath = newDocument.FilePath; + Contract.ThrowIfNull(filePath); - var newDocumentsAndHashes = filePathToNewDocumentsAndHashes.GetOrAdd(filePath, static (_, capacity) => new(capacity), relatedDocumentIds.Length); + var newDocumentsAndHashes = filePathToNewDocumentsAndHashes.GetOrAdd(filePath, static (_, capacity) => DocumentAndHashBuilder.GetInstance(capacity), relatedDocumentIds.Length); - var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var newContentHash = newText.GetContentHash(); - // Ignore any linked documents that we have the same contents as. - if (newDocumentsAndHashes.Any(t => t.newContentHash.SequenceEqual(newContentHash))) - continue; + var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var newContentHash = newText.GetContentHash(); + // Ignore any linked documents that we have the same contents as. + if (newDocumentsAndHashes.Any(t => t.newContentHash.SequenceEqual(newContentHash))) + continue; - newDocumentsAndHashes.Add((newDocument, newContentHash)); - } + newDocumentsAndHashes.Add((newDocument, newContentHash)); + } - var updatedSolution = newSolution; - using var _ = ArrayBuilder.GetInstance( - filePathToNewDocumentsAndHashes.Count(static kvp => kvp.Value.Count > 1), - out var linkedFileMergeResults); + var updatedSolution = newSolution; + using var _ = ArrayBuilder.GetInstance( + filePathToNewDocumentsAndHashes.Count(static kvp => kvp.Value.Count > 1), + out var linkedFileMergeResults); - foreach (var (filePath, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) - { - Contract.ThrowIfTrue(newDocumentsAndHashes.Count == 0); + foreach (var (filePath, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) + { + Contract.ThrowIfTrue(newDocumentsAndHashes.Count == 0); - // Don't need to do anything if this document has no linked siblings. - var firstNewDocument = newDocumentsAndHashes[0].newDocument; + // Don't need to do anything if this document has no linked siblings. + var firstNewDocument = newDocumentsAndHashes[0].newDocument; - var relatedDocuments = newSolution.GetRelatedDocumentIds(firstNewDocument.Id); - Contract.ThrowIfTrue(relatedDocuments.Length == 1, "We should have skipped non-linked files in the prior loop."); + var relatedDocuments = newSolution.GetRelatedDocumentIds(firstNewDocument.Id); + Contract.ThrowIfTrue(relatedDocuments.Length == 1, "We should have skipped non-linked files in the prior loop."); - if (newDocumentsAndHashes.Count == 1) - { - // The file has linked siblings, but we collapsed down to only one actual document change. Ensure that - // any linked files have that same content as well. - var firstSourceText = await firstNewDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - updatedSolution = updatedSolution.WithDocumentTexts( - relatedDocuments.SelectAsArray(d => (d, firstSourceText))); - } - else - { - // Otherwise, merge the changes and set all the linked files to that merged content. - var mergeGroupResult = await MergeLinkedDocumentGroupAsync(newDocumentsAndHashes, sessionInfo, mergeConflictHandler, cancellationToken).ConfigureAwait(false); - linkedFileMergeResults.Add(mergeGroupResult); - updatedSolution = updatedSolution.WithDocumentTexts( - relatedDocuments.SelectAsArray(d => (d, mergeGroupResult.MergedSourceText))); + if (newDocumentsAndHashes.Count == 1) + { + // The file has linked siblings, but we collapsed down to only one actual document change. Ensure that + // any linked files have that same content as well. + var firstSourceText = await firstNewDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + updatedSolution = updatedSolution.WithDocumentTexts( + relatedDocuments.SelectAsArray(d => (d, firstSourceText))); + } + else + { + // Otherwise, merge the changes and set all the linked files to that merged content. + var mergeGroupResult = await MergeLinkedDocumentGroupAsync(newDocumentsAndHashes, sessionInfo, mergeConflictHandler, cancellationToken).ConfigureAwait(false); + linkedFileMergeResults.Add(mergeGroupResult); + updatedSolution = updatedSolution.WithDocumentTexts( + relatedDocuments.SelectAsArray(d => (d, mergeGroupResult.MergedSourceText))); + } } - } - return new LinkedFileMergeSessionResult(updatedSolution, linkedFileMergeResults); + return new LinkedFileMergeSessionResult(updatedSolution, linkedFileMergeResults); + } + finally + { + foreach (var (_, newDocumentsAndHashes) in filePathToNewDocumentsAndHashes) + newDocumentsAndHashes.Free(); + } } private async Task MergeLinkedDocumentGroupAsync( - List<(Document newDocument, ImmutableArray newContentHash)> newDocumentsAndHashes, + DocumentAndHashBuilder newDocumentsAndHashes, LinkedFileDiffMergingSessionInfo sessionInfo, IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) From 5cfd5dcb6a5951abc2aded08a718404ebcdb4432 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:37:37 -0700 Subject: [PATCH 249/423] Remove unreported telemetry --- .../LinkedFileDiffMergingSession.cs | 60 +------------------ 1 file changed, 3 insertions(+), 57 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 4cecd7a09a3db..6e2d90d77f91f 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -22,8 +22,6 @@ internal sealed class LinkedFileDiffMergingSession(Solution oldSolution, Solutio { internal async Task MergeDiffsAsync(IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { - var sessionInfo = new LinkedFileDiffMergingSessionInfo(); - using var _1 = PooledDictionary.GetInstance(out var filePathToNewDocumentsAndHashes); try { @@ -75,7 +73,7 @@ internal async Task MergeDiffsAsync(IMergeConflict else { // Otherwise, merge the changes and set all the linked files to that merged content. - var mergeGroupResult = await MergeLinkedDocumentGroupAsync(newDocumentsAndHashes, sessionInfo, mergeConflictHandler, cancellationToken).ConfigureAwait(false); + var mergeGroupResult = await MergeLinkedDocumentGroupAsync(newDocumentsAndHashes, mergeConflictHandler, cancellationToken).ConfigureAwait(false); linkedFileMergeResults.Add(mergeGroupResult); updatedSolution = updatedSolution.WithDocumentTexts( relatedDocuments.SelectAsArray(d => (d, mergeGroupResult.MergedSourceText))); @@ -93,14 +91,11 @@ internal async Task MergeDiffsAsync(IMergeConflict private async Task MergeLinkedDocumentGroupAsync( DocumentAndHashBuilder newDocumentsAndHashes, - LinkedFileDiffMergingSessionInfo sessionInfo, IMergeConflictHandler? mergeConflictHandler, CancellationToken cancellationToken) { Contract.ThrowIfTrue(newDocumentsAndHashes.Count < 2); - var groupSessionInfo = new LinkedFileGroupSessionInfo(); - // Automatically merge non-conflicting diffs while collecting the conflicting diffs var textDifferencingService = oldSolution.Services.GetRequiredService(); @@ -123,7 +118,6 @@ private async Task MergeLinkedDocumentGroupAsync( siblingNewDocument, appliedChanges, unmergedChanges, - groupSessionInfo, textDifferencingService, cancellationToken).ConfigureAwait(false); } @@ -138,7 +132,7 @@ private async Task MergeLinkedDocumentGroupAsync( mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); - (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits, groupSessionInfo); + (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits); } else { @@ -147,9 +141,6 @@ private async Task MergeLinkedDocumentGroupAsync( } var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); - groupSessionInfo.LinkedDocuments = linkedDocuments.Length; - groupSessionInfo.DocumentsWithChanges = newDocumentsAndHashes.Count; - sessionInfo.LogLinkedFileResult(groupSessionInfo); return new LinkedFileMergeResult( linkedDocuments, @@ -162,7 +153,6 @@ private static async Task> AddDocumentMergeChangesAsy Document newDocument, ImmutableArray cumulativeChanges, ArrayBuilder unmergedChanges, - LinkedFileGroupSessionInfo groupSessionInfo, IDocumentTextDifferencingService textDiffService, CancellationToken cancellationToken) { @@ -180,8 +170,6 @@ private static async Task> AddDocumentMergeChangesAsy // Existing change that does not overlap with the current change in consideration successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]); cumulativeChangeIndex++; - - groupSessionInfo.IsolatedDiffs++; } if (cumulativeChangeIndex < cumulativeChanges.Length) @@ -191,8 +179,6 @@ private static async Task> AddDocumentMergeChangesAsy { // The current change in consideration does not intersect with any existing change successfullyMergedChanges.Add(change); - - groupSessionInfo.IsolatedDiffs++; } else { @@ -201,24 +187,12 @@ private static async Task> AddDocumentMergeChangesAsy // The current change in consideration overlaps an existing change but // the changes are not identical. unmergedDocumentChanges.Add(change); - - groupSessionInfo.OverlappingDistinctDiffs++; - if (change.Span == cumulativeChange.Span) - { - groupSessionInfo.OverlappingDistinctDiffsWithSameSpan++; - if (change.NewText!.Contains(cumulativeChange.NewText!) || cumulativeChange.NewText!.Contains(change.NewText)) - { - groupSessionInfo.OverlappingDistinctDiffsWithSameSpanAndSubstringRelation++; - } - } } else { // The current change in consideration is identical to an existing change successfullyMergedChanges.Add(change); cumulativeChangeIndex++; - - groupSessionInfo.IdenticalDiffs++; } } } @@ -226,8 +200,6 @@ private static async Task> AddDocumentMergeChangesAsy { // The current change in consideration does not intersect with any existing change successfullyMergedChanges.Add(change); - - groupSessionInfo.IsolatedDiffs++; } } @@ -236,7 +208,6 @@ private static async Task> AddDocumentMergeChangesAsy // Existing change that does not overlap with the current change in consideration successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]); cumulativeChangeIndex++; - groupSessionInfo.IsolatedDiffs++; } if (unmergedDocumentChanges.Count != 0) @@ -252,8 +223,7 @@ private static async Task> AddDocumentMergeChangesAsy private static (ImmutableArray mergeChanges, ImmutableArray mergeConflictResolutionSpans) MergeChangesWithMergeFailComments( ImmutableArray mergedChanges, - ImmutableArray commentChanges, - LinkedFileGroupSessionInfo groupSessionInfo) + ImmutableArray commentChanges) { var mergedChangesList = NormalizeChanges(mergedChanges); var commentChangesList = NormalizeChanges(commentChanges); @@ -310,9 +280,6 @@ private static (ImmutableArray mergeChanges, ImmutableArray NormalizeChanges(ImmutableArray LinkedFileGroups = []; - - public void LogLinkedFileResult(LinkedFileGroupSessionInfo info) - => LinkedFileGroups.Add(info); - } - - internal sealed class LinkedFileGroupSessionInfo - { - public int LinkedDocuments; - public int DocumentsWithChanges; - public int IsolatedDiffs; - public int IdenticalDiffs; - public int OverlappingDistinctDiffs; - public int OverlappingDistinctDiffsWithSameSpan; - public int OverlappingDistinctDiffsWithSameSpanAndSubstringRelation; - public int InsertedMergeConflictComments; - public int InsertedMergeConflictCommentsAtAdjustedLocation; - } } From dc069e0776daf658cb0c24029319923c68187347 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:37:54 -0700 Subject: [PATCH 250/423] Cleanup --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 6e2d90d77f91f..5c5c8c8ecb340 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Runtime; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; From acfa57a38014dee44f882095307ad7345e8eecb0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 14:54:08 -0700 Subject: [PATCH 251/423] implement --- .../InlineRenameSession.OpenTextBufferManager.cs | 2 +- ...stractLinkedFileMergeConflictCommentAdditionService.cs | 8 +++++--- .../LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs | 3 ++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs index 18a99ec18cde6..492e1b75c1b09 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameSession.OpenTextBufferManager.cs @@ -454,7 +454,7 @@ internal void ApplyConflictResolutionEdits(IInlineRenameReplacementInfo conflict // Show merge conflicts comments as unresolvable conflicts, and do not // show any other rename-related spans that overlap a merge conflict comment. mergeResult.MergeConflictCommentSpans.TryGetValue(document.Id, out var mergeConflictComments); - mergeConflictComments ??= []; + mergeConflictComments = mergeConflictComments.NullToEmpty(); foreach (var conflict in mergeConflictComments) { diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs index e8599a8880c70..3f76b79b27406 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/AbstractLinkedFileMergeConflictCommentAdditionService.cs @@ -5,8 +5,10 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -16,9 +18,9 @@ internal abstract class AbstractLinkedFileMergeConflictCommentAdditionService : { internal abstract string GetConflictCommentText(string header, string beforeString, string afterString); - public List CreateEdits(SourceText originalSourceText, List unmergedChanges) + public ImmutableArray CreateEdits(SourceText originalSourceText, ArrayBuilder unmergedChanges) { - var commentChanges = new List(); + using var _ = ArrayBuilder.GetInstance(out var commentChanges); foreach (var documentWithChanges in unmergedChanges) { @@ -28,7 +30,7 @@ public List CreateEdits(SourceText originalSourceText, List> PartitionChangesForDocument(IEnumerable changes, SourceText originalSourceText) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs index 093b68bbf178e..0d3f426e966a6 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeSessionResult.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -12,7 +13,7 @@ internal sealed class LinkedFileMergeSessionResult { public Solution MergedSolution { get; } - public readonly Dictionary> MergeConflictCommentSpans = []; + public readonly Dictionary> MergeConflictCommentSpans = []; public LinkedFileMergeSessionResult(Solution mergedSolution, ArrayBuilder fileMergeResults) { From 2bf8bc6e612323f8ff3570f80cefca0a65a3804e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:00:45 -0700 Subject: [PATCH 252/423] Cleanup --- .../LinkedFileDiffMergingSession.cs | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 5c5c8c8ecb340..c7faa720d7067 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -123,28 +123,16 @@ private async Task MergeLinkedDocumentGroupAsync( // Add comments in source explaining diffs that could not be merged - ImmutableArray allChanges; - ImmutableArray mergeConflictResolutionSpans; + var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); - if (unmergedChanges.Count != 0) - { - mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); - var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); + if (unmergedChanges.Count == 0) + return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(appliedChanges), []); - (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits); - } - else - { - allChanges = appliedChanges; - mergeConflictResolutionSpans = []; - } - - var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); + mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); + var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); - return new LinkedFileMergeResult( - linkedDocuments, - firstOldSourceText.WithChanges(allChanges), - mergeConflictResolutionSpans); + var (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits); + return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(allChanges), mergeConflictResolutionSpans); } private static async Task> AddDocumentMergeChangesAsync( From 545729ce85c303f38485182671fbe9cdb3f7efda Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:03:00 -0700 Subject: [PATCH 253/423] rename --- .../LinkedFileDiffMergingSession.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index c7faa720d7067..bb0bc46183a60 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -103,7 +103,7 @@ private async Task MergeLinkedDocumentGroupAsync( var firstOldDocument = oldSolution.GetRequiredDocument(firstNewDocument.Id); var firstOldSourceText = await firstOldDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); - var appliedChanges = await textDifferencingService.GetTextChangesAsync( + var allTextChangesAcrossLinkedFiles = await textDifferencingService.GetTextChangesAsync( firstOldDocument, firstNewDocument, TextDifferenceTypes.Line, cancellationToken).ConfigureAwait(false); using var _ = ArrayBuilder.GetInstance(out var unmergedChanges); @@ -112,10 +112,10 @@ private async Task MergeLinkedDocumentGroupAsync( var siblingNewDocument = newDocumentsAndHashes[i].newDocument; var siblingOldDocument = oldSolution.GetRequiredDocument(siblingNewDocument.Id); - appliedChanges = await AddDocumentMergeChangesAsync( + allTextChangesAcrossLinkedFiles = await AddDocumentMergeChangesAsync( siblingOldDocument, siblingNewDocument, - appliedChanges, + allTextChangesAcrossLinkedFiles, unmergedChanges, textDifferencingService, cancellationToken).ConfigureAwait(false); @@ -126,12 +126,12 @@ private async Task MergeLinkedDocumentGroupAsync( var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); if (unmergedChanges.Count == 0) - return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(appliedChanges), []); + return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(allTextChangesAcrossLinkedFiles), []); mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); - var (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(appliedChanges, mergeConflictTextEdits); + var (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(allTextChangesAcrossLinkedFiles, mergeConflictTextEdits); return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(allChanges), mergeConflictResolutionSpans); } From 7dd6ad34d61ac050621c91cdef834ce9048a3267 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:05:50 -0700 Subject: [PATCH 254/423] Simplify --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index bb0bc46183a60..337728b402f0f 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -217,8 +217,8 @@ private static (ImmutableArray mergeChanges, ImmutableArray.GetInstance(out var combinedChanges); using var _2 = ArrayBuilder.GetInstance(out var mergeConflictResolutionSpans); - var insertedMergeConflictCommentsAtAdjustedLocation = 0; + var insertedMergeConflictCommentsAtAdjustedLocation = 0; var commentChangeIndex = 0; var currentPositionDelta = 0; From f30cd90ecda498e77b149143f5da970f37078179 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:07:16 -0700 Subject: [PATCH 255/423] Readonly --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 6 +++--- .../LinkedFileDiffMerging/UnmergedDocumentChanges.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 337728b402f0f..8ff994bd0d894 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -143,8 +143,8 @@ private static async Task> AddDocumentMergeChangesAsy IDocumentTextDifferencingService textDiffService, CancellationToken cancellationToken) { - var unmergedDocumentChanges = new List(); - using var _ = ArrayBuilder.GetInstance(out var successfullyMergedChanges); + using var _1 = ArrayBuilder.GetInstance(out var unmergedDocumentChanges); + using var _2 = ArrayBuilder.GetInstance(out var successfullyMergedChanges); var cumulativeChangeIndex = 0; @@ -200,7 +200,7 @@ private static async Task> AddDocumentMergeChangesAsy if (unmergedDocumentChanges.Count != 0) { unmergedChanges.Add(new UnmergedDocumentChanges( - unmergedDocumentChanges, + unmergedDocumentChanges.ToImmutableAndClear(), oldDocument.Project.Name, oldDocument.Id)); } diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs index f205b4ddf473f..475a678b81355 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis; -internal sealed class UnmergedDocumentChanges(List unmergedChanges, string projectName, DocumentId documentId) +internal sealed class UnmergedDocumentChanges(ImmutableArray unmergedChanges, string projectName, DocumentId documentId) { - public List UnmergedChanges { get; } = unmergedChanges; + public ImmutableArray UnmergedChanges { get; } = unmergedChanges; public string ProjectName { get; } = projectName; public DocumentId DocumentId { get; } = documentId; } From 6f6f62a5ecb9d7bb9c08fbda99d817f3294aca12 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:08:01 -0700 Subject: [PATCH 256/423] struct --- .../Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs index 475a678b81355..cb20462462857 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs @@ -7,7 +7,7 @@ namespace Microsoft.CodeAnalysis; -internal sealed class UnmergedDocumentChanges(ImmutableArray unmergedChanges, string projectName, DocumentId documentId) +internal readonly struct UnmergedDocumentChanges(ImmutableArray unmergedChanges, string projectName, DocumentId documentId) { public ImmutableArray UnmergedChanges { get; } = unmergedChanges; public string ProjectName { get; } = projectName; From 22cd422ad22dc69d74582f55ab90d6d44aaa6a05 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:08:27 -0700 Subject: [PATCH 257/423] struct --- .../LinkedFileDiffMerging/UnmergedDocumentChanges.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs index cb20462462857..d5f463427e08d 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/UnmergedDocumentChanges.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis; internal readonly struct UnmergedDocumentChanges(ImmutableArray unmergedChanges, string projectName, DocumentId documentId) { - public ImmutableArray UnmergedChanges { get; } = unmergedChanges; - public string ProjectName { get; } = projectName; - public DocumentId DocumentId { get; } = documentId; + public readonly ImmutableArray UnmergedChanges = unmergedChanges; + public readonly string ProjectName = projectName; + public readonly DocumentId DocumentId = documentId; } From b38278a0d185a60d9e04ac8f4952d8a5047e6060 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:08:58 -0700 Subject: [PATCH 258/423] struct --- .../LinkedFileDiffMerging/LinkedFileMergeResult.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs index 3a4791202ce41..daf2558de6b42 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileMergeResult.cs @@ -7,10 +7,10 @@ namespace Microsoft.CodeAnalysis; -internal sealed class LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, ImmutableArray mergeConflictResolutionSpans) +internal readonly struct LinkedFileMergeResult(ImmutableArray documentIds, SourceText mergedSourceText, ImmutableArray mergeConflictResolutionSpans) { - public ImmutableArray DocumentIds => documentIds; - public SourceText MergedSourceText => mergedSourceText; - public ImmutableArray MergeConflictResolutionSpans => mergeConflictResolutionSpans; + public readonly ImmutableArray DocumentIds = documentIds; + public readonly SourceText MergedSourceText = mergedSourceText; + public readonly ImmutableArray MergeConflictResolutionSpans = mergeConflictResolutionSpans; public bool HasMergeConflicts => !MergeConflictResolutionSpans.IsEmpty; } From 5fadd38f232ea94eb526dde1ed6c6ce2dadc3bf9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 10 May 2024 15:10:21 -0700 Subject: [PATCH 259/423] comment --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 8ff994bd0d894..69af2720739bf 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -294,6 +294,7 @@ private static ImmutableArray NormalizeChanges(ImmutableArray Date: Fri, 10 May 2024 15:11:22 -0700 Subject: [PATCH 260/423] simplify --- .../LinkedFileDiffMergingSession.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 69af2720739bf..5e40dcc200240 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -229,7 +228,7 @@ private static (ImmutableArray mergeChanges, ImmutableArray mergeChanges, ImmutableArray mergeChanges, ImmutableArray mergeChanges, ImmutableArray Date: Fri, 10 May 2024 15:12:19 -0700 Subject: [PATCH 261/423] comment --- .../LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs index 5e40dcc200240..f55837400141f 100644 --- a/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs +++ b/src/Workspaces/Core/Portable/LinkedFileDiffMerging/LinkedFileDiffMergingSession.cs @@ -120,8 +120,6 @@ private async Task MergeLinkedDocumentGroupAsync( cancellationToken).ConfigureAwait(false); } - // Add comments in source explaining diffs that could not be merged - var linkedDocuments = oldSolution.GetRelatedDocumentIds(firstOldDocument.Id); if (unmergedChanges.Count == 0) @@ -130,6 +128,7 @@ private async Task MergeLinkedDocumentGroupAsync( mergeConflictHandler ??= firstOldDocument.GetRequiredLanguageService(); var mergeConflictTextEdits = mergeConflictHandler.CreateEdits(firstOldSourceText, unmergedChanges); + // Add comments in source explaining diffs that could not be merged var (allChanges, mergeConflictResolutionSpans) = MergeChangesWithMergeFailComments(allTextChangesAcrossLinkedFiles, mergeConflictTextEdits); return new LinkedFileMergeResult(linkedDocuments, firstOldSourceText.WithChanges(allChanges), mergeConflictResolutionSpans); } From 4bbcd1d34db50d6f5fd778a03f72acc6689e2672 Mon Sep 17 00:00:00 2001 From: Arun Chander Date: Fri, 10 May 2024 16:50:23 -0700 Subject: [PATCH 262/423] =?UTF-8?q?Revert=20"Move=20AsyncBatchingWorkQueue?= =?UTF-8?q?=20usage=20in=20telemetry=20to=20TelemetryLogging=20le=E2=80=A6?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 21181a7a2c4af7b8784fc6963c892b2af8a2693e. --- .../Handler/RequestTelemetryLogger.cs | 13 ++---- .../Shared/AggregatingTelemetryLog.cs | 6 ++- .../Shared/AggregatingTelemetryLogManager.cs | 40 +++++++++++++++---- .../Telemetry/Shared/TelemetryLogProvider.cs | 8 ++-- .../Portable/Telemetry/TelemetryLogging.cs | 34 +--------------- 5 files changed, 47 insertions(+), 54 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs b/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs index 7341d04b9c5d9..1ff18f536c1e3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestTelemetryLogger.cs @@ -36,8 +36,6 @@ public RequestTelemetryLogger(string serverTypeName) _requestCounters = new(); _findDocumentResults = new(); _usedForkedSolutionCounter = new(); - - TelemetryLogging.Flushed += OnFlushed; } public void UpdateFindDocumentTelemetryData(bool success, string? workspaceKind) @@ -94,14 +92,6 @@ public void Dispose() return; } - // Flush all telemetry logged through TelemetryLogging - TelemetryLogging.Flush(); - - TelemetryLogging.Flushed -= OnFlushed; - } - - private void OnFlushed(object? sender, EventArgs e) - { foreach (var kvp in _requestCounters) { TelemetryLogging.Log(FunctionId.LSP_RequestCounter, KeyValueLogMessage.Create(LogType.Trace, m => @@ -134,6 +124,9 @@ private void OnFlushed(object? sender, EventArgs e) } })); + // Flush all telemetry logged through TelemetryLogging + TelemetryLogging.Flush(); + _requestCounters.Clear(); } diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs index 711cf06437e77..042a8bb5877a1 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLog.cs @@ -28,6 +28,7 @@ internal sealed class AggregatingTelemetryLog : ITelemetryLog private readonly HistogramConfiguration? _histogramConfiguration; private readonly string _eventName; private readonly FunctionId _functionId; + private readonly AggregatingTelemetryLogManager _aggregatingTelemetryLogManager; private readonly object _flushLock; private ImmutableDictionary Histogram, TelemetryEvent TelemetryEvent, object Lock)> _histograms = ImmutableDictionary, TelemetryEvent, object)>.Empty; @@ -39,7 +40,7 @@ internal sealed class AggregatingTelemetryLog : ITelemetryLog /// Used to derive meter name /// Optional values indicating bucket boundaries in milliseconds. If not specified, /// all histograms created will use the default histogram configuration - public AggregatingTelemetryLog(TelemetrySession session, FunctionId functionId, double[]? bucketBoundaries) + public AggregatingTelemetryLog(TelemetrySession session, FunctionId functionId, double[]? bucketBoundaries, AggregatingTelemetryLogManager aggregatingTelemetryLogManager) { var meterName = TelemetryLogger.GetPropertyName(functionId, "meter"); var meterProvider = new VSTelemetryMeterProvider(); @@ -48,6 +49,7 @@ public AggregatingTelemetryLog(TelemetrySession session, FunctionId functionId, _meter = meterProvider.CreateMeter(meterName, version: MeterVersion); _eventName = TelemetryLogger.GetEventName(functionId); _functionId = functionId; + _aggregatingTelemetryLogManager = aggregatingTelemetryLogManager; _flushLock = new(); if (bucketBoundaries != null) @@ -102,6 +104,8 @@ public void Log(KeyValueLogMessage logMessage) { histogram.Record(value); } + + _aggregatingTelemetryLogManager.EnsureTelemetryWorkQueued(); } public IDisposable? LogBlockTime(KeyValueLogMessage logMessage, int minThresholdMs) diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs index 2b3042e607592..1188da45610ee 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingTelemetryLogManager.cs @@ -2,24 +2,39 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Telemetry; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Telemetry; /// -/// Manages creation and obtaining aggregated telemetry logs. +/// Manages creation and obtaining aggregated telemetry logs. Also, notifies logs to +/// send aggregated events every 30 minutes. /// internal sealed class AggregatingTelemetryLogManager { + private static readonly TimeSpan s_batchedTelemetryCollectionPeriod = TimeSpan.FromMinutes(30); + private readonly TelemetrySession _session; + private readonly AsyncBatchingWorkQueue _postTelemetryQueue; private ImmutableDictionary _aggregatingLogs = ImmutableDictionary.Empty; - public AggregatingTelemetryLogManager(TelemetrySession session) + public AggregatingTelemetryLogManager(TelemetrySession session, IAsynchronousOperationListener asyncListener) { _session = session; + + _postTelemetryQueue = new AsyncBatchingWorkQueue( + s_batchedTelemetryCollectionPeriod, + PostCollectedTelemetryAsync, + asyncListener, + CancellationToken.None); } public ITelemetryLog? GetLog(FunctionId functionId, double[]? bucketBoundaries) @@ -27,11 +42,22 @@ public AggregatingTelemetryLogManager(TelemetrySession session) if (!_session.IsOptedIn) return null; - return ImmutableInterlocked.GetOrAdd( - ref _aggregatingLogs, - functionId, - static (functionId, arg) => new AggregatingTelemetryLog(arg._session, functionId, arg.bucketBoundaries), - factoryArgument: (_session, bucketBoundaries)); + return ImmutableInterlocked.GetOrAdd(ref _aggregatingLogs, functionId, functionId => new AggregatingTelemetryLog(_session, functionId, bucketBoundaries, this)); + } + + public void EnsureTelemetryWorkQueued() + { + // Ensure PostCollectedTelemetryAsync will get fired after the collection period. + _postTelemetryQueue.AddWork(); + } + + private ValueTask PostCollectedTelemetryAsync(CancellationToken token) + { + token.ThrowIfCancellationRequested(); + + Flush(); + + return ValueTaskFactory.CompletedTask; } public void Flush() diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs index 16e44077e4178..3bc330135068c 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/TelemetryLogProvider.cs @@ -17,17 +17,17 @@ internal sealed class TelemetryLogProvider : ITelemetryLogProvider private readonly AggregatingTelemetryLogManager _aggregatingTelemetryLogManager; private readonly VisualStudioTelemetryLogManager _visualStudioTelemetryLogManager; - private TelemetryLogProvider(TelemetrySession session, ILogger telemetryLogger) + private TelemetryLogProvider(TelemetrySession session, ILogger telemetryLogger, IAsynchronousOperationListener asyncListener) { - _aggregatingTelemetryLogManager = new AggregatingTelemetryLogManager(session); + _aggregatingTelemetryLogManager = new AggregatingTelemetryLogManager(session, asyncListener); _visualStudioTelemetryLogManager = new VisualStudioTelemetryLogManager(session, telemetryLogger); } public static TelemetryLogProvider Create(TelemetrySession session, ILogger telemetryLogger, IAsynchronousOperationListener asyncListener) { - var logProvider = new TelemetryLogProvider(session, telemetryLogger); + var logProvider = new TelemetryLogProvider(session, telemetryLogger, asyncListener); - TelemetryLogging.SetLogProvider(logProvider, asyncListener); + TelemetryLogging.SetLogProvider(logProvider); return logProvider; } diff --git a/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs b/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs index 23aded8e6276a..b26b770f7dcf0 100644 --- a/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs +++ b/src/Workspaces/Core/Portable/Telemetry/TelemetryLogging.cs @@ -3,44 +3,26 @@ // See the LICENSE file in the project root for more information. using System; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Telemetry; /// /// Provides access to posting telemetry events or adding information -/// to aggregated telemetry events. Posts pending telemetry at 30 -/// minute intervals. +/// to aggregated telemetry events. /// internal static class TelemetryLogging { private static ITelemetryLogProvider? s_logProvider; - private static AsyncBatchingWorkQueue? s_postTelemetryQueue; public const string KeyName = "Name"; public const string KeyValue = "Value"; public const string KeyLanguageName = "LanguageName"; public const string KeyMetricName = "MetricName"; - public static event EventHandler? Flushed; - - public static void SetLogProvider(ITelemetryLogProvider logProvider, IAsynchronousOperationListener asyncListener) + public static void SetLogProvider(ITelemetryLogProvider logProvider) { s_logProvider = logProvider; - - InterlockedOperations.Initialize(ref s_postTelemetryQueue, () => - new AsyncBatchingWorkQueue( - TimeSpan.FromMinutes(30), - PostCollectedTelemetryAsync, - asyncListener, - CancellationToken.None)); - - // Add the initial item to the queue to ensure later processing. - s_postTelemetryQueue?.AddWork(); } /// @@ -130,17 +112,5 @@ public static void LogAggregated(FunctionId functionId, KeyValueLogMessage logMe public static void Flush() { s_logProvider?.Flush(); - - Flushed?.Invoke(null, EventArgs.Empty); - } - - private static ValueTask PostCollectedTelemetryAsync(CancellationToken cancellationToken) - { - Flush(); - - // Ensure PostCollectedTelemetryAsync will get fired again after the collection period. - s_postTelemetryQueue?.AddWork(); - - return ValueTaskFactory.CompletedTask; } } From f76408516e591484bdc64c684c097f78564484d3 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sat, 11 May 2024 19:51:20 +0300 Subject: [PATCH 263/423] Make placement of raw string guide line independent of the space char used --- ...ringIndentationAdornmentManager.VisibleBlock.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs index 6f1533b0da943..a4e0d164dfed9 100644 --- a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs +++ b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.VisibleBlock.cs @@ -44,17 +44,21 @@ private VisibleBlock(double x, ImmutableArray<(double start, double end)> ySegme if (span.End.GetContainingLine().Start == span.End) return null; - // We want to draw the line right before the quote character. So -1 to get that character's position. - // Horizontally position the adornment in the center of the character. This position could actually be - // 0 if the entire doc is deleted and we haven't recomputed the updated tags yet. So be resilient for - // the position being out of bounds. + // This position could actually be 0 if the entire doc is deleted + // and we haven't recomputed the updated tags yet. So be resilient + // for the position being out of bounds. if (span.End == 0) return null; + // We want to draw the line right before the quote character. So -1 to get that character's position. + // If we position the adornment right at the end of that character it will visually merge with the + // text, so we want to back it off a little bit to the left. Half of a virtual space is gonna do the trick. + // It is important to keep this value independent of what space character is used to avoid visual bugs + // like https://github.com/dotnet/roslyn/issues/64230 var bufferPosition = span.End - 1; var anchorPointLine = view.GetTextViewLineContainingBufferPosition(bufferPosition); var bounds = anchorPointLine.GetCharacterBounds(bufferPosition); - var x = Math.Floor((bounds.Left + bounds.Right) * 0.5); + var x = Math.Floor(bounds.Right - (anchorPointLine.VirtualSpaceWidth / 2)); var firstLine = view.TextViewLines.FirstVisibleLine; var lastLine = view.TextViewLines.LastVisibleLine; From 8bdd4972eb6a8768b39bfd7e8732bac856f8ae7f Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sun, 12 May 2024 19:12:43 +0300 Subject: [PATCH 264/423] Port C# "Invert if" refactoring tests to `VerifyCS` --- .../CSharpInvertIfCodeRefactoringProvider.cs | 11 +- .../InvertIf/InvertIfTests.Elseless.cs | 1223 ++++----- .../CSharpTest/InvertIf/InvertIfTests.cs | 2408 ++++++++--------- 3 files changed, 1778 insertions(+), 1864 deletions(-) diff --git a/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs index 3daf3339608c8..83eb0d70ac6b4 100644 --- a/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/InvertIf/CSharpInvertIfCodeRefactoringProvider.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InvertIf; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -21,15 +22,11 @@ namespace Microsoft.CodeAnalysis.CSharp.InvertIf; using static SyntaxFactory; [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InvertIf), Shared] -internal sealed class CSharpInvertIfCodeRefactoringProvider : AbstractInvertIfCodeRefactoringProvider< +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpInvertIfCodeRefactoringProvider() : AbstractInvertIfCodeRefactoringProvider< SyntaxKind, StatementSyntax, IfStatementSyntax, StatementSyntax> { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpInvertIfCodeRefactoringProvider() - { - } - protected override string GetTitle() => CSharpFeaturesResources.Invert_if; diff --git a/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs b/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs index 856f322b68f74..b69f6484d85c0 100644 --- a/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs +++ b/src/Features/CSharpTest/InvertIf/InvertIfTests.Elseless.cs @@ -3,177 +3,90 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf; + +public partial class InvertIfTests { - [Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)] - public partial class InvertIfTests + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause1() { - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause1() - { - await TestInRegularAndScriptAsync( - """ - class C + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - if (c) - { - [||]if (c) - { - return 1; - } - } - return 2; - } - } - } - """, - """ - class C - { - void M() - { - switch (o) - { - case 1: - if (c) - { - [||]if (!c) - { - } - else - { - return 1; - } - } - return 2; - } - } - } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause2() - { - await TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - switch (o) - { - case 1: + case 1: + if (c) + { [||]if (c) { - f(); + return 1; } - g(); - g(); - break; - } + } + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: + case 1: + if (c) + { [||]if (!c) { } else { - f(); + return 1; } - g(); - g(); - break; - } - } - } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause3() - { - await TestInRegularAndScriptAsync( - """ - class C - { - void M() - { - [||]if (c) - { - f(); - } - g(); - g(); - } - } - """, - """ - class C - { - void M() - { - if (!c) - { - } - else - { - f(); - } - g(); - g(); + } + return 2; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause4() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause2() + { + await TestAsync(""" + class C + { + void M() { - bool M() + switch (o) { - if (c) - { + case 1: [||]if (c) { f(); } g(); - } - return false; + g(); + break; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - bool M() + switch (o) { - if (c) - { - if (!c) + case 1: + [||]if (!c) { } else @@ -181,36 +94,74 @@ bool M() f(); } g(); - } - return false; + g(); + break; + } + } + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause3() + { + await TestAsync(""" + class C + { + void M() + { + [||]if (c) + { + f(); + } + g(); + g(); + } + } + """, """ + class C + { + void M() + { + if (!c) + { + } + else + { + f(); } + g(); + g(); } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause5() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause4() + { + await TestAsync(""" + class C + { + bool M() { - void M() + if (c) { [||]if (c) { f(); } - - g(); g(); } + return false; } - """, - """ - class C + } + """, """ + class C + { + bool M() { - void M() + if (c) { if (!c) { @@ -219,435 +170,487 @@ void M() { f(); } - - g(); g(); } + return false; + } + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause5() + { + await TestAsync(""" + class C + { + void M() + { + [||]if (c) + { + f(); + } + + g(); + g(); } - """); - } + } + """, """ + class C + { + void M() + { + if (!c) + { + } + else + { + f(); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause6() - { - await TestInRegularAndScriptAsync( - """ - class C + g(); + g(); + } + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause6() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (c) + case 1: + [||]if (c) + { + if (c) { - if (c) - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (!c) - { - } - else + case 1: + [||]if (!c) + { + } + else + { + if (c) { - if (c) - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveIfBodyToElseClause7() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveIfBodyToElseClause7() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - if (c) + case 1: + if (c) + { + [||]if (c) { - [||]if (c) - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - if (c) + case 1: + if (c) + { + if (!c) + { + } + else { - if (!c) - { - } - else - { - f(); - return 1; - } + f(); + return 1; } + } - f(); - return 2; - } + f(); + return 2; } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40909")] - public async Task IfWithoutElse_MoveIfBodyToElseClause8() - { - await TestInRegularAndScriptAsync( - """ - using System.Diagnostics; - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40909")] + public async Task IfWithoutElse_MoveIfBodyToElseClause8() + { + await TestAsync(""" + using System.Diagnostics; + class C + { + private static bool IsFalse(bool val) { - private static bool IsFalse(bool val) { + [|if|] (!val) { - [|if|] (!val) - { - return true; - } - Debug.Assert(val); + return true; } - return false; + Debug.Assert(val); } + return false; } - """, - """ - using System.Diagnostics; - class C + } + """, """ + using System.Diagnostics; + class C + { + private static bool IsFalse(bool val) { - private static bool IsFalse(bool val) { + if (val) { - if (val) - { - } - else - { - return true; - } - Debug.Assert(val); } - return false; + else + { + return true; + } + Debug.Assert(val); } + return false; } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) + [||]if (!c) + { + continue; + } + // comments + f(); + } + } + } + """, """ + class C + { + void M() + { + foreach (var item in list) + { + [||]if (c) { - [||]if (!c) - { - continue; - } // comments f(); } } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody2() + { + await TestAsync(""" + class C + { + void M() { - void M() + while (c) { - foreach (var item in list) + if (c) { [||]if (c) { - // comments - f(); + continue; } + if (c()) + return; } } } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody2() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + while (c) { - while (c) + if (c) { - if (c) + [||]if (!c) { - [||]if (c) - { - continue; - } if (c()) return; } } } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody3() + { + await TestAsync(""" + class C + { + void M() { - void M() + while (c) { - while (c) { - if (c) + [||]if (c) { - [||]if (!c) - { - if (c()) - return; - } + continue; } + if (c()) + return; } } } - """); - } - - [Fact] - public async Task IfWithoutElse_MoveSubsequentStatementsToIfBody3() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + while (c) { - while (c) { + [||]if (!c) { - [||]if (c) - { - continue; - } if (c()) return; } } } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - while (c) - { - { - [||]if (!c) - { - if (c()) - return; - } - } - } + [||]if (c) + break; + return; } } - """); - } - - [Fact] - public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements1() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) - { - [||]if (c) - break; + [||]if (!c) return; - } + break; } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements2() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) + [||]if (!c) { - [||]if (!c) - return; - break; + return; } + break; } } - """); - } - - [Fact] - public async Task IfWithoutElse_SwapIfBodyWithSubsequentStatements2() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) + [||]if (c) { - [||]if (!c) - { - return; - } break; } + return; } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_WithElseClause1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) - { - [||]if (c) - { - break; - } + [||]if (!c) return; - } + f(); } } - """); - } - - [Fact] - public async Task IfWithoutElse_WithElseClause1() - { - await TestInRegularAndScriptAsync( - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) - { - [||]if (!c) - return; + if (c) f(); - } + else + return; } } - """, - """ - class C + } + """); + } + + [Fact] + public async Task IfWithoutElse_WithNegatedCondition1() + { + await TestAsync(""" + class C + { + void M() + { + [||]if (c) { } + } + } + """, """ + class C + { + void M() { - void M() - { - foreach (var item in list) - { - if (c) - f(); - else - return; - } - } + if (!c) { } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNegatedCondition1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement1() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - [||]if (c) { } + [||]if (c) + { + f(); + } } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - if (!c) { } + [||]if (!c) + { + continue; + } + f(); } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement2() + { + await TestAsync(""" + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) { [||]if (c) { @@ -656,13 +659,14 @@ void M() } } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + foreach (var item in list) { - foreach (var item in list) { [||]if (!c) { @@ -672,252 +676,205 @@ void M() } } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement2() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement3() + { + await TestAsync(""" + class C + { + void M() { - void M() + [||]if (c) { - foreach (var item in list) - { - { - [||]if (c) - { - f(); - } - } - } + f(); } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + [||]if (!c) { - foreach (var item in list) - { - { - [||]if (!c) - { - continue; - } - f(); - } - } + return; } + f(); } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement3() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithNearmostJumpStatement4() + { + await TestAsync(""" + class C + { + void M() { - void M() + for (;;) { [||]if (c) { - f(); + break; } } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + for (;;) { [||]if (!c) { - return; + continue; } - f(); + break; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithNearmostJumpStatement4() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithSubsequentExitPointStatement1() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - for (;;) - { + case 1: [||]if (c) { - break; + f(); + f(); } - } + break; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - for (;;) - { + case 1: [||]if (!c) { - continue; + break; } + f(); + f(); break; - } } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithSubsequentExitPointStatement1() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task IfWithoutElse_WithSubsequentExitPointStatement2() + { + await TestAsync(""" + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (c) + case 1: + [||]if (c) + { + if (c) { - f(); - f(); + return 1; } - break; - } + } + + return 2; } } - """, - """ - class C + } + """, """ + class C + { + void M() { - void M() + switch (o) { - switch (o) - { - case 1: - [||]if (!c) - { - break; - } - f(); - f(); - break; - } + case 1: + [||]if (!c) + { + return 2; + } + if (c) + { + return 1; + } + + return 2; } } - """); - } + } + """); + } - [Fact] - public async Task IfWithoutElse_WithSubsequentExitPointStatement2() - { - await TestInRegularAndScriptAsync( - """ - class C + [Theory] + [InlineData("get")] + [InlineData("set")] + [InlineData("init")] + public async Task IfWithoutElse_InPropertyAccessors(string accessor) + { + await TestAsync($$""" + class C + { + private bool _b; + + public string Prop { - void M() + {{accessor}} { - switch (o) + [||]if (_b) { - case 1: - [||]if (c) - { - if (c) - { - return 1; - } - } - - return 2; + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); } } } - """, - """ - class C + } + """, $$""" + class C + { + private bool _b; + + public string Prop { - void M() + {{accessor}} { - switch (o) + if (!_b) { - case 1: - [||]if (!c) - { - return 2; - } - if (c) - { - return 1; - } - - return 2; + return; } + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(); } } - """); - } - - [Theory] - [InlineData("get")] - [InlineData("set")] - [InlineData("init")] - public async Task IfWithoutElse_InPropertyAccessors(string accessor) - { - await TestInRegularAndScriptAsync( -$@"class C -{{ - private bool _b; - - public string Prop - {{ - {accessor} - {{ - [||]if (_b) - {{ - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine(); - }} - }} - }} -}}", -$@"class C -{{ - private bool _b; - - public string Prop - {{ - {accessor} - {{ - if (!_b) - {{ - return; - }} - Console.WriteLine(); - Console.WriteLine(); - Console.WriteLine(); - }} - }} -}}"); - } + } + """); } } diff --git a/src/Features/CSharpTest/InvertIf/InvertIfTests.cs b/src/Features/CSharpTest/InvertIf/InvertIfTests.cs index 328d153609067..9dee33f7b2f7e 100644 --- a/src/Features/CSharpTest/InvertIf/InvertIfTests.cs +++ b/src/Features/CSharpTest/InvertIf/InvertIfTests.cs @@ -3,33 +3,28 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.InvertIf; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.InvertIf; + +[UseExportProvider, Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)] +public partial class InvertIfTests { - [Trait(Traits.Feature, Traits.Features.CodeActionsInvertIf)] - public partial class InvertIfTests : AbstractCSharpCodeActionTest_NoEditor + private static Task TestInsideMethodAsync( + string initial, + string expected) { - private async Task TestFixOneAsync( - string initial, - string expected) - { - await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected)); - } - - protected override CodeRefactoringProvider CreateCodeRefactoringProvider(TestWorkspace workspace, TestParameters parameters) - => new CSharpInvertIfCodeRefactoringProvider(); + return TestAsync(CreateTreeText(initial), CreateTreeText(expected)); - private static string CreateTreeText(string initial) + static string CreateTreeText(string initial) { - return - """ + return $$""" class A { bool a = true; @@ -39,428 +34,499 @@ class A void Goo() { - """ + initial + """ + {{initial}} } } """; } + } - [Fact] - public async Task TestSingleLine_Identifier() + private static async Task TestAsync(string initial, string expected, LanguageVersion languageVersion = LanguageVersion.Latest) + { + await new CSharpCodeRefactoringVerifier.Test { - await TestFixOneAsync( + TestCode = initial, + FixedCode = expected, + LanguageVersion = languageVersion, + CompilerDiagnostics = CompilerDiagnostics.None, + }.RunAsync(); + } + + [Fact] + public async Task TestSingleLine_Identifier() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else { b(); }", @"if (!a) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_IdentifierWithTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_IdentifierWithTrivia() + { + await TestInsideMethodAsync( @"[||]if /*0*/(/*1*/a/*2*/)/*3*/ { a(); } else { b(); }", @"if /*0*/(/*1*/!a/*2*/)/*3*/ { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NotIdentifier() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NotIdentifier() + { + await TestInsideMethodAsync( @"[||]if (!a) { a(); } else { b(); }", @"if (a) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NotIdentifierWithTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NotIdentifierWithTrivia() + { + await TestInsideMethodAsync( @"[||]if /*0*/(/*1*/!/*1b*/a/*2*/)/*3*/ { a(); } else { b(); }", @"if /*0*/(/*1*/a/*2*/)/*3*/ { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_EqualsEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_EqualsEquals() + { + await TestInsideMethodAsync( @"[||]if (a == b) { a(); } else { b(); }", @"if (a != b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NotEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NotEquals() + { + await TestInsideMethodAsync( @"[||]if (a != b) { a(); } else { b(); }", @"if (a == b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_GreaterThan() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_GreaterThan() + { + await TestInsideMethodAsync( @"[||]if (a > b) { a(); } else { b(); }", @"if (a <= b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_GreaterThanEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_GreaterThanEquals() + { + await TestInsideMethodAsync( @"[||]if (a >= b) { a(); } else { b(); }", @"if (a < b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_LessThan() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_LessThan() + { + await TestInsideMethodAsync( @"[||]if (a < b) { a(); } else { b(); }", @"if (a >= b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_LessThanEquals() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_LessThanEquals() + { + await TestInsideMethodAsync( @"[||]if (a <= b) { a(); } else { b(); }", @"if (a > b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_DoubleParentheses() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoubleParentheses() + { + await TestInsideMethodAsync( @"[||]if ((a)) { a(); } else { b(); }", @"if (!a) { b(); } else { a(); }"); - } + } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/26427")] - public async Task TestSingleLine_DoubleParenthesesWithInnerTrivia() - { - await TestFixOneAsync( + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/26427")] + public async Task TestSingleLine_DoubleParenthesesWithInnerTrivia() + { + await TestInsideMethodAsync( @"[||]if ((/*1*/a/*2*/)) { a(); } else { b(); }", @"if (/*1*/!a/*2*/) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_DoubleParenthesesWithMiddleTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoubleParenthesesWithMiddleTrivia() + { + await TestInsideMethodAsync( @"[||]if (/*1*/(a)/*2*/) { a(); } else { b(); }", @"if (/*1*/!a/*2*/) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_DoubleParenthesesWithOutsideTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoubleParenthesesWithOutsideTrivia() + { + await TestInsideMethodAsync( @"[||]if /*before*/((a))/*after*/ { a(); } else { b(); }", @"if /*before*/(!a)/*after*/ { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Is() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Is() + { + await TestInsideMethodAsync( @"[||]if (a is Goo) { a(); } else { b(); }", @"if (a is not Goo) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_MethodCall() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_MethodCall() + { + await TestInsideMethodAsync( @"[||]if (a.Goo()) { a(); } else { b(); }", @"if (!a.Goo()) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or() + { + await TestInsideMethodAsync( @"[||]if (a || b) { a(); } else { b(); }", @"if (!a && !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or2() + { + await TestInsideMethodAsync( @"[||]if (!a || !b) { a(); } else { b(); }", @"if (a && b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or3() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or3() + { + await TestInsideMethodAsync( @"[||]if (!a || b) { a(); } else { b(); }", @"if (a && !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Or4() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Or4() + { + await TestInsideMethodAsync( @"[||]if (a | b) { a(); } else { b(); }", @"if (!a & !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And() + { + await TestInsideMethodAsync( @"[||]if (a && b) { a(); } else { b(); }", @"if (!a || !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And2() + { + await TestInsideMethodAsync( @"[||]if (!a && !b) { a(); } else { b(); }", @"if (a || b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And3() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And3() + { + await TestInsideMethodAsync( @"[||]if (!a && b) { a(); } else { b(); }", @"if (a || !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_And4() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_And4() + { + await TestInsideMethodAsync( @"[||]if (a & b) { a(); } else { b(); }", @"if (!a | !b) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_ParenthesizeAndForPrecedence() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_ParenthesizeAndForPrecedence() + { + await TestInsideMethodAsync( @"[||]if (a && b || c) { a(); } else { b(); }", @"if ((!a || !b) && !c) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Plus() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Plus() + { + await TestInsideMethodAsync( @"[||]if (a + b) { a(); } else { b(); }", @"if (!(a + b)) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_True() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_True() + { + await TestInsideMethodAsync( @"[||]if (true) { a(); } else { b(); }", @"if (false) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_TrueWithTrivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_TrueWithTrivia() + { + await TestInsideMethodAsync( @"[||]if (/*1*/true/*2*/) { a(); } else { b(); }", @"if (/*1*/false/*2*/) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_False() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_False() + { + await TestInsideMethodAsync( @"[||]if (false) { a(); } else { b(); }", @"if (true) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_OtherLiteralExpression() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_OtherLiteralExpression() + { + await TestInsideMethodAsync( @"[||]if (literalexpression) { a(); } else { b(); }", @"if (!literalexpression) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_TrueAndFalse() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_TrueAndFalse() + { + await TestInsideMethodAsync( @"[||]if (true && false) { a(); } else { b(); }", @"if (false || true) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_NoCurlyBraces() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_NoCurlyBraces() + { + await TestInsideMethodAsync( @"[||]if (a) a(); else b();", @"if (!a) b(); else a();"); - } + } - [Fact] - public async Task TestSingleLine_CurlyBracesOnIf() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_CurlyBracesOnIf() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else b();", @"if (!a) b(); else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_CurlyBracesOnElse() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_CurlyBracesOnElse() + { + await TestInsideMethodAsync( @"[||]if (a) a(); else { b(); }", @"if (!a) { b(); } else a();"); - } + } - [Fact] - public async Task TestSingleLine_IfElseIf() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_IfElseIf() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else if (b) { b(); }", @"if (!a) { if (b) { b(); } } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_IfElseIfElse() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_IfElseIfElse() + { + await TestInsideMethodAsync( @"[||]if (a) { a(); } else if (b) { b(); } else { c(); }", @"if (!a) { if (b) { b(); } else { c(); } } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_CompoundConditional() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_CompoundConditional() + { + await TestInsideMethodAsync( @"[||]if (((a == b) && (c != d)) || ((e < f) && (!g))) { a(); } else { b(); }", @"if ((a != b || c == d) && (e >= f || g)) { b(); } else { a(); }"); - } + } - [Fact] - public async Task TestSingleLine_Trivia() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_Trivia() + { + await TestInsideMethodAsync( @"[||]if /*1*/ (a) /*2*/ { /*3*/ a() /*4*/; /*5*/ } /*6*/ else if /*7*/ (b) /*8*/ { /*9*/ b(); /*10*/ } /*11*/ else /*12*/ { /*13*/ c(); /*14*/} /*15*/", @"if /*1*/ (!a) /*2*/ { if /*7*/ (b) /*8*/ { /*9*/ b(); /*10*/ } /*11*/ else /*12*/ { /*13*/ c(); /*14*/} /*6*/ } else { /*3*/ a() /*4*/; /*5*/ } /*15*/"); - } + } - [Fact] - public async Task TestKeepTriviaWithinExpression_BrokenCode() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact] + public async Task TestKeepTriviaWithinExpression_BrokenCode() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + [||]if (a || + b && + c < // comment + d) { - [||]if (a || - b && - c < // comment - d) - { - a(); - } - else - { - b(); - } + a(); + } + else + { + b(); } } - """, - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + if (!a && + (!b || + c >= // comment + d)) { - if (!a && - (!b || - c >= // comment - d)) - { - b(); - } - else - { - a(); - } + b(); + } + else + { + a(); } } - """); - } + } + """); + } - [Fact] - public async Task TestKeepTriviaWithinExpression() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact] + public async Task TestKeepTriviaWithinExpression() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + bool a = true; + bool b = true; + bool c = true; + bool d = true; + + [||]if (a || + b && + c < // comment + d) { - bool a = true; - bool b = true; - bool c = true; - bool d = true; - - [||]if (a || - b && - c < // comment - d) - { - a(); - } - else - { - b(); - } + a(); + } + else + { + b(); } } - """, - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + bool a = true; + bool b = true; + bool c = true; + bool d = true; + + if (!a && + (!b || + c >= // comment + d)) + { + b(); + } + else { - bool a = true; - bool b = true; - bool c = true; - bool d = true; + a(); + } + } + } + """); + } - if (!a && - (!b || - c >= // comment - d)) + [Fact] + public async Task TestMultiline_IfElseIfElse() + { + await TestAsync(""" + class A + { + void Goo() + { + [||]if (a) + { + a(); + } + else if (b) + { + b(); + } + else + { + c(); + } + } + } + """, """ + class A + { + void Goo() + { + if (!a) + { + if (b) { b(); } else { - a(); + c(); } } + else + { + a(); + } } - """); - } + } + """); + } - [Fact] - public async Task TestMultiline_IfElseIfElse() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] + public async Task TestMultiline_IfElseIfElseSelection1() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + [|if (a) { - [||]if (a) - { - a(); - } - else if (b) + a(); + } + else if (b) + { + b(); + } + else + { + c(); + }|] + } + } + """, """ + class A + { + void Goo() + { + if (!a) + { + if (b) { b(); } @@ -469,245 +535,195 @@ void Goo() c(); } } + else + { + a(); + } } - """, - """ - class A + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] + public async Task TestMultiline_IfElseIfElseSelection2() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() + [|if (a) + { + a(); + }|] + else if (b) { - if (!a) + b(); + } + else + { + c(); + } + } + } + """, """ + class A + { + void Goo() + { + if (!a) + { + if (b) { - if (b) - { - b(); - } - else - { - c(); - } + b(); } else { - a(); + c(); } } + else + { + a(); + } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] - public async Task TestMultiline_IfElseIfElseSelection1() - { - await TestInRegularAndScriptAsync( - """ - class A + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] + public async Task TestMultilineMissing_IfElseIfElseSubSelection() + { + var code = """ + class A + { + void Goo() { - void Goo() + if (a) { - [|if (a) - { - a(); - } - else if (b) - { - b(); - } - else - { - c(); - }|] + a(); } - } - """, - """ - class A - { - void Goo() + [|else if (b) { - if (!a) - { - if (b) - { - b(); - } - else - { - c(); - } - } - else - { - a(); - } + b(); } + else + { + c(); + }|] } - """); - } + } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] - public async Task TestMultiline_IfElseIfElseSelection2() - { - await TestInRegularAndScriptAsync( - """ - class A + await TestAsync(code, code); + } + + [Fact] + public async Task TestMultiline_IfElse() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() - { - [|if (a) - { - a(); - }|] - else if (b) - { - b(); - } - else - { - c(); - } - } + [||]if (foo) + bar(); + else + if (baz) + Quux(); } - """, - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + if (!foo) { - if (!a) - { - if (b) - { - b(); - } - else - { - c(); - } - } - else - { - a(); - } + if (baz) + Quux(); } + else + bar(); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/35525")] - public async Task TestMultilineMissing_IfElseIfElseSubSelection() - { - await TestMissingInRegularAndScriptAsync( - """ - class A + [Fact] + public async Task TestMultiline_OpenCloseBracesSameLine() + { + await TestAsync(""" + class A + { + void Goo() { - void Goo() - { - if (a) - { - a(); - } - [|else if (b) - { - b(); - } - else - { - c(); - }|] + [||]if (foo) { + x(); + x(); + } else { + y(); + y(); } } - """); - } - - [Fact] - public async Task TestMultiline_IfElse() - { - await TestInRegularAndScriptAsync( - """ - class A + } + """, """ + class A + { + void Goo() { - void Goo() + if (!foo) { - [||]if (foo) - bar(); - else - if (baz) - Quux(); + y(); + y(); } - } - """, - """ - class A - { - void Goo() + else { - if (!foo) - { - if (baz) - Quux(); - } - else - bar(); + x(); + x(); } } - """); - } + } + """); + } - [Fact] - public async Task TestMultiline_OpenCloseBracesSameLine() - { - await TestInRegularAndScriptAsync( - """ - class A - { - void Goo() - { - [||]if (foo) { - x(); - x(); - } else { - y(); - y(); - } - } + [Fact] + public async Task TestMultiline_Trivia() + { + await TestAsync(""" + class A + { + void Goo() + { /*1*/ + [||]if (a) /*2*/ + { /*3*/ + /*4*/ + goo(); /*5*/ + /*6*/ + } /*7*/ + else if (b) /*8*/ + { /*9*/ + /*10*/ + goo(); /*11*/ + /*12*/ + } /*13*/ + else /*14*/ + { /*15*/ + /*16*/ + goo(); /*17*/ + /*18*/ + } /*19*/ + /*20*/ } - """, - """ - class A - { - void Goo() + } + """, """ + class A + { + void Goo() + { /*1*/ + if (!a) /*2*/ { - if (!foo) - { - y(); - y(); - } - else - { - x(); - x(); - } - } - } - """); - } - [Fact] - public async Task TestMultiline_Trivia() - { - await TestInRegularAndScriptAsync( - """ - class A - { - void Goo() - { /*1*/ - [||]if (a) /*2*/ - { /*3*/ - /*4*/ - goo(); /*5*/ - /*6*/ - } /*7*/ - else if (b) /*8*/ + if (b) /*8*/ { /*9*/ /*10*/ goo(); /*11*/ @@ -719,963 +735,907 @@ void Goo() goo(); /*17*/ /*18*/ } /*19*/ - /*20*/ } + else + { /*3*/ + /*4*/ + goo(); /*5*/ + /*6*/ + } /*7*/ + /*20*/ } - """, - """ - class A - { - void Goo() - { /*1*/ - if (!a) /*2*/ - { - if (b) /*8*/ - { /*9*/ - /*10*/ - goo(); /*11*/ - /*12*/ - } /*13*/ - else /*14*/ - { /*15*/ - /*16*/ - goo(); /*17*/ - /*18*/ - } /*19*/ - } - else - { /*3*/ - /*4*/ - goo(); /*5*/ - /*6*/ - } /*7*/ - /*20*/ - } - } - """); - } + } + """); + } - [Fact] - public async Task TestOverlapsHiddenPosition1() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + [Fact] + public async Task TestOverlapsHiddenPosition1() + { + var code = """ + class C + { + void F() { - void F() + #line hidden + [||]if (a) { - #line hidden - [||]if (a) - { - a(); - } - else - { - b(); - } - #line default + a(); + } + else + { + b(); } + #line default } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition2() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition2() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - #line hidden - a(); - #line default - } - else - { - b(); - } + #line hidden + a(); + #line default + } + else + { + b(); } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition3() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition3() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - a(); - } - else - { - #line hidden - b(); - #line default - } + a(); + } + else + { + #line hidden + b(); + #line default } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition4() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition4() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - #line hidden - a(); - } - else - { - b(); - #line default - } + #line hidden + a(); + } + else + { + b(); + #line default } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition5() - { - await TestMissingInRegularAndScriptAsync( - """ - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition5() + { + var code = """ + class C + { + void F() { - void F() + [||]if (a) { - [||]if (a) - { - a(); - #line hidden - } - else - { - #line default - b(); - } + a(); + #line hidden + } + else + { + #line default + b(); } } - """); - } + } + """; - [Fact] - public async Task TestOverlapsHiddenPosition6() - { - await TestInRegularAndScriptAsync( - """ - #line hidden - class C + await TestAsync(code, code); + } + + [Fact] + public async Task TestOverlapsHiddenPosition6() + { + await TestAsync(""" + #line hidden + class C + { + void F() { - void F() + #line default + [||]if (a) { - #line default - [||]if (a) - { - a(); - } - else - { - b(); - } + a(); + } + else + { + b(); } } - """, - - """ - #line hidden - class C + } + """, """ + #line hidden + class C + { + void F() { - void F() + #line default + if (!a) { - #line default - if (!a) - { - b(); - } - else - { - a(); - } + b(); + } + else + { + a(); } } - """); - } + } + """); - [Fact] - public async Task TestOverlapsHiddenPosition7() - { - await TestInRegularAndScriptAsync( - """ - #line hidden - class C + } + + [Fact] + public async Task TestOverlapsHiddenPosition7() + { + await TestAsync(""" + #line hidden + class C + { + void F() { - void F() + #line default + [||]if (a) { - #line default - [||]if (a) - { - a(); - } - else - { - b(); - } - #line hidden + a(); + } + else + { + b(); } + #line hidden } - #line default - """, - - """ - #line hidden - class C + } + #line default + """, """ + #line hidden + class C + { + void F() { - void F() + #line default + if (!a) { - #line default - if (!a) - { - b(); - } - else - { - a(); - } - #line hidden + b(); + } + else + { + a(); } + #line hidden } - #line default - """); - } + } + #line default + """); + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string x; if (x.Length == 0) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero2() + { + await TestInsideMethodAsync( @"string[] x; [||]if (x.Length > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string[] x; if (x.Length == 0) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero3() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero3() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length > 0x0) { a(); } else { b(); } } } ", @"string x; if (x.Length == 0x0) { b(); } else { a(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_SimplifyToLengthEqualsZero4() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_SimplifyToLengthEqualsZero4() + { + await TestInsideMethodAsync( @"string x; [||]if (0 < x.Length) { a(); } else { b(); } } } ", @"string x; if (0 == x.Length) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero1() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero1() + { + await TestInsideMethodAsync( @"byte x = 1; [||]if (0 < x) { a(); } else { b(); } } } ", @"byte x = 1; if (0 == x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero2() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero2() + { + await TestInsideMethodAsync( @"ushort x = 1; [||]if (0 < x) { a(); } else { b(); } } } ", @"ushort x = 1; if (0 == x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero3() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero3() + { + await TestInsideMethodAsync( @"uint x = 1; [||]if (0 < x) { a(); } else { b(); } } } ", @"uint x = 1; if (0 == x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToEqualsZero4() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToEqualsZero4() + { + await TestInsideMethodAsync( @"ulong x = 1; [||]if (x > 0) { a(); } else { b(); } } } ", @"ulong x = 1; if (x == 0) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToNotEqualsZero1() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToNotEqualsZero1() + { + await TestInsideMethodAsync( @"ulong x = 1; [||]if (0 == x) { a(); } else { b(); } } } ", @"ulong x = 1; if (0 != x) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] - public async Task TestSingleLine_SimplifyToNotEqualsZero2() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545986")] + public async Task TestSingleLine_SimplifyToNotEqualsZero2() + { + await TestInsideMethodAsync( @"ulong x = 1; [||]if (x == 0) { a(); } else { b(); } } } ", @"ulong x = 1; if (x != 0) { b(); } else { a(); } } } "); - } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530505")] - public async Task TestSingleLine_SimplifyLongLengthEqualsZero() - { - await TestFixOneAsync( + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530505")] + public async Task TestSingleLine_SimplifyLongLengthEqualsZero() + { + await TestInsideMethodAsync( @"string[] x; [||]if (x.LongLength > 0) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string[] x; if (x.LongLength == 0) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length >= 0) { a(); } else { b(); } } } ", @"string x; if (x.Length < 0) { b(); } else { a(); } } } "); - } + } - [Fact] - public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero2() - { - await TestFixOneAsync( + [Fact] + public async Task TestSingleLine_DoesNotSimplifyToLengthEqualsZero2() + { + await TestInsideMethodAsync( @"string x; [||]if (x.Length > 0.0f) { GreaterThanZero(); } else { EqualsZero(); } } } ", @"string x; if (x.Length <= 0.0f) { EqualsZero(); } else { GreaterThanZero(); } } } "); - } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/29434")] - public async Task TestIsExpression() - { - await TestInRegularAndScriptAsync( + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/29434")] + public async Task TestIsExpression() + { + await TestAsync( @"class C { void M(object o) { [||]if (o is C) { a(); } else { } } }", @"class C { void M(object o) { if (o is not C) { } else { a(); } } }"); - } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] - public async Task TestEmptyIf() - { - await TestInRegularAndScriptAsync( - @"class C { void M(string s){ [||]if (s == ""a""){}else{ s = ""b""}}}", - @"class C { void M(string s){ if (s != ""a"") { s = ""b""}}}"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] + public async Task TestEmptyIf() + { + await TestAsync( + @"class C { void M(string s){ [||]if (s == ""a""){}else{ s = ""b""}}}", + @"class C { void M(string s){ if (s != ""a"") { s = ""b""}}}"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] - public async Task TestOnlySingleLineCommentIf() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] + public async Task TestOnlySingleLineCommentIf() + { + await TestAsync(""" + class C + { + void M(string s) { - void M(string s) + [||]if (s == "a") { - [||]if (s == "a") - { - // A single line comment - } - else - { - s = "b" - } + // A single line comment + } + else + { + s = "b" } } - """, - """ - class C + } + """, """ + class C + { + void M(string s) { - void M(string s) + if (s != "a") { - if (s != "a") - { - s = "b" - } - else - { - // A single line comment - } + s = "b" + } + else + { + // A single line comment } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] - public async Task TestOnlyMultilineLineCommentIf() - { - await TestInRegularAndScriptAsync( - """ - class C - { - void M(string s) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/43224")] + public async Task TestOnlyMultilineLineCommentIf() + { + await TestAsync(""" + class C + { + void M(string s) + { + [||]if (s == "a") { - [||]if (s == "a") - { - /* - * This is - * a multiline - * comment with - * two words - * per line. - */ - } - else - { - s = "b" - } + /* + * This is + * a multiline + * comment with + * two words + * per line. + */ + } + else + { + s = "b" } } - """, - """ - class C - { - void M(string s) + } + """, """ + class C + { + void M(string s) + { + if (s != "a") { - if (s != "a") - { - s = "b" - } - else - { - /* - * This is - * a multiline - * comment with - * two words - * per line. - */ - } + s = "b" + } + else + { + /* + * This is + * a multiline + * comment with + * two words + * per line. + */ } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsCheck_CSharp6() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsCheck_CSharp6() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is object) { - [||]if (c is object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (!(c is object)) { - if (!(c is object)) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6)); - } + } + """, LanguageVersion.CSharp6); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsCheck_CSharp8() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsCheck_CSharp8() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is object) { - [||]if (c is object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is null) { - if (c is null) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8)); - } + } + """, LanguageVersion.CSharp8); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsCheck_CSharp9() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsCheck_CSharp9() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is object) { - [||]if (c is object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is null) { - if (c is null) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9)); - } + } + """, LanguageVersion.CSharp9); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsNotObjectCheck_CSharp8() - { - // Not terrific. But the starting code is not legal C#8 either. In this case because we don't even support - // 'not' patterns wee dont' bother diving into the pattern to negate it, and we instead just negate the - // expression. - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsNotObjectCheck_CSharp8() + { + // Not terrific. But the starting code is not legal C#8 either. In this case because we don't even support + // 'not' patterns wee don't bother diving into the pattern to negate it, and we instead just negate the + // expression. + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is not object) { - [||]if (c is not object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is object) { - if (c is object) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8)); - } + } + """, LanguageVersion.CSharp8); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] - public async Task TestIsNotObjectCheck_CSharp9() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51359")] + public async Task TestIsNotObjectCheck_CSharp9() + { + await TestAsync(""" + class C + { + int M() { - int M() + [||]if (c is not object) { - [||]if (c is not object) - { - return 1; - } - else - { - return 2; - } + return 1; + } + else + { + return 2; } } - """, - """ - class C + } + """, """ + class C + { + int M() { - int M() + if (c is not null) { - if (c is not null) - { - return 2; - } - else - { - return 1; - } + return 2; + } + else + { + return 1; } } - """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9)); - } + } + """, LanguageVersion.CSharp9); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_GreaterThan() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_GreaterThan() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p > 10) { - [||]if (p > 10) - { - System.Console.WriteLine("p is not null and p.Value > 10"); - } + System.Console.WriteLine("p is not null and p.Value > 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p > 10)) { - if (!(p > 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value > 10"); + return; } + System.Console.WriteLine("p is not null and p.Value > 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_GreaterThanOrEqual() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_GreaterThanOrEqual() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p >= 10) { - [||]if (p >= 10) - { - System.Console.WriteLine("p is not null and p.Value >= 10"); - } + System.Console.WriteLine("p is not null and p.Value >= 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p >= 10)) { - if (!(p >= 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value >= 10"); + return; } + System.Console.WriteLine("p is not null and p.Value >= 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_LessThan() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_LessThan() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p < 10) { - [||]if (p < 10) - { - System.Console.WriteLine("p is not null and p.Value < 10"); - } + System.Console.WriteLine("p is not null and p.Value < 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p < 10)) { - if (!(p < 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value < 10"); + return; } + System.Console.WriteLine("p is not null and p.Value < 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestLiftedNullable_LessThanOrEqual() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestLiftedNullable_LessThanOrEqual() + { + await TestAsync(""" + class C + { + void M(int? p) { - void M(int? p) + [||]if (p <= 10) { - [||]if (p <= 10) - { - System.Console.WriteLine("p is not null and p.Value <= 10"); - } + System.Console.WriteLine("p is not null and p.Value <= 10"); } } - """, - """ - class C + } + """, """ + class C + { + void M(int? p) { - void M(int? p) + if (!(p <= 10)) { - if (!(p <= 10)) - { - return; - } - System.Console.WriteLine("p is not null and p.Value <= 10"); + return; } + System.Console.WriteLine("p is not null and p.Value <= 10"); } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] - public async Task TestNullableReference_GreaterThan() - { - await TestInRegularAndScriptAsync( - """ - #nullable enable - using System; - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/63311")] + public async Task TestNullableReference_GreaterThan() + { + await TestAsync(""" + #nullable enable + using System; + class C + { + void M(C? p) { - void M(C? p) + [||]if (p > new C()) { - [||]if (p > new C()) - { - Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); - } + Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); } - - public static bool operator <(C? left, C? right) => throw new NotImplementedException(); - public static bool operator >(C? left, C? right) => throw new NotImplementedException(); - public static bool operator <=(C? left, C? right) => throw new NotImplementedException(); - public static bool operator >=(C? left, C? right) => throw new NotImplementedException(); } - """, - """ - #nullable enable - using System; - class C + + public static bool operator <(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >(C? left, C? right) => throw new NotImplementedException(); + public static bool operator <=(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >=(C? left, C? right) => throw new NotImplementedException(); + } + """, """ + #nullable enable + using System; + class C + { + void M(C? p) { - void M(C? p) + if (p <= new C()) { - if (p <= new C()) - { - return; - } - Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); + return; } - - public static bool operator <(C? left, C? right) => throw new NotImplementedException(); - public static bool operator >(C? left, C? right) => throw new NotImplementedException(); - public static bool operator <=(C? left, C? right) => throw new NotImplementedException(); - public static bool operator >=(C? left, C? right) => throw new NotImplementedException(); + Console.WriteLine("Null-handling semantics may actually change depending on the operator implementation"); } - """); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40585")] - public async Task TestYieldBreak() - { - await TestInRegularAndScriptAsync( - """ - using System.Collections; + public static bool operator <(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >(C? left, C? right) => throw new NotImplementedException(); + public static bool operator <=(C? left, C? right) => throw new NotImplementedException(); + public static bool operator >=(C? left, C? right) => throw new NotImplementedException(); + } + """); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40585")] + public async Task TestYieldBreak() + { + await TestAsync(""" + using System.Collections; - class Program + class Program + { + public static IEnumerable Method(bool condition) { - public static IEnumerable Method(bool condition) + [||]if (condition) { - [||]if (condition) - { - yield return 1; - } + yield return 1; } } - """, - """ - using System.Collections; + } + """, """ + using System.Collections; - class Program + class Program + { + public static IEnumerable Method(bool condition) { - public static IEnumerable Method(bool condition) + if (!condition) { - if (!condition) - { - yield break; - } - yield return 1; + yield break; } + yield return 1; } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] - public async Task PreserveSpacing() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] + public async Task PreserveSpacing() + { + await TestAsync(""" + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - [||]if (l == "hello") - { - return null; - } + [||]if (l == "hello") + { + return null; + } - return l; + return l; - } } - """, - """ - class C + } + """, """ + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - if (l != "hello") - { - return l; - } + if (l != "hello") + { + return l; + } - return null; + return null; - } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] - public async Task PreserveSpacing_WithComments() - { - await TestInRegularAndScriptAsync( - """ - class C + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] + public async Task PreserveSpacing_WithComments() + { + await TestAsync(""" + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - [||]if (l == "hello") - { - // null 1 - return null; // null 2 - // null 3 - } + [||]if (l == "hello") + { + // null 1 + return null; // null 2 + // null 3 + } - // l 1 - return l; // l 2 - // l 3 + // l 1 + return l; // l 2 + // l 3 - } } - """, - """ - class C + } + """, """ + class C + { + string? M(string s) { - string? M(string s) - { - var l = s.ToLowerCase(); + var l = s.ToLowerCase(); - if (l != "hello") - { - // l 1 - return l; // l 2 - // null 3 - } + if (l != "hello") + { + // l 1 + return l; // l 2 + // null 3 + } - // null 1 - return null; // null 2 - // l 3 + // null 1 + return null; // null 2 + // l 3 - } } - """); - } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] - public async Task PreserveSpacing_NoTrivia() - { - await TestInRegularAndScriptAsync( - """ - class C - { - string? M(bool b) - {[||]if(b){return(true);}return(false);} - } - """, - """ - class C - { - string? M(bool b) - { if (!b) { return (false); } return (true); } - } - """); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/42715")] + public async Task PreserveSpacing_NoTrivia() + { + await TestAsync(""" + class C + { + string? M(bool b) + {[||]if(b){return(true);}return(false);} + } + """, """ + class C + { + string? M(bool b) + { if (!b) { return (false); } return (true); } + } + """); } } From 6449f1e1dc02f40fe2396d78b49f2b768b6a87c3 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sun, 12 May 2024 19:50:11 +0300 Subject: [PATCH 265/423] Port VB --- ...ertIfCodeRefactoringProvider.SingleLine.vb | 4 +- .../InvertIf/InvertSingleLineIfTests.vb | 169 +++++++++--------- 2 files changed, 83 insertions(+), 90 deletions(-) diff --git a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb index 2c2325b37ca3a..a83e71dd19c96 100644 --- a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb +++ b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.SingleLine.vb @@ -3,8 +3,8 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports System.Diagnostics.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InvertIf Inherits VisualBasicInvertIfCodeRefactoringProvider(Of SingleLineIfStatementSyntax) - + Public Sub New() End Sub diff --git a/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb b/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb index cb49815aa7f34..41ad87bbb660e 100644 --- a/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb +++ b/src/Features/VisualBasicTest/InvertIf/InvertSingleLineIfTests.vb @@ -2,22 +2,24 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports Microsoft.CodeAnalysis.CodeRefactorings -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings -Imports Microsoft.CodeAnalysis.VisualBasic.InvertIf +Imports Microsoft.CodeAnalysis.Testing +Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeRefactoringVerifier(Of + Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertSingleLineIfCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InvertIf - + Public Class InvertSingleLineIfTests - Inherits AbstractVisualBasicCodeActionTest_NoEditor - - Protected Overrides Function CreateCodeRefactoringProvider(workspace As TestWorkspace, parameters As TestParameters) As CodeRefactoringProvider - Return New VisualBasicInvertSingleLineIfCodeRefactoringProvider() + Private Shared Async Function TestInsideSubAsync(initial As String, expected As String) As Task + Await TestAsync(CreateTreeText(initial), CreateTreeText(expected)) End Function - Public Async Function TestFixOneAsync(initial As String, expected As String) As Task - Await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected)) + Private Shared Async Function TestAsync(initial As String, expected As String) As Task + Await New VerifyVB.Test With + { + .TestCode = initial, + .FixedCode = expected, + .CompilerDiagnostics = CompilerDiagnostics.None + }.RunAsync() End Function Public Shared Function CreateTreeText(initial As String) As String @@ -55,7 +57,7 @@ End Module Public Async Function TestAnd() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a And b Then aMethod() Else bMethod() ", @@ -67,7 +69,7 @@ End Module Public Async Function TestAddEmptyArgumentListIfNeeded() As Task Dim markup = - +" Module A Sub Main() [||]If True Then : Goo : Goo @@ -77,14 +79,14 @@ Module A Sub Goo() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestAndAlso() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a AndAlso b Then aMethod() Else bMethod() ", @@ -95,7 +97,7 @@ End Module Public Async Function TestCall() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a.Goo() Then aMethod() Else bMethod() ", @@ -106,7 +108,7 @@ End Module Public Async Function TestNotIdentifier() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If Not a Then aMethod() Else bMethod() ", @@ -117,7 +119,7 @@ End Module Public Async Function TestTrueLiteral() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If True Then aMethod() Else bMethod() ", @@ -128,7 +130,7 @@ End Module Public Async Function TestFalseLiteral() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If False Then aMethod() Else bMethod() ", @@ -139,7 +141,7 @@ End Module Public Async Function TestEquals() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a = b Then aMethod() Else bMethod() ", @@ -150,7 +152,7 @@ End Module Public Async Function TestNotEquals() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a <> b Then aMethod() Else bMethod() ", @@ -161,7 +163,7 @@ End Module Public Async Function TestLessThan() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a < b Then aMethod() Else bMethod() ", @@ -172,7 +174,7 @@ End Module Public Async Function TestLessThanOrEqual() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a <= b Then aMethod() Else bMethod() ", @@ -183,7 +185,7 @@ End Module Public Async Function TestGreaterThan() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a > b Then aMethod() Else bMethod() ", @@ -194,7 +196,7 @@ End Module Public Async Function TestGreaterThanOrEqual() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a >= b Then aMethod() Else bMethod() ", @@ -205,7 +207,7 @@ End Module Public Async Function TestIs() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " Dim myObject As New Object Dim thisObject = myObject @@ -222,7 +224,7 @@ End Module Public Async Function TestIsNot() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " Dim myObject As New Object Dim thisObject = myObject @@ -239,7 +241,7 @@ End Module Public Async Function TestOr() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Or b Then aMethod() Else bMethod() ", @@ -250,7 +252,7 @@ End Module Public Async Function TestOrElse() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a OrElse b Then aMethod() Else bMethod() ", @@ -261,7 +263,7 @@ End Module Public Async Function TestOr2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f Not a Or Not b Then aMethod() Else bMethod() ", @@ -272,7 +274,7 @@ End Module Public Async Function TestOrElse2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f Not a OrElse Not b Then aMethod() Else bMethod() ", @@ -283,7 +285,7 @@ End Module Public Async Function TestAnd2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If Not a And Not b Then aMethod() Else bMethod() ", @@ -294,7 +296,7 @@ End Module Public Async Function TestAndAlso2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If Not a AndAlso Not b Then aMethod() Else bMethod() ", @@ -305,7 +307,7 @@ End Module Public Async Function TestXor() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f a Xor b Then aMethod() Else bMethod() ", @@ -317,7 +319,7 @@ End Module Public Async Function TestXor2() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " I[||]f Not (a Xor b) Then aMethod() Else bMethod() ", @@ -328,7 +330,7 @@ End Module Public Async Function TestNested() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If (((a = b) AndAlso (c <> d)) OrElse ((e < f) AndAlso (Not g))) Then aMethod() Else bMethod() ", @@ -340,7 +342,7 @@ End Module Public Async Function TestEscapeKeywordsIfNeeded1() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -350,15 +352,15 @@ Module Program Sub Take() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded2() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -368,15 +370,15 @@ Module Program Sub Ascending() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded3() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -386,15 +388,15 @@ Module Program Sub Ascending() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded4() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -402,26 +404,15 @@ Module Program Take: Return End Sub End Module - - - Dim expected = - -Imports System.Linq -Module Program - Sub Main() - If False Then Console.WriteLine() Else Dim q = From x In "" -[Take]: Return - End Sub -End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestEscapeKeywordsIfNeeded5() As Task Dim markup = - +" Imports System.Linq Module Program Sub Main() @@ -431,14 +422,14 @@ Module Program Sub Take() End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestSelection() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [|If a And b Then aMethod() Else bMethod()|] ", @@ -449,7 +440,7 @@ End Module Public Async Function TestMultipleStatementsSingleLineIfStatement() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " If[||] a Then aMethod() : bMethod() Else cMethod() : d() ", @@ -460,7 +451,7 @@ End Module Public Async Function TestTriviaAfterSingleLineIfStatement() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Then aMethod() Else bMethod() ' I will stay put ", @@ -470,7 +461,7 @@ End Module End Function Public Async Function TestParenthesizeForLogicalExpressionPrecedence() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Sub Main() I[||]f a AndAlso b Or c Then aMethod() Else bMethod() End Sub @@ -483,7 +474,7 @@ End Module") Public Async Function TestParenthesizeComparisonOperands() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If 0 <= .GetHashCode Then aMethod() Else bMethod() ", @@ -496,7 +487,7 @@ End Module") Public Async Function TestNestedSingleLineIfs() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() ' Invert the 1st If @@ -513,18 +504,20 @@ End Module") Public Async Function TestTryToParenthesizeAwkwardSyntaxInsideSingleLineLambdaMethod() As Task - Await TestMissingAsync( + Dim markup = "Module Program Sub Main() ' Invert If Dim x = Sub() I[||]f True Then Dim y Else Console.WriteLine(), z = 1 End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestOnConditionOfSingleLineIf() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) If T[||]rue Then Return Else Console.WriteLine(""a"") @@ -541,42 +534,42 @@ End Module") Public Async Function TestImplicitLineContinuationBeforeClosingParenIsRemoved() As Task Dim markup = - +" [||]If (True OrElse True ) Then Else End If - +" Dim expected = - +" If False AndAlso False Then Else End If - +" - Await TestAsync(markup, expected) + Await TestInsideSubAsync(markup, expected) End Function Public Async Function TestParenthesizeToKeepParseTheSame1() As Task Dim markup = - +" Module Program Sub Main() - [||]If 0 >= <x/>.GetHashCode Then Console.WriteLine(1) Else Console.WriteLine(2) + [||]If 0 >= .GetHashCode Then Console.WriteLine(1) Else Console.WriteLine(2) End Sub End Module - +" Dim expected = - +" Module Program Sub Main() - If 0 < (<x/>.GetHashCode) Then Console.WriteLine(2) Else Console.WriteLine(1) + If 0 < (.GetHashCode) Then Console.WriteLine(2) Else Console.WriteLine(1) End Sub End Module - +" Await TestAsync(markup, expected) End Function @@ -584,7 +577,7 @@ End Module Public Async Function TestParenthesizeToKeepParseTheSame2() As Task Dim markup = - +" Module Program Sub Main() Select Nothing @@ -592,14 +585,14 @@ Module Program End Select End Sub End Module - +" - Await TestMissingAsync(markup) + Await TestAsync(markup, markup) End Function Public Async Function TestSingleLineIdentifier() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Then aMethod() Else bMethod() ", @@ -610,7 +603,7 @@ End Module Public Async Function TestWithMissingTrueStatementWithinUsing() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub M(Disposable As IDisposable) Dim x = True From 99f2c59b5b50387eb3c628cd772ad3c52a65ec50 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sun, 12 May 2024 20:20:07 +0300 Subject: [PATCH 266/423] Port VB --- ...vertIfCodeRefactoringProvider.MultiLine.vb | 4 +- .../InvertIf/InvertMultiLineIfTests.vb | 151 ++++++++++-------- 2 files changed, 89 insertions(+), 66 deletions(-) diff --git a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb index 2cadad82d13ef..b91b722459024 100644 --- a/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb +++ b/src/Features/VisualBasic/Portable/InvertIf/VisualBasicInvertIfCodeRefactoringProvider.MultiLine.vb @@ -3,8 +3,8 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition -Imports System.Diagnostics.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InvertIf Inherits VisualBasicInvertIfCodeRefactoringProvider(Of MultiLineIfBlockSyntax) - + Public Sub New() End Sub diff --git a/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb b/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb index 03cadc892d57f..de65c9f26ff25 100644 --- a/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb +++ b/src/Features/VisualBasicTest/InvertIf/InvertMultiLineIfTests.vb @@ -2,22 +2,25 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports Microsoft.CodeAnalysis.CodeRefactorings -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings -Imports Microsoft.CodeAnalysis.VisualBasic.InvertIf +Imports Microsoft.CodeAnalysis.Testing +Imports VerifyVB = Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions.VisualBasicCodeRefactoringVerifier(Of + Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertMultiLineIfCodeRefactoringProvider) Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.InvertIf - + Public Class InvertMultiLineIfTests - Inherits AbstractVisualBasicCodeActionTest_NoEditor - - Protected Overrides Function CreateCodeRefactoringProvider(workspace As TestWorkspace, parameters As TestParameters) As CodeRefactoringProvider - Return New VisualBasicInvertMultiLineIfCodeRefactoringProvider() + Private Shared Async Function TestInsideSubAsync(initial As String, expected As String, Optional languageVersion As LanguageVersion = LanguageVersion.Latest) As Task + Await TestAsync(CreateTreeText(initial), CreateTreeText(expected), languageVersion) End Function - Public Async Function TestFixOneAsync(initial As String, expected As String, Optional parseOptions As ParseOptions = Nothing) As Task - Await TestInRegularAndScriptAsync(CreateTreeText(initial), CreateTreeText(expected), parseOptions:=parseOptions) + Private Shared Async Function TestAsync(initial As String, expected As String, Optional languageVersion As LanguageVersion = LanguageVersion.Latest) As Task + Await New VerifyVB.Test With + { + .TestCode = initial, + .FixedCode = expected, + .LanguageVersion = languageVersion, + .CompilerDiagnostics = CompilerDiagnostics.None + }.RunAsync() End Function Public Shared Function CreateTreeText(initial As String) As String @@ -55,7 +58,7 @@ End Module Public Async Function TestMultiLineIdentifier() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If a Then aMethod() @@ -74,7 +77,7 @@ End Module Public Async Function TestElseIf() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = " Sub Main() If a Then @@ -85,12 +88,14 @@ Sub Main() cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestKeepElseIfKeyword() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() If a Then @@ -101,12 +106,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnIfElseIfElse() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() I[||]f a Then @@ -117,12 +124,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestSelection() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [|If a Then aMethod() @@ -141,7 +150,7 @@ End Module") Public Async Function TestDoesNotOverlapHiddenPosition1() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() #End ExternalSource @@ -170,7 +179,7 @@ End Module") Public Async Function TestDoesNotOverlapHiddenPosition2() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() #ExternalSource File.vb 1 @@ -197,7 +206,7 @@ End Module") Public Async Function TestMissingOnOverlapsHiddenPosition1() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -208,12 +217,14 @@ End Module") bMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition2() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() If a Then @@ -226,12 +237,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition3() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -244,12 +257,14 @@ End Module") cMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition4() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -260,12 +275,14 @@ End Module") #End ExternalSource End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition5() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -276,12 +293,14 @@ End Module") #End ExternalSource End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMissingOnOverlapsHiddenPosition6() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main() [||]If a Then @@ -292,12 +311,14 @@ End Module") bMethod() End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestMultipleStatementsMultiLineIfBlock() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() [||]If a Then @@ -324,7 +345,7 @@ End Module") Public Async Function TestTriviaAfterMultiLineIfBlock() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() [||]If a Then @@ -347,7 +368,7 @@ End Module") Public Async Function TestKeepExplicitLineContinuationTriviaMethod() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() I[||]f a And b _ @@ -372,7 +393,7 @@ End Module") Public Async Function TestKeepTriviaInStatementsInMultiLineIfBlock() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() [||]If a Then @@ -400,7 +421,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -425,7 +446,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero2() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x() As String @@ -450,7 +471,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero4() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x() As String @@ -475,7 +496,7 @@ End Module") Public Async Function TestSimplifyToLengthEqualsZero5() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -500,7 +521,7 @@ End Module") Public Async Function TestDoesNotSimplifyToLengthEqualsZero() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -525,7 +546,7 @@ End Module") Public Async Function TestDoesNotSimplifyToLengthEqualsZero2() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) Dim x As String @@ -552,7 +573,7 @@ End Module") Public Async Function TestColonAfterSingleLineIfWithEmptyElse() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main() ' Invert If @@ -570,7 +591,7 @@ End Module") Public Async Function TestOnlyOnElseIf() As Task - Await TestMissingInRegularAndScriptAsync( + Dim markup = "Module Program Sub Main(args As String()) If False Then @@ -581,12 +602,14 @@ End Module") Console.WriteLine(""a"") End If End Sub -End Module") +End Module" + + Await TestAsync(markup, markup) End Function Public Async Function TestOnConditionOfMultiLineIfStatement() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "Module Program Sub Main(args As String()) If [||]False Then @@ -611,12 +634,12 @@ End Module") Public Async Function TestDoNotRemoveTypeCharactersDuringComplexification() As Task Dim markup = - +" Imports System Module Program Sub Main() Goo(Function(take) - [||]If True Then Console.WriteLine("true") Else Console.WriteLine("false") + [||]If True Then Console.WriteLine(""true"") Else Console.WriteLine(""false"") take$.ToString() Return Function() 1 End Function) @@ -626,15 +649,15 @@ Imports System Sub Goo(Of T)(x As Func(Of Integer, T)) End Sub End Module - +" Dim expected = - +" Imports System Module Program Sub Main() Goo(Function(take) - If False Then Console.WriteLine("false") Else Console.WriteLine("true") + If False Then Console.WriteLine(""false"") Else Console.WriteLine(""true"") take$.ToString() Return Function() 1 End Function) @@ -644,14 +667,14 @@ Imports System Sub Goo(Of T)(x As Func(Of Integer, T)) End Sub End Module - +" Await TestAsync(markup, expected) End Function Public Async Function InvertIfWithoutStatements() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "class C sub M(x as String) [||]If x = ""a"" Then @@ -677,7 +700,7 @@ end class") Public Async Function InvertIfWithOnlyComment() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "class C sub M(x as String) [||]If x = ""a"" Then @@ -706,7 +729,7 @@ end class") Public Async Function InvertIfWithoutElse() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( "class C sub M(x as String) [||]If x = ""a"" Then @@ -730,7 +753,7 @@ end class") Public Async Function TestMultiLineTypeOfIs_VB12() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If TypeOf a Is String Then aMethod() @@ -744,12 +767,12 @@ end class") Else aMethod() End If -", VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.VisualBasic12)) +", LanguageVersion.VisualBasic12) End Function Public Async Function TestMultiLineTypeOfIs_VB14() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If TypeOf a Is String Then aMethod() @@ -763,12 +786,12 @@ end class") Else aMethod() End If -", VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.VisualBasic14)) +", LanguageVersion.VisualBasic14) End Function Public Async Function TestMultiLineTypeOfIsNot() As Task - Await TestFixOneAsync( + Await TestInsideSubAsync( " [||]If TypeOf a IsNot String Then aMethod() @@ -788,7 +811,7 @@ end class") Public Async Function PreserveSpace() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( " class C sub M(s as string) @@ -819,7 +842,7 @@ end class") Public Async Function PreserveSpace_WithComments() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( " class C sub M(s as string) @@ -858,7 +881,7 @@ end class") Public Async Function PreserveSpace_NoTrivia() As Task - Await TestInRegularAndScriptAsync( + Await TestAsync( " class C sub M(s as string) From e5f023caafbfeb624377dd0478569dff887be7b2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 12 May 2024 21:36:01 -0700 Subject: [PATCH 267/423] Small tweaks to speed up ctrl-click --- ...ableSymbolService.NavigableSymbolSource.cs | 33 ++++++++++++------- .../AbstractDefinitionLocationService.cs | 3 +- .../AbstractGoToDefinitionSymbolService.cs | 6 ++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs b/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs index 225a795bef732..6414859f4ed2a 100644 --- a/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs +++ b/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs @@ -41,19 +41,30 @@ public void Dispose() if (definitionLocationService == null) return null; - var definitionLocation = await definitionLocationService.GetDefinitionLocationAsync( - document, position, cancellationToken).ConfigureAwait(false); - if (definitionLocation == null) - return null; - var indicatorFactory = document.Project.Solution.Services.GetRequiredService(); - return new NavigableSymbol( - service, - textView, - definitionLocation.Location, - snapshot.GetSpan(definitionLocation.Span.SourceSpan.ToSpan()), - indicatorFactory); + // We want to compute this as quickly as possible so that the symbol be squiggled and navigated to. We + // don't want to wait on expensive operations like computing source-generators or skeletons if we can avoid + // it. So first try with a frozen document, then fallback to a normal document. This mirrors how go-to-def + // works as well. + return + await GetNavigableSymbolWorkerAsync(document.WithFrozenPartialSemantics(cancellationToken)).ConfigureAwait(false) ?? + await GetNavigableSymbolWorkerAsync(document).ConfigureAwait(false); + + async Task GetNavigableSymbolWorkerAsync(Document document) + { + var definitionLocation = await definitionLocationService.GetDefinitionLocationAsync( + document, position, cancellationToken).ConfigureAwait(false); + if (definitionLocation == null) + return null; + + return new NavigableSymbol( + service, + textView, + definitionLocation.Location, + snapshot.GetSpan(definitionLocation.Span.SourceSpan.ToSpan()), + indicatorFactory); + } } } } diff --git a/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs b/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs index 6f5eb9015334d..ed2ba45d411ad 100644 --- a/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs +++ b/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs @@ -140,8 +140,7 @@ private static async Task IsThirdPartyNavigationAllowedAsync( if (containingTypeDeclaration != null) { - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - Debug.Assert(semanticModel != null); + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); // Allow third parties to navigate to all symbols except types/constructors // if we are navigating from the corresponding type. diff --git a/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs b/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs index b8670f91f4d2a..94d6995fb5a54 100644 --- a/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs +++ b/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs @@ -25,7 +25,8 @@ internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSym var project = document.Project; var services = document.Project.Solution.Services; - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + // We don't need nullable information to compute the symbol. So avoid expensive work computing this. + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); var semanticInfo = await SymbolFinder.GetSemanticInfoAtPositionAsync(semanticModel, position, services, cancellationToken).ConfigureAwait(false); // Prefer references to declarations. It's more likely that the user is attempting to @@ -72,7 +73,8 @@ internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSym if (token == default) return default; - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + // We don't need nullable information to compute control flow targets. So avoid expensive work computing this. + var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); return (GetTargetPositionIfControlFlow(semanticModel, token), token.Span); } From 0dc5adeb0bceeadaaca0646dcaa2ae99b5afbdae Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 12 May 2024 21:43:14 -0700 Subject: [PATCH 268/423] move location --- .../AbstractDefinitionLocationService.cs | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs b/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs index ed2ba45d411ad..c2515dc973c1a 100644 --- a/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs +++ b/src/EditorFeatures/Core/Navigation/AbstractDefinitionLocationService.cs @@ -39,47 +39,56 @@ protected AbstractDefinitionLocationService( workspace, document.Id, position, virtualSpace: 0, cancellationToken); } - public async Task GetDefinitionLocationAsync( - Document document, int position, CancellationToken cancellationToken) + public async Task GetDefinitionLocationAsync(Document document, int position, CancellationToken cancellationToken) { - var symbolService = document.GetRequiredLanguageService(); - var (controlFlowTarget, controlFlowSpan) = await symbolService.GetTargetIfControlFlowAsync( - document, position, cancellationToken).ConfigureAwait(false); - if (controlFlowTarget != null) - { - var location = await GetNavigableLocationAsync( - document, controlFlowTarget.Value, cancellationToken).ConfigureAwait(false); - return location is null ? null : new DefinitionLocation(location, new DocumentSpan(document, controlFlowSpan)); - } - else + // We want to compute this as quickly as possible so that the symbol be squiggled and navigated to. We + // don't want to wait on expensive operations like computing source-generators or skeletons if we can avoid + // it. So first try with a frozen document, then fallback to a normal document. This mirrors how go-to-def + // works as well. + return await GetDefinitionLocationWorkerAsync(document.WithFrozenPartialSemantics(cancellationToken)).ConfigureAwait(false) ?? + await GetDefinitionLocationWorkerAsync(document).ConfigureAwait(false); + + async Task GetDefinitionLocationWorkerAsync(Document document) { - // Try to compute the referenced symbol and attempt to go to definition for the symbol. - var (symbol, project, span) = await symbolService.GetSymbolProjectAndBoundSpanAsync( + var symbolService = document.GetRequiredLanguageService(); + var (controlFlowTarget, controlFlowSpan) = await symbolService.GetTargetIfControlFlowAsync( document, position, cancellationToken).ConfigureAwait(false); - if (symbol is null) - return null; - - // if the symbol only has a single source location, and we're already on it, - // try to see if there's a better symbol we could navigate to. - var remappedLocation = await GetAlternativeLocationIfAlreadyOnDefinitionAsync( - project, position, symbol, originalDocument: document, cancellationToken).ConfigureAwait(false); - if (remappedLocation != null) - return new DefinitionLocation(remappedLocation, new DocumentSpan(document, span)); - - var isThirdPartyNavigationAllowed = await IsThirdPartyNavigationAllowedAsync( - symbol, position, document, cancellationToken).ConfigureAwait(false); - - var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( - symbol, - project.Solution, - _threadingContext, - _streamingPresenter, - thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed, - cancellationToken: cancellationToken).ConfigureAwait(false); - if (location is null) - return null; - - return new DefinitionLocation(location, new DocumentSpan(document, span)); + if (controlFlowTarget != null) + { + var location = await GetNavigableLocationAsync( + document, controlFlowTarget.Value, cancellationToken).ConfigureAwait(false); + return location is null ? null : new DefinitionLocation(location, new DocumentSpan(document, controlFlowSpan)); + } + else + { + // Try to compute the referenced symbol and attempt to go to definition for the symbol. + var (symbol, project, span) = await symbolService.GetSymbolProjectAndBoundSpanAsync( + document, position, cancellationToken).ConfigureAwait(false); + if (symbol is null) + return null; + + // if the symbol only has a single source location, and we're already on it, + // try to see if there's a better symbol we could navigate to. + var remappedLocation = await GetAlternativeLocationIfAlreadyOnDefinitionAsync( + project, position, symbol, originalDocument: document, cancellationToken).ConfigureAwait(false); + if (remappedLocation != null) + return new DefinitionLocation(remappedLocation, new DocumentSpan(document, span)); + + var isThirdPartyNavigationAllowed = await IsThirdPartyNavigationAllowedAsync( + symbol, position, document, cancellationToken).ConfigureAwait(false); + + var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( + symbol, + project.Solution, + _threadingContext, + _streamingPresenter, + thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed, + cancellationToken: cancellationToken).ConfigureAwait(false); + if (location is null) + return null; + + return new DefinitionLocation(location, new DocumentSpan(document, span)); + } } } From 9c06d9b4dc7dbf047acef8e58bf721e382a2bd1c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 12 May 2024 21:43:29 -0700 Subject: [PATCH 269/423] REvert --- ...ableSymbolService.NavigableSymbolSource.cs | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs b/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs index 6414859f4ed2a..225a795bef732 100644 --- a/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs +++ b/src/EditorFeatures/Core.Wpf/NavigableSymbols/NavigableSymbolService.NavigableSymbolSource.cs @@ -41,30 +41,19 @@ public void Dispose() if (definitionLocationService == null) return null; - var indicatorFactory = document.Project.Solution.Services.GetRequiredService(); - - // We want to compute this as quickly as possible so that the symbol be squiggled and navigated to. We - // don't want to wait on expensive operations like computing source-generators or skeletons if we can avoid - // it. So first try with a frozen document, then fallback to a normal document. This mirrors how go-to-def - // works as well. - return - await GetNavigableSymbolWorkerAsync(document.WithFrozenPartialSemantics(cancellationToken)).ConfigureAwait(false) ?? - await GetNavigableSymbolWorkerAsync(document).ConfigureAwait(false); + var definitionLocation = await definitionLocationService.GetDefinitionLocationAsync( + document, position, cancellationToken).ConfigureAwait(false); + if (definitionLocation == null) + return null; - async Task GetNavigableSymbolWorkerAsync(Document document) - { - var definitionLocation = await definitionLocationService.GetDefinitionLocationAsync( - document, position, cancellationToken).ConfigureAwait(false); - if (definitionLocation == null) - return null; + var indicatorFactory = document.Project.Solution.Services.GetRequiredService(); - return new NavigableSymbol( - service, - textView, - definitionLocation.Location, - snapshot.GetSpan(definitionLocation.Span.SourceSpan.ToSpan()), - indicatorFactory); - } + return new NavigableSymbol( + service, + textView, + definitionLocation.Location, + snapshot.GetSpan(definitionLocation.Span.SourceSpan.ToSpan()), + indicatorFactory); } } } From fac6cd6b06e38c15b62f90bb27d79c196ac04720 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 12 May 2024 21:54:04 -0700 Subject: [PATCH 270/423] Simplify --- .../CSharpGoToDefinitionSymbolService.cs | 16 +++++++--------- .../AbstractGoToDefinitionSymbolService.cs | 10 ++++------ .../IGoToDefinitionSymbolService.cs | 2 +- .../VisualBasicGoToDefinitionSymbolService.vb | 6 ++++-- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs b/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs index 15c8118bed05b..af747c6470b09 100644 --- a/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs +++ b/src/Features/CSharp/Portable/GoToDefinition/CSharpGoToDefinitionSymbolService.cs @@ -6,6 +6,8 @@ using System.Composition; using System.Diagnostics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GoToDefinition; @@ -16,16 +18,12 @@ namespace Microsoft.CodeAnalysis.CSharp.GoToDefinition; [ExportLanguageService(typeof(IGoToDefinitionSymbolService), LanguageNames.CSharp), Shared] -internal class CSharpGoToDefinitionSymbolService : AbstractGoToDefinitionSymbolService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpGoToDefinitionSymbolService() : AbstractGoToDefinitionSymbolService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpGoToDefinitionSymbolService() - { - } - - protected override ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation) - => symbol; + protected override Task FindRelatedExplicitlyDeclaredSymbolAsync(Project project, ISymbol symbol, CancellationToken cancellationToken) + => Task.FromResult(symbol); protected override int? GetTargetPositionIfControlFlow(SemanticModel semanticModel, SyntaxToken token) { diff --git a/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs b/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs index 94d6995fb5a54..5bb83805b1e99 100644 --- a/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs +++ b/src/Features/Core/Portable/GoToDefinition/AbstractGoToDefinitionSymbolService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -16,11 +15,11 @@ namespace Microsoft.CodeAnalysis.GoToDefinition; internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSymbolService { - protected abstract ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation); + protected abstract Task FindRelatedExplicitlyDeclaredSymbolAsync(Project project, ISymbol symbol, CancellationToken cancellationToken); protected abstract int? GetTargetPositionIfControlFlow(SemanticModel semanticModel, SyntaxToken token); - public async Task<(ISymbol?, Project, TextSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken) + public async Task<(ISymbol? symbol, Project project, TextSpan boundSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken) { var project = document.Project; var services = document.Project.Solution.Services; @@ -59,9 +58,8 @@ internal abstract class AbstractGoToDefinitionSymbolService : IGoToDefinitionSym project = mapping.Project; } - // The compilation will have already been realised, either by the semantic model or the symbol mapping - var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); - return (FindRelatedExplicitlyDeclaredSymbol(symbol, compilation), project, semanticInfo.Span); + var explicitlyDeclaredSymbol = await FindRelatedExplicitlyDeclaredSymbolAsync(project, symbol, cancellationToken).ConfigureAwait(false); + return (explicitlyDeclaredSymbol, project, semanticInfo.Span); } public async Task<(int? targetPosition, TextSpan tokenSpan)> GetTargetIfControlFlowAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs b/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs index c2353232129a8..5076dc0268dd7 100644 --- a/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs +++ b/src/Features/Core/Portable/GoToDefinition/IGoToDefinitionSymbolService.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.GoToDefinition; internal interface IGoToDefinitionSymbolService : ILanguageService { - Task<(ISymbol?, Project, TextSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken); + Task<(ISymbol? symbol, Project project, TextSpan boundSpan)> GetSymbolProjectAndBoundSpanAsync(Document document, int position, CancellationToken cancellationToken); /// /// If the position is on a control flow keyword (continue, break, yield, return , etc), returns the relevant position in the corresponding control flow statement. diff --git a/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb b/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb index 6af12e705418b..277e3494c07c8 100644 --- a/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb +++ b/src/Features/VisualBasic/Portable/GoToDefinition/VisualBasicGoToDefinitionSymbolService.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports System.Composition +Imports System.Threading Imports Microsoft.CodeAnalysis.GoToDefinition Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Operations @@ -12,7 +13,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.GoToDefinition - Friend Class VisualBasicGoToDefinitionSymbolService + Friend NotInheritable Class VisualBasicGoToDefinitionSymbolService Inherits AbstractGoToDefinitionSymbolService @@ -20,7 +21,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GoToDefinition Public Sub New() End Sub - Protected Overrides Function FindRelatedExplicitlyDeclaredSymbol(symbol As ISymbol, compilation As Compilation) As ISymbol + Protected Overrides Async Function FindRelatedExplicitlyDeclaredSymbolAsync(project As Project, symbol As ISymbol, cancellationToken As CancellationToken) As Task(Of ISymbol) + Dim compilation = Await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(False) Return symbol.FindRelatedExplicitlyDeclaredSymbol(compilation) End Function From d3ea419bcfddf3d57d68f31a6fd0a10468690ecf Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Mon, 13 May 2024 12:38:29 -0700 Subject: [PATCH 271/423] Add dynamic registration for razor and cshtml files (#73369) --- .../RazorDynamicDocumentSyncRegistration.cs | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs new file mode 100644 index 0000000000000..d255899e89c82 --- /dev/null +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/RazorDynamicDocumentSyncRegistration.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.Options; +using Roslyn.LanguageServer.Protocol; + +namespace Microsoft.CodeAnalysis.LanguageServer.LanguageServer; + +[ExportCSharpVisualBasicLspServiceFactory(typeof(OnInitialized)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class RazorDynamicDocumentSyncRegistration(IGlobalOptionService globalOptionService) : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + => new OnInitialized(globalOptionService); + + public class OnInitialized : IOnInitialized, ILspService + { + private readonly IGlobalOptionService _globalOptionService; + + public OnInitialized(IGlobalOptionService globalOptionService) + { + _globalOptionService = globalOptionService; + } + + public async Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) + { + // Hot reload only works in devkit scenarios. Without, there is no need to register for dynamic document sync. + if (!_globalOptionService.GetOption(LspOptionsStorage.LspUsingDevkitFeatures)) + { + return; + } + + // If dynamic registration for text document synchronization is supported, register for .razor and .cshtml files + // so that they are up to date for hot reload scenarios rather than depending on the file watchers to update + // the contents. + if (clientCapabilities.TextDocument?.Synchronization?.DynamicRegistration is true) + { + var languageServerManager = context.GetRequiredLspService(); + + var documentFilters = new[] { new DocumentFilter() { Pattern = "**/*.razor" }, new DocumentFilter() { Pattern = "**/*.cshtml" } }; + var registrationOptions = new TextDocumentRegistrationOptions() + { + DocumentSelector = documentFilters + }; + + await languageServerManager.SendRequestAsync(Methods.ClientRegisterCapabilityName, + new RegistrationParams() + { + Registrations = [ + new() + { + Id = Guid.NewGuid().ToString(), // No need to save this for unregistering + Method = Methods.TextDocumentDidOpenName, + RegisterOptions = registrationOptions + }, + new() + { + Id = Guid.NewGuid().ToString(), // No need to save this for unregistering + Method = Methods.TextDocumentDidChangeName, + RegisterOptions = registrationOptions + }, + new() + { + Id = Guid.NewGuid().ToString(), // No need to save this for unregistering + Method = Methods.TextDocumentDidCloseName, + RegisterOptions = registrationOptions + } + ] + }, + cancellationToken).ConfigureAwait(false); + } + } + } +} + From 57d67afebfc70925312772ee69c19fc0e51aeee4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:10:02 -0700 Subject: [PATCH 272/423] Improve memory allocs in interval trees --- .../Core/Collections/IntervalTree`1.cs | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 0e0c3b9ca927a..792e2abca76a7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -27,8 +27,11 @@ internal partial class IntervalTree : IEnumerable private delegate bool TestInterval(T value, int start, int length, in TIntrospector introspector) where TIntrospector : struct, IIntervalIntrospector; - private static readonly ObjectPool> s_stackPool - = SharedPools.Default>(); + private static readonly ObjectPool> s_stackPool + = SharedPools.Default>(); + + private static readonly ObjectPool> s_nodePool + = SharedPools.Default>(); public IntervalTree() { @@ -139,9 +142,41 @@ public bool HasIntervalThatContains(int start, int length, in TIn private bool Any(int start, int length, TestInterval testInterval, in TIntrospector introspector) where TIntrospector : struct, IIntervalIntrospector { - using var result = TemporaryArray.Empty; - var matches = FillWithIntervalsThatMatch(start, length, testInterval, ref result.AsRef(), in introspector, stopAfterFirst: true); - return matches > 0; + // Inlined version of FillWithIntervalsThatMatch, optimized to do less work and stop once it finds a match. + if (root is null) + return false; + + using var pooledObject = s_nodePool.GetPooledObject(); + var candidates = pooledObject.Object; + + var end = start + length; + + candidates.Push(root); + + while (candidates.TryPop(out var currentNode)) + { + // Check the nodes as we go down. That way we can stop immediately when we find something that matches, + // instead of having to do an entire in-order walk, which might end up hitting a lot of nodes we don't care + // about and placing a lot into the stack. + if (testInterval(currentNode.Value, start, length, in introspector)) + return true; + + // right children's starts will never be to the left of the parent's start so we should consider right + // subtree only if root's start overlaps with interval's End, + if (introspector.GetStart(currentNode.Value) <= end) + { + var right = currentNode.Right; + if (right != null && GetEnd(right.MaxEndNode.Value, in introspector) >= start) + candidates.Push(right); + } + + // only if left's maxVal overlaps with interval's start, we should consider left subtree + var left = currentNode.Left; + if (left != null && GetEnd(left.MaxEndNode.Value, in introspector) >= start) + candidates.Push(left); + } + + return false; } private ImmutableArray GetIntervalsThatMatch( @@ -161,28 +196,11 @@ private int FillWithIntervalsThatMatch( where TIntrospector : struct, IIntervalIntrospector { if (root == null) - { return 0; - } using var pooledObject = s_stackPool.GetPooledObject(); var candidates = pooledObject.Object; - var matches = FillWithIntervalsThatMatch( - start, length, testInterval, - ref builder, in introspector, - stopAfterFirst, candidates); - - return matches; - } - - /// The number of matching intervals found by the method. - private int FillWithIntervalsThatMatch( - int start, int length, TestInterval testInterval, - ref TemporaryArray builder, in TIntrospector introspector, - bool stopAfterFirst, Stack<(Node? node, bool firstTime)> candidates) - where TIntrospector : struct, IIntervalIntrospector - { var matches = 0; var end = start + length; From 0b537d57b735bd6d2f6eaecf330a8171d4389aa9 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:18:53 -0700 Subject: [PATCH 273/423] Share code --- .../Core/Collections/IntervalTree`1.cs | 96 ++++++++++--------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 792e2abca76a7..12828fac8ca55 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -22,20 +23,18 @@ internal partial class IntervalTree : IEnumerable { public static readonly IntervalTree Empty = new(); - protected Node? root; - - private delegate bool TestInterval(T value, int start, int length, in TIntrospector introspector) - where TIntrospector : struct, IIntervalIntrospector; - private static readonly ObjectPool> s_stackPool = SharedPools.Default>(); - private static readonly ObjectPool> s_nodePool - = SharedPools.Default>(); + /// + /// Keep around a fair number of these as we often use them in parallel algorithms. + /// + private static readonly ObjectPool> s_nodePool = new(() => new(), 1024); - public IntervalTree() - { - } + private delegate bool TestInterval(T value, int start, int length, in TIntrospector introspector) + where TIntrospector : struct, IIntervalIntrospector; + + protected Node? root; public static IntervalTree Create(in TIntrospector introspector, IEnumerable values) where TIntrospector : struct, IIntervalIntrospector @@ -161,18 +160,10 @@ private bool Any(int start, int length, TestInterval= start) - candidates.Push(right); - } + if (ShouldExamineRight(start, end, currentNode, in introspector, out var right)) + candidates.Push(right); - // only if left's maxVal overlaps with interval's start, we should consider left subtree - var left = currentNode.Left; - if (left != null && GetEnd(left.MaxEndNode.Value, in introspector) >= start) + if (ShouldExamineLeft(start, currentNode, in introspector, out var left)) candidates.Push(left); } @@ -223,45 +214,64 @@ private int FillWithIntervalsThatMatch( builder.Add(currentNode.Value); if (stopAfterFirst) - { return 1; - } } } else { - // First time we're seeing this node. In order to see the node 'in-order', - // we push the right side, then the node again, then the left side. This - // time we mark the current node with 'false' to indicate that it's the - // second time we're seeing it the next time it comes around. - - // right children's starts will never be to the left of the parent's start - // so we should consider right subtree only if root's start overlaps with - // interval's End, - if (introspector.GetStart(currentNode.Value) <= end) - { - var right = currentNode.Right; - if (right != null && GetEnd(right.MaxEndNode.Value, in introspector) >= start) - { - candidates.Push((right, firstTime: true)); - } - } + // First time we're seeing this node. In order to see the node 'in-order', we push the right side, then + // the node again, then the left side. This time we mark the current node with 'false' to indicate that + // it's the second time we're seeing it the next time it comes around. + + if (ShouldExamineRight(start, end, currentNode, in introspector, out var right)) + candidates.Push((right, firstTime: true)); candidates.Push((currentNode, firstTime: false)); // only if left's maxVal overlaps with interval's start, we should consider // left subtree - var left = currentNode.Left; - if (left != null && GetEnd(left.MaxEndNode.Value, in introspector) >= start) - { + if (ShouldExamineLeft(start, currentNode, in introspector, out var left)) candidates.Push((left, firstTime: true)); - } } } return matches; } + private static bool ShouldExamineRight( + int start, int end, + Node currentNode, + in TIntrospector introspector, + [NotNullWhen(true)] out Node? right) where TIntrospector : struct, IIntervalIntrospector + { + // right children's starts will never be to the left of the parent's start so we should consider right + // subtree only if root's start overlaps with interval's End, + if (introspector.GetStart(currentNode.Value) <= end) + { + right = currentNode.Right; + if (right != null && GetEnd(right.MaxEndNode.Value, in introspector) >= start) + return true; + } + + right = null; + return false; + } + + private static bool ShouldExamineLeft( + int start, + Node currentNode, + in TIntrospector introspector, + [NotNullWhen(true)] out Node? left) where TIntrospector : struct, IIntervalIntrospector + { + // only if left's maxVal overlaps with interval's start, we should consider + // left subtree + left = currentNode.Left; + if (left != null && GetEnd(left.MaxEndNode.Value, in introspector) >= start) + return true; + + return false; + } + public bool IsEmpty() => this.root == null; protected static Node Insert(Node? root, Node newNode, in TIntrospector introspector) From 1003a4e894abc870332e17d4d113e0221fec4964 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:19:12 -0700 Subject: [PATCH 274/423] Share code --- .../Compiler/Core/Collections/IntervalTree`1.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 12828fac8ca55..a4c60137227a1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -228,8 +228,6 @@ private int FillWithIntervalsThatMatch( candidates.Push((currentNode, firstTime: false)); - // only if left's maxVal overlaps with interval's start, we should consider - // left subtree if (ShouldExamineLeft(start, currentNode, in introspector, out var left)) candidates.Push((left, firstTime: true)); } From 8d0ca6c28ca43c6decb39d9220f7eb04997551e2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:19:33 -0700 Subject: [PATCH 275/423] Simplify --- .../Compiler/Core/Collections/IntervalTree`1.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index a4c60137227a1..2af6883220892 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -202,9 +202,7 @@ private int FillWithIntervalsThatMatch( var currentNode = currentTuple.node; RoslynDebug.Assert(currentNode != null); - var firstTime = currentTuple.firstTime; - - if (!firstTime) + if (!currentTuple.firstTime) { // We're seeing this node for the second time (as we walk back up the left // side of it). Now see if it matches our test, and if so return it out. From 43bf60d280cca7ffc893b19737d5159c8aadc93a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:19:51 -0700 Subject: [PATCH 276/423] Simplify --- .../Compiler/Core/Collections/IntervalTree`1.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 2af6883220892..377643d7130ba 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -200,7 +200,6 @@ private int FillWithIntervalsThatMatch( while (candidates.TryPop(out var currentTuple)) { var currentNode = currentTuple.node; - RoslynDebug.Assert(currentNode != null); if (!currentTuple.firstTime) { From 54f589b2a218940a83d8027ced5c1b68c0940c4b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:39:40 -0700 Subject: [PATCH 277/423] PRoduce less string allocs while formatting documents --- .../Formatting/BottomUpBaseIndentationFinder.cs | 4 ++-- .../Core/Formatting/FormattingExtensions.cs | 6 +++--- .../Core/Utilities/CommonFormattingHelpers.cs | 14 ++++---------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs index d47e5050dadfa..d0a46391100f9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/BottomUpBaseIndentationFinder.cs @@ -219,7 +219,7 @@ private List GetParentIndentBlockOperations(SyntaxToken to allNodes.Do(n => _formattingRules.AddIndentBlockOperations(list, n)); // sort them in right order - list.RemoveAll(CommonFormattingHelpers.IsNull); + list.RemoveAll(static o => o is null); list.Sort(CommonFormattingHelpers.IndentBlockOperationComparer); return list; @@ -296,7 +296,7 @@ private SyntaxToken GetAlignmentBaseTokenFor(SyntaxToken token) } // well, found no appropriate one - list.RemoveAll(CommonFormattingHelpers.IsNull); + list.RemoveAll(static o => o is null); if (list.Count == 0) { return null; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs index 8073cdc2c43eb..0ab7048ad4140 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -53,7 +54,8 @@ public static bool ContainsElasticTrivia(this SuppressOperation operation, Token var endToken = tokenStream.GetTokenData(operation.EndToken); var previousToken = endToken.GetPreviousTokenData(); - return tokenStream.GetTriviaData(startToken, nextToken).TreatAsElastic || tokenStream.GetTriviaData(previousToken, endToken).TreatAsElastic; + return CommonFormattingHelpers.HasAnyWhitespaceElasticTrivia(startToken.Token, nextToken.Token) || + CommonFormattingHelpers.HasAnyWhitespaceElasticTrivia(previousToken.Token, endToken.Token); } public static bool HasAnyWhitespaceElasticTrivia(this SyntaxTriviaList list) @@ -62,9 +64,7 @@ public static bool HasAnyWhitespaceElasticTrivia(this SyntaxTriviaList list) foreach (var trivia in list) { if (trivia.IsElastic()) - { return true; - } } return false; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs index 62dcc67daf5cc..aa20832c3047f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs @@ -346,21 +346,15 @@ public static int GetStartPositionOfSpan(SyntaxToken token) public static bool HasAnyWhitespaceElasticTrivia(SyntaxToken previousToken, SyntaxToken currentToken) { - if ((!previousToken.ContainsAnnotations && !currentToken.ContainsAnnotations) || - (!previousToken.HasTrailingTrivia && !currentToken.HasLeadingTrivia)) - { + if (!previousToken.ContainsAnnotations && !currentToken.ContainsAnnotations) + return false; + + if (!previousToken.HasTrailingTrivia && !currentToken.HasLeadingTrivia) return false; - } return previousToken.TrailingTrivia.HasAnyWhitespaceElasticTrivia() || currentToken.LeadingTrivia.HasAnyWhitespaceElasticTrivia(); } - public static bool IsNull(T t) where T : class - => t == null; - - public static bool IsNotNull(T t) where T : class - => !IsNull(t); - public static TextSpan GetFormattingSpan(SyntaxNode root, TextSpan span) { Contract.ThrowIfNull(root); From a7e17c197c28a77acde6be5f34febf9f05562d43 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 13:45:07 -0700 Subject: [PATCH 278/423] Update src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs --- .../Compiler/Core/Collections/IntervalTree`1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs index 377643d7130ba..f90939d8824f0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTree`1.cs @@ -29,7 +29,7 @@ internal partial class IntervalTree : IEnumerable /// /// Keep around a fair number of these as we often use them in parallel algorithms. /// - private static readonly ObjectPool> s_nodePool = new(() => new(), 1024); + private static readonly ObjectPool> s_nodePool = new(() => new(), 128); private delegate bool TestInterval(T value, int start, int length, in TIntrospector introspector) where TIntrospector : struct, IIntervalIntrospector; From f906b4e365e027f0b91a71b453c8fedf98d25645 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:23:55 -0700 Subject: [PATCH 279/423] Optimize suppress operations --- .../PooledObjects/ArrayBuilder.cs | 7 ++- .../Formatting/TypingFormattingRule.cs | 5 +- .../Formatting/DefaultOperationProvider.cs | 3 +- .../Formatting/Rules/BaseFormattingRule.cs | 9 +-- .../Rules/ElasticTriviaFormattingRule.cs | 9 +-- .../Rules/QueryExpressionFormattingRule.cs | 3 +- .../Formatting/Rules/SpacingFormattingRule.cs | 5 +- .../Rules/SuppressFormattingRule.cs | 19 +++--- .../Rules/WrappingFormattingRule.cs | 13 ++-- .../Indentation/CSharpSmartTokenFormatter.cs | 3 +- .../Core/Extensions/ListExtensions.cs | 20 +++++++ .../FormattingContext.InitialContextFinder.cs | 60 ++++++++----------- .../Formatting/Context/FormattingContext.cs | 3 +- .../Formatting/Engine/AbstractFormatEngine.cs | 32 +++++++--- .../Engine/ChainedFormattingRules.cs | 3 +- .../Rules/AbstractFormattingRule.cs | 4 +- .../Rules/CompatAbstractFormattingRule.cs | 5 +- .../Rules/NextSuppressOperationAction.cs | 3 +- .../Rules/Operations/FormattingOperations.cs | 12 ---- 19 files changed, 124 insertions(+), 94 deletions(-) diff --git a/src/Dependencies/PooledObjects/ArrayBuilder.cs b/src/Dependencies/PooledObjects/ArrayBuilder.cs index b0246977a88b4..62c42b66d1412 100644 --- a/src/Dependencies/PooledObjects/ArrayBuilder.cs +++ b/src/Dependencies/PooledObjects/ArrayBuilder.cs @@ -303,7 +303,12 @@ public void Sort(IComparer comparer) } public void Sort(Comparison compare) - => Sort(Comparer.Create(compare)); + { + if (this.Count <= 1) + return; + + Sort(Comparer.Create(compare)); + } public void Sort(int startIndex, IComparer comparer) { diff --git a/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs b/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs index 96365c7cbe9e6..5cca530503ddf 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/TypingFormattingRule.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -13,7 +14,7 @@ internal class TypingFormattingRule : BaseFormattingRule { public static readonly TypingFormattingRule Instance = new(); - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { if (TryAddSuppressionOnMissingCloseBraceCase(list, node)) { @@ -23,7 +24,7 @@ public override void AddSuppressOperations(List list, SyntaxN base.AddSuppressOperations(list, node, in nextOperation); } - private static bool TryAddSuppressionOnMissingCloseBraceCase(List list, SyntaxNode node) + private static bool TryAddSuppressionOnMissingCloseBraceCase(ArrayBuilder list, SyntaxNode node) { var bracePair = node.GetBracePair(); if (!bracePair.IsValidBracketOrBracePair()) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs index 8b392f7679a57..67fa60bca9850 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/DefaultOperationProvider.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -19,7 +20,7 @@ private DefaultOperationProvider() { } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs index d2c5f95c7cc6b..d8259513c82c9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -116,13 +117,13 @@ protected static void SetAlignmentBlockOperation( list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(baseToken, startToken, endToken, indentationDelta: 0, option: option)); } - protected static void AddSuppressWrappingIfOnSingleLineOperation(List list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) + protected static void AddSuppressWrappingIfOnSingleLineOperation(ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) => AddSuppressOperation(list, startToken, endToken, SuppressOption.NoWrappingIfOnSingleLine | extraOption); - protected static void AddSuppressAllOperationIfOnMultipleLine(List list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) + protected static void AddSuppressAllOperationIfOnMultipleLine(ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption extraOption = SuppressOption.None) => AddSuppressOperation(list, startToken, endToken, SuppressOption.NoSpacingIfOnMultipleLine | SuppressOption.NoWrapping | extraOption); - protected static void AddSuppressOperation(List list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption option) + protected static void AddSuppressOperation(ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken, SuppressOption option) { if (startToken.Kind() == SyntaxKind.None || endToken.Kind() == SyntaxKind.None) { @@ -158,7 +159,7 @@ protected static AdjustNewLinesOperation CreateAdjustNewLinesOperation(int line, protected static AdjustSpacesOperation CreateAdjustSpacesOperation(int space, AdjustSpacesOption option) => FormattingOperations.CreateAdjustSpacesOperation(space, option); - protected static void AddBraceSuppressOperations(List list, SyntaxNode node) + protected static void AddBraceSuppressOperations(ArrayBuilder list, SyntaxNode node) { var bracePair = node.GetBracePair(); if (!bracePair.IsValidBracketOrBracePair()) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs index f3b79019b4238..977c5ebd4978a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/ElasticTriviaFormattingRule.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -21,7 +22,7 @@ internal class ElasticTriviaFormattingRule : BaseFormattingRule { internal const string Name = "CSharp Elastic trivia Formatting Rule"; - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -37,7 +38,7 @@ public override void AddSuppressOperations(List list, SyntaxN AddCollectionExpressionSuppressOperations(list, node); } - private static void AddPropertyDeclarationSuppressOperations(List list, SyntaxNode node) + private static void AddPropertyDeclarationSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is BasePropertyDeclarationSyntax basePropertyDeclaration && basePropertyDeclaration.AccessorList != null && basePropertyDeclaration.AccessorList.Accessors.All(a => a.Body == null) && @@ -49,7 +50,7 @@ private static void AddPropertyDeclarationSuppressOperations(List list, SyntaxNode node) + private static void AddInitializerSuppressOperations(ArrayBuilder list, SyntaxNode node) { var initializer = GetInitializerNode(node); var lastTokenOfType = GetLastTokenOfType(node); @@ -66,7 +67,7 @@ private static void AddInitializerSuppressOperations(List lis } } - private static void AddCollectionExpressionSuppressOperations(List list, SyntaxNode node) + private static void AddCollectionExpressionSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is CollectionExpressionSyntax { OpenBracketToken.IsMissing: false, CloseBracketToken.IsMissing: false } collectionExpression) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs index 32bdf04b114db..e9628ccd59744 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/QueryExpressionFormattingRule.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -38,7 +39,7 @@ public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions optio return new QueryExpressionFormattingRule(newOptions); } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs index 68df98b39dfce..ccf10986adf30 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SpacingFormattingRule.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -557,7 +558,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return nextOperation.Invoke(in previousToken, in currentToken); } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -570,7 +571,7 @@ private static bool IsEmptyForStatement(ForStatementSyntax forStatement) && forStatement.Condition == null && forStatement.Incrementors.Count == 0; - private void SuppressVariableDeclaration(List list, SyntaxNode node) + private void SuppressVariableDeclaration(ArrayBuilder list, SyntaxNode node) { if (node.Kind() is SyntaxKind.FieldDeclaration diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs index d649ba761a44c..dd4aecbdbe93c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -17,7 +18,7 @@ internal class SuppressFormattingRule : BaseFormattingRule { internal const string Name = "CSharp Suppress Formatting Rule"; - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -32,7 +33,7 @@ public override void AddSuppressOperations(List list, SyntaxN AddSpecificNodesSuppressOperations(list, node); } - private static void AddSpecificNodesSuppressOperations(List list, SyntaxNode node) + private static void AddSpecificNodesSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is IfStatementSyntax ifStatementNode) { @@ -259,7 +260,7 @@ private static void AddSpecificNodesSuppressOperations(List l } } - private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void AddStatementExceptBlockSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is not StatementSyntax statementNode || statementNode.Kind() == SyntaxKind.Block) { @@ -272,7 +273,7 @@ private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void AddFormatSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (!node.ContainsDirectives) { @@ -293,7 +294,7 @@ private static void AddFormatSuppressOperations(List list, Sy return; // Local functions - static void ProcessTriviaList(List list, SyntaxTriviaList triviaList) + static void ProcessTriviaList(ArrayBuilder list, SyntaxTriviaList triviaList) { foreach (var trivia in triviaList) { @@ -301,7 +302,7 @@ static void ProcessTriviaList(List list, SyntaxTriviaList tri } } - static void ProcessTrivia(List list, SyntaxTrivia trivia) + static void ProcessTrivia(ArrayBuilder list, SyntaxTrivia trivia) { if (!(trivia.HasStructure)) { @@ -311,7 +312,7 @@ static void ProcessTrivia(List list, SyntaxTrivia trivia) ProcessStructuredTrivia(list, trivia.GetStructure()!); } - static void ProcessStructuredTrivia(List list, SyntaxNode structure) + static void ProcessStructuredTrivia(ArrayBuilder list, SyntaxNode structure) { if (structure is not PragmaWarningDirectiveTriviaSyntax pragmaWarningDirectiveTrivia) { @@ -365,7 +366,7 @@ private static bool IsFormatDirective(DirectiveTriviaSyntax? trivia, SyntaxKind return false; } - private static void AddInitializerSuppressOperations(List list, SyntaxNode node) + private static void AddInitializerSuppressOperations(ArrayBuilder list, SyntaxNode node) { // array or collection initializer case if (node.IsInitializerForArrayOrCollectionCreationExpression()) @@ -395,7 +396,7 @@ private static void AddInitializerSuppressOperations(List lis } } - private static void AddInitializerSuppressOperations(List list, SyntaxNode parent, IEnumerable items) + private static void AddInitializerSuppressOperations(ArrayBuilder list, SyntaxNode parent, IEnumerable items) { // make creation node itself to not break into multiple line, if it is on same line AddSuppressWrappingIfOnSingleLineOperation(list, parent.GetFirstToken(includeZeroWidth: true), parent.GetLastToken(includeZeroWidth: true)); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs index 7332ca7cbdf80..1e41f578424e3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -42,7 +43,7 @@ public override AbstractFormattingRule WithOptions(SyntaxFormattingOptions optio return new WrappingFormattingRule(newOptions); } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { nextOperation.Invoke(); @@ -88,7 +89,7 @@ private static (SyntaxToken firstToken, SyntaxToken lastToken) GetSpecificNodeSu }; } - private static void AddSpecificNodesSuppressOperations(List list, SyntaxNode node) + private static void AddSpecificNodesSuppressOperations(ArrayBuilder list, SyntaxNode node) { var (firstToken, lastToken) = GetSpecificNodeSuppressionTokenRange(node); if (!firstToken.IsKind(SyntaxKind.None) || !lastToken.IsKind(SyntaxKind.None)) @@ -97,7 +98,7 @@ private static void AddSpecificNodesSuppressOperations(List l } } - private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void AddStatementExceptBlockSuppressOperations(ArrayBuilder list, SyntaxNode node) { if (node is not StatementSyntax statementNode || statementNode.Kind() == SyntaxKind.Block) { @@ -110,7 +111,7 @@ private static void AddStatementExceptBlockSuppressOperations(List list, SyntaxNode node) + private static void RemoveSuppressOperationForStatementMethodDeclaration(ArrayBuilder list, SyntaxNode node) { if (!(node is not StatementSyntax statementNode || statementNode.Kind() == SyntaxKind.Block)) { @@ -133,7 +134,7 @@ private static void RemoveSuppressOperationForStatementMethodDeclaration(List list, SyntaxNode node) + private static void RemoveSuppressOperationForBlock(ArrayBuilder list, SyntaxNode node) { var bracePair = GetBracePair(node); if (!bracePair.IsValidBracketOrBracePair()) @@ -175,7 +176,7 @@ private static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBracePair(Synt } private static void RemoveSuppressOperation( - List list, + ArrayBuilder list, SyntaxToken startToken, SyntaxToken endToken) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs index 2e538e86dd901..a432b7527a472 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -146,7 +147,7 @@ private class NoLineChangeFormattingRule : AbstractFormattingRule private class SmartTokenFormattingRule : NoLineChangeFormattingRule { - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { // don't suppress anything } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index 535a7e3fdb495..4a05b3ffbfe13 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; @@ -58,6 +59,25 @@ public static void RemoveOrTransformAll(this List list, Func(this ArrayBuilder list, Func transform, TArg arg) + where T : struct + { + RoslynDebug.AssertNotNull(list); + RoslynDebug.AssertNotNull(transform); + + var targetIndex = 0; + for (var sourceIndex = 0; sourceIndex < list.Count; sourceIndex++) + { + var newValue = transform(list[sourceIndex], arg); + if (newValue is null) + continue; + + list[targetIndex++] = newValue.Value; + } + + list.RemoveRange(targetIndex, list.Count - targetIndex); + } + /// /// Attempts to remove the first item selected by . /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs index b49d180422640..641931ca4e693 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -39,7 +41,7 @@ public InitialContextFinder( _rootNode = rootNode; } - public (List indentOperations, List? suppressOperations) Do(SyntaxToken startToken, SyntaxToken endToken) + public (List indentOperations, ImmutableArray suppressOperations) Do(SyntaxToken startToken, SyntaxToken endToken) { // we are formatting part of document, try to find initial context that formatting will be based on such as // initial indentation and etc. @@ -53,7 +55,6 @@ public InitialContextFinder( if (initialSuppressOperations != null) { Debug.Assert( - initialSuppressOperations.IsEmpty() || initialSuppressOperations.All( o => o.TextSpan.Contains(startToken.SpanStart) || o.TextSpan.Contains(endToken.SpanStart))); @@ -121,71 +122,60 @@ private List GetInitialIndentBlockOperations(SyntaxToken s return operations; } - private List? GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken) + private ImmutableArray GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken) { - var noWrapList = this.GetInitialSuppressOperations(startToken, endToken, SuppressOption.NoWrapping); - var noSpaceList = this.GetInitialSuppressOperations(startToken, endToken, SuppressOption.NoSpacing); + using var _ = ArrayBuilder.GetInstance(out var result); - var list = noWrapList.Combine(noSpaceList); - if (list == null) - { - return null; - } + this.AddInitialSuppressOperations(startToken, endToken, SuppressOption.NoWrapping, result); + this.AddInitialSuppressOperations(startToken, endToken, SuppressOption.NoSpacing, result); - list.Sort(CommonFormattingHelpers.SuppressOperationComparer); - return list; + result.Sort(CommonFormattingHelpers.SuppressOperationComparer); + return result.ToImmutable(); } - private List? GetInitialSuppressOperations(SyntaxToken startToken, SyntaxToken endToken, SuppressOption mask) + private void AddInitialSuppressOperations( + SyntaxToken startToken, SyntaxToken endToken, SuppressOption mask, ArrayBuilder result) { - var startList = this.GetInitialSuppressOperations(startToken, mask); - var endList = this.GetInitialSuppressOperations(endToken, mask); - - return startList.Combine(endList); + this.AddInitialSuppressOperations(startToken, mask, result); + this.AddInitialSuppressOperations(endToken, mask, result); } - private List? GetInitialSuppressOperations(SyntaxToken token, SuppressOption mask) + private void AddInitialSuppressOperations(SyntaxToken token, SuppressOption mask, ArrayBuilder result) { var startNode = token.Parent; var startPosition = token.SpanStart; // starting from given token, move up to root until the first meaningful // operation has found - var list = new List(); + using var _ = ArrayBuilder.GetInstance(out var buffer); var currentIndentationNode = startNode; - Predicate predicate = Predicate; while (currentIndentationNode != null) { - _formattingRules.AddSuppressOperations(list, currentIndentationNode); + _formattingRules.AddSuppressOperations(buffer, currentIndentationNode); - list.RemoveAll(predicate); - if (list.Count > 0) + buffer.RemoveAll(Predicate, (startPosition, _tokenStream, mask)); + if (buffer.Count > 0) { - return list; + result.AddRange(buffer); + return; } currentIndentationNode = currentIndentationNode.Parent; } - return null; + return; - bool Predicate(SuppressOperation operation) + static bool Predicate(SuppressOperation operation, (int startPosition, TokenStream tokenStream, SuppressOption mask) tuple) { - if (!operation.TextSpan.Contains(startPosition)) - { + if (!operation.TextSpan.Contains(tuple.startPosition)) return true; - } - if (operation.ContainsElasticTrivia(_tokenStream) && !operation.Option.IsOn(SuppressOption.IgnoreElasticWrapping)) - { + if (operation.ContainsElasticTrivia(tuple.tokenStream) && !operation.Option.IsOn(SuppressOption.IgnoreElasticWrapping)) return true; - } - if (!operation.Option.IsMaskOn(mask)) - { + if (!operation.Option.IsMaskOn(tuple.mask)) return true; - } return false; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs index b408318b2916e..965a3a2b38a6f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs @@ -115,7 +115,8 @@ public void Initialize( _initialIndentBlockOperations = indentationOperations; } - suppressOperations?.Do(this.AddInitialSuppressOperation); + foreach (var suppressOperation in suppressOperations) + this.AddInitialSuppressOperation(suppressOperation); } public void AddIndentBlockOperations( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 60cd1f2e7f4fa..25a4edbf60315 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -133,10 +133,10 @@ protected virtual NodeOperations CreateNodeOperations(CancellationToken cancella var nodeOperations = new NodeOperations(); - var indentBlockOperation = new List(); - var suppressOperation = new List(); - var alignmentOperation = new List(); - var anchorIndentationOperations = new List(); + var indentBlockOperationScratch = new List(); + var alignmentOperationScratch = new List(); + var anchorIndentationOperationsScratch = new List(); + using var _ = ArrayBuilder.GetInstance(out var suppressOperationScratch); // Cache delegates out here to avoid allocation overhead. @@ -150,14 +150,14 @@ protected virtual NodeOperations CreateNodeOperations(CancellationToken cancella { cancellationToken.ThrowIfCancellationRequested(); - AddOperations(nodeOperations.IndentBlockOperation, indentBlockOperation, node, addIndentBlockOperations); - AddOperations(nodeOperations.SuppressOperation, suppressOperation, node, addSuppressOperation); - AddOperations(nodeOperations.AlignmentOperation, alignmentOperation, node, addAlignTokensOperations); - AddOperations(nodeOperations.AnchorIndentationOperations, anchorIndentationOperations, node, addAnchorIndentationOperations); + AddOperations(nodeOperations.IndentBlockOperation, indentBlockOperationScratch, node, addIndentBlockOperations); + AddOperations(nodeOperations.SuppressOperation, suppressOperationScratch, node, addSuppressOperation); + AddOperations(nodeOperations.AlignmentOperation, alignmentOperationScratch, node, addAlignTokensOperations); + AddOperations(nodeOperations.AnchorIndentationOperations, anchorIndentationOperationsScratch, node, addAnchorIndentationOperations); } // make sure we order align operation from left to right - alignmentOperation.Sort(static (o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); + nodeOperations.AlignmentOperation.Sort(static (o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); return nodeOperations; } @@ -176,6 +176,20 @@ private static void AddOperations(SegmentedList operations, List scratc scratch.Clear(); } + private static void AddOperations(SegmentedList operations, ArrayBuilder scratch, SyntaxNode node, Action, SyntaxNode> addOperations) + { + Debug.Assert(scratch.Count == 0); + + addOperations(scratch, node); + foreach (var operation in scratch) + { + if (operation is not null) + operations.Add(operation); + } + + scratch.Clear(); + } + private void AddTokenOperations( TokenStream tokenStream, SegmentedList list, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs index b0dbfa8d1204c..605b0be2231aa 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs @@ -10,6 +10,7 @@ using System.Reflection; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; @@ -43,7 +44,7 @@ public ChainedFormattingRules(IEnumerable formattingRule _getAdjustSpacesOperationRules = FilterToRulesImplementingMethod(_formattingRules, nameof(AbstractFormattingRule.GetAdjustSpacesOperation)); } - public void AddSuppressOperations(List list, SyntaxNode currentNode) + public void AddSuppressOperations(ArrayBuilder list, SyntaxNode currentNode) { var action = new NextSuppressOperationAction(_addSuppressOperationsRules, index: 0, currentNode, list); action.Invoke(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs index 33620eea5d158..8af68c34064e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/AbstractFormattingRule.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Formatting.Rules; @@ -20,7 +20,7 @@ public virtual AbstractFormattingRule WithOptions(SyntaxFormattingOptions option /// Returns SuppressWrappingIfOnSingleLineOperations under a node either by itself or by /// filtering/replacing operations returned by NextOperation /// - public virtual void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public virtual void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) => nextOperation.Invoke(); /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs index 0b5609b7364b2..08e9a2da873c4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/CompatAbstractFormattingRule.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Formatting.Rules; @@ -13,7 +14,7 @@ internal abstract class CompatAbstractFormattingRule : AbstractFormattingRule #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member [Obsolete("Do not call this method directly (it will Stack Overflow).", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] - public sealed override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public sealed override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { var nextOperationCopy = nextOperation; AddSuppressOperationsSlow(list, node, ref nextOperationCopy); @@ -68,7 +69,7 @@ public sealed override void AddAlignTokensOperations(List /// Returns SuppressWrappingIfOnSingleLineOperations under a node either by itself or by /// filtering/replacing operations returned by NextOperation /// - public virtual void AddSuppressOperationsSlow(List list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) + public virtual void AddSuppressOperationsSlow(ArrayBuilder list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) => base.AddSuppressOperations(list, node, in nextOperation); /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs index a3e051ab705c8..f0a02813eb808 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; @@ -13,7 +14,7 @@ internal readonly struct NextSuppressOperationAction( ImmutableArray formattingRules, int index, SyntaxNode node, - List list) + ArrayBuilder list) { private NextSuppressOperationAction NextAction => new(formattingRules, index + 1, node, list); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs index 0b2d965c14a0f..ea13eaf68014c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs @@ -141,18 +141,6 @@ public static AdjustSpacesOperation CreateAdjustSpacesOperation(int space, Adjus return new AdjustSpacesOperation(space, option); } - /// - /// return SuppressOperation for the node provided by the given formatting rules - /// - internal static IEnumerable GetSuppressOperations(IEnumerable formattingRules, SyntaxNode node, SyntaxFormattingOptions options) - { - var chainedFormattingRules = new ChainedFormattingRules(formattingRules, options); - - var list = new List(); - chainedFormattingRules.AddSuppressOperations(list, node); - return list; - } - /// /// return AnchorIndentationOperation for the node provided by the given formatting rules /// From 731a3021f11e31874bca3b20ba399c09a9febc6b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:25:35 -0700 Subject: [PATCH 280/423] Downstream --- ...taAsSourceService+CompatAbstractMetadataFormattingRule.cs | 5 +++-- .../VisualBasic/Indentation/SpecialFormattingOperation.vb | 3 ++- .../Portable/Formatting/DefaultOperationProvider.vb | 3 ++- .../Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs index 7bf7c7cfc4da7..eda6341df098a 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService+CompatAbstractMetadataFormattingRule.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.ComponentModel; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.MetadataAsSource; @@ -18,7 +19,7 @@ protected abstract class CompatAbstractMetadataFormattingRule : AbstractMetadata #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member [Obsolete("Do not call this method directly (it will Stack Overflow).", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] - public sealed override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public sealed override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { var nextOperationCopy = nextOperation; AddSuppressOperationsSlow(list, node, ref nextOperationCopy); @@ -73,7 +74,7 @@ public sealed override AdjustSpacesOperation GetAdjustSpacesOperation(in SyntaxT /// Returns SuppressWrappingIfOnSingleLineOperations under a node either by itself or by /// filtering/replacing operations returned by NextOperation /// - public virtual void AddSuppressOperationsSlow(List list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) + public virtual void AddSuppressOperationsSlow(ArrayBuilder list, SyntaxNode node, ref NextSuppressOperationAction nextOperation) => base.AddSuppressOperations(list, node, in nextOperation); /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb index f62b444c2c872..0fd8a219aa27c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/Indentation/SpecialFormattingOperation.vb @@ -5,6 +5,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -18,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation _indentStyle = indentStyle End Sub - Public Overrides Sub AddSuppressOperationsSlow(list As List(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) + Public Overrides Sub AddSuppressOperationsSlow(list As ArrayBuilder(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) ' don't suppress anything End Sub diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb b/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb index f581265ed4cf0..49c635c13a72f 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/DefaultOperationProvider.vb @@ -5,6 +5,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities @@ -36,7 +37,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Return New DefaultOperationProvider(options) End Function - Public Overrides Sub AddSuppressOperationsSlow(operations As List(Of SuppressOperation), node As SyntaxNode, ByRef nextAction As NextSuppressOperationAction) + Public Overrides Sub AddSuppressOperationsSlow(operations As ArrayBuilder(Of SuppressOperation), node As SyntaxNode, ByRef nextAction As NextSuppressOperationAction) End Sub Public Overrides Sub AddAnchorIndentationOperationsSlow(operations As List(Of AnchorIndentationOperation), node As SyntaxNode, ByRef nextAction As NextAnchorIndentationOperationAction) diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb index 9c87a6a33365c..eac504169c6df 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Rules/ElasticTriviaFormattingRule.vb @@ -4,6 +4,7 @@ Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting @@ -14,7 +15,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Public Sub New() End Sub - Public Overrides Sub AddSuppressOperationsSlow(list As List(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) + Public Overrides Sub AddSuppressOperationsSlow(list As ArrayBuilder(Of SuppressOperation), node As SyntaxNode, ByRef nextOperation As NextSuppressOperationAction) nextOperation.Invoke() End Sub From cad6075383bffe51e2d232c37c59f3910bb83a06 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:26:07 -0700 Subject: [PATCH 281/423] Downstream --- .../Portable/BraceCompletion/CurlyBraceCompletionService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs index 98ab13f19a3f9..f513c3af27576 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -265,11 +266,11 @@ public override void AddAlignTokensOperations(List list, S } } - public override void AddSuppressOperations(List list, SyntaxNode node, in NextSuppressOperationAction nextOperation) + public override void AddSuppressOperations(ArrayBuilder list, SyntaxNode node, in NextSuppressOperationAction nextOperation) { base.AddSuppressOperations(list, node, in nextOperation); - // not sure exactly what is happening here, but removing the bellow causesthe indentation to be wrong. + // not sure exactly what is happening here, but removing the bellow causes the indentation to be wrong. // remove suppression rules for array and collection initializer if (node.IsInitializerForArrayOrCollectionCreationExpression()) From bdf5fff3f48942dc052a885bf89a92f92554925e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:40:54 -0700 Subject: [PATCH 282/423] Remove array allocations --- .../Core/Formatting/Engine/ChainedFormattingRules.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs index b0dbfa8d1204c..d34b6473a4e55 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs @@ -32,7 +32,7 @@ public ChainedFormattingRules(IEnumerable formattingRule { Contract.ThrowIfNull(formattingRules); - _formattingRules = formattingRules.Select(rule => rule.WithOptions(options)).ToImmutableArray(); + _formattingRules = formattingRules.SelectAsArray(rule => rule.WithOptions(options)); _options = options; _addSuppressOperationsRules = FilterToRulesImplementingMethod(_formattingRules, nameof(AbstractFormattingRule.AddSuppressOperations)); @@ -81,7 +81,7 @@ public void AddAlignTokensOperations(List list, SyntaxNode private static ImmutableArray FilterToRulesImplementingMethod(ImmutableArray rules, string name) { - return rules.Where(rule => + return rules.WhereAsArray(rule => { var type = GetTypeImplementingMethod(rule, name); if (type == typeof(AbstractFormattingRule)) @@ -99,7 +99,7 @@ private static ImmutableArray FilterToRulesImplementingM } return true; - }).ToImmutableArray(); + }); } private static Type? GetTypeImplementingMethod(object obj, string name) From 4c054c56ab37cbe6584dbd2ca84906d4bd8693b7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:46:32 -0700 Subject: [PATCH 283/423] more arrays --- .../Analyzers/Formatting/FormatterHelper.cs | 15 ++--- .../Providers/FormatCodeCleanupProvider.cs | 2 +- .../Core/Portable/Formatting/Formatter.cs | 56 +++++++++---------- .../Formatting/AbstractSyntaxFormatting.cs | 13 ++--- .../Core/Formatting/ISyntaxFormatting.cs | 3 +- 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs b/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs index cc91eb9d9ddc7..ad221cea502b9 100644 --- a/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs +++ b/src/Analyzers/Core/Analyzers/Formatting/FormatterHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Text; @@ -30,10 +31,10 @@ internal static IEnumerable GetDefaultFormattingRules(IS /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [node.FullSpan], syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [node.FullSpan], syntaxFormattingService, options, rules: default, cancellationToken: cancellationToken); public static SyntaxNode Format(SyntaxNode node, TextSpan spanToFormat, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [spanToFormat], syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [spanToFormat], syntaxFormattingService, options, rules: default, cancellationToken: cancellationToken); /// /// Formats the whitespace of a syntax tree. @@ -44,15 +45,15 @@ public static SyntaxNode Format(SyntaxNode node, TextSpan spanToFormat, ISyntaxF /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) - => Format(node, GetAnnotatedSpans(node, annotation), syntaxFormattingService, options, rules, cancellationToken: cancellationToken); + => Format(node, GetAnnotatedSpans(node, annotation), syntaxFormattingService, options, rules.AsImmutableOrNull(), cancellationToken: cancellationToken); - internal static SyntaxNode Format(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetFormattedRoot(cancellationToken); - internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetTextChanges(cancellationToken); - internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable spans, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => syntaxFormattingService.GetFormattingResult(node, spans, options, rules, cancellationToken); /// @@ -63,5 +64,5 @@ internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerab /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, ISyntaxFormatting syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => GetFormattedTextChanges(node, [node.FullSpan], syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, [node.FullSpan], syntaxFormattingService, options, rules: default, cancellationToken: cancellationToken); } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs index 84bdd989f7768..179de6c20bd69 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CodeCleanup.Providers; -internal sealed class FormatCodeCleanupProvider(IEnumerable? rules = null) : ICodeCleanupProvider +internal sealed class FormatCodeCleanupProvider(ImmutableArray rules = default) : ICodeCleanupProvider { public string Name => PredefinedCodeCleanupProviderNames.Format; diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 6803e4697a6df..b68782503aa8a 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -52,7 +52,7 @@ public static Task FormatAsync(Document document, OptionSet? options = #pragma warning restore internal static Task FormatAsync(Document document, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => FormatAsync(document, spans: null, options, rules: null, cancellationToken); + => FormatAsync(document, spans: null, options, rules: default, cancellationToken); /// /// Formats the whitespace in an area of a document corresponding to a text span. @@ -68,7 +68,7 @@ public static Task FormatAsync(Document document, TextSpan span, Optio #pragma warning restore internal static Task FormatAsync(Document document, TextSpan span, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => FormatAsync(document, [span], options, rules: null, cancellationToken); + => FormatAsync(document, [span], options, rules: default, cancellationToken); /// /// Formats the whitespace in areas of a document corresponding to multiple non-overlapping spans. @@ -90,7 +90,7 @@ public static async Task FormatAsync(Document document, IEnumerable FormatAsync(Document document, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Services; @@ -106,19 +106,19 @@ internal static async Task FormatAsync(Document document, IEnumerable< /// An optional cancellation token. /// The formatted document. public static Task FormatAsync(Document document, SyntaxAnnotation annotation, OptionSet? options = null, CancellationToken cancellationToken = default) - => FormatAsync(document, annotation, options, rules: null, cancellationToken: cancellationToken); + => FormatAsync(document, annotation, options, rules: default, cancellationToken: cancellationToken); internal static Task FormatAsync(Document document, SyntaxAnnotation annotation, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => FormatAsync(document, annotation, options, rules: null, cancellationToken); + => FormatAsync(document, annotation, options, rules: default, cancellationToken); - internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var services = document.Project.Solution.Services; return document.WithSyntaxRoot(Format(root, annotation, services, options, rules, cancellationToken)); } - internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, OptionSet? optionSet, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, SyntaxAnnotation annotation, OptionSet? optionSet, ImmutableArray rules, CancellationToken cancellationToken) { if (document == null) { @@ -150,12 +150,12 @@ internal static async Task FormatAsync(Document document, SyntaxAnnota /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, annotation, workspace, options, rules: null, cancellationToken); + => Format(node, annotation, workspace, options, rules: default, cancellationToken); internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, annotation, services, options, rules: null, cancellationToken); + => Format(node, annotation, services, options, rules: default, cancellationToken); - private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { if (workspace == null) { @@ -175,7 +175,7 @@ private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, W return Format(node, GetAnnotatedSpans(node, annotation), workspace, options, rules, cancellationToken); } - internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => Format(node, GetAnnotatedSpans(node, annotation), services, options, rules, cancellationToken); /// @@ -187,10 +187,10 @@ internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, [node.FullSpan], workspace, options, rules: null, cancellationToken); + => Format(node, [node.FullSpan], workspace, options, rules: default, cancellationToken); internal static SyntaxNode Format(SyntaxNode node, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [node.FullSpan], services, options, rules: null, cancellationToken); + => Format(node, [node.FullSpan], services, options, rules: default, cancellationToken); /// /// Formats the whitespace in areas of a syntax tree identified by a span. @@ -202,10 +202,10 @@ internal static SyntaxNode Format(SyntaxNode node, SolutionServices services, Sy /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, [span], workspace, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [span], workspace, options, rules: default, cancellationToken: cancellationToken); internal static SyntaxNode Format(SyntaxNode node, TextSpan span, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => Format(node, [span], services, options, rules: null, cancellationToken: cancellationToken); + => Format(node, [span], services, options, rules: default, cancellationToken: cancellationToken); /// /// Formats the whitespace in areas of a syntax tree identified by multiple non-overlapping spans. @@ -217,18 +217,18 @@ internal static SyntaxNode Format(SyntaxNode node, TextSpan span, SolutionServic /// An optional cancellation token. /// The formatted tree's root node. public static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, spans, workspace, options, rules: null, cancellationToken: cancellationToken); + => Format(node, spans, workspace, options, rules: default, cancellationToken: cancellationToken); - private static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null ? node : formattingResult.GetFormattedRoot(cancellationToken); } - internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) => GetFormattingResult(node, spans, services, options, rules, cancellationToken).GetFormattedRoot(cancellationToken); - private static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { if (workspace == null) { @@ -252,7 +252,7 @@ internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, return languageFormatter.GetFormattingResult(node, spans, formattingOptions, rules, cancellationToken); } - internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { var formatter = services.GetRequiredLanguageService(node.Language); return formatter.GetFormattingResult(node, spans, options, rules, cancellationToken); @@ -267,10 +267,10 @@ internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerab /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, [node.FullSpan], workspace, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, [node.FullSpan], workspace, options, rules: default, cancellationToken: cancellationToken); internal static IList GetFormattedTextChanges(SyntaxNode node, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) - => GetFormattedTextChanges(node, [node.FullSpan], services, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, [node.FullSpan], services, options, rules: default, cancellationToken: cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. @@ -282,10 +282,10 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, Solut /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, [span], workspace, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, [span], workspace, options, rules: default, cancellationToken); internal static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, [span], services, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, [span], services, options, rules: default, cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. @@ -297,12 +297,12 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, TextS /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, spans, workspace, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, spans, workspace, options, rules: default, cancellationToken); internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, spans, services, options, rules: null, cancellationToken); + => GetFormattedTextChanges(node, spans, services, options, rules: default, cancellationToken); - private static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, ImmutableArray rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null @@ -310,7 +310,7 @@ private static IList GetFormattedTextChanges(SyntaxNode node, IEnume : formattingResult.GetTextChanges(cancellationToken); } - internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken = default) + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, SolutionServices services, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken = default) { var formatter = services.GetRequiredLanguageService(node.Language); return formatter.GetFormattingResult(node, spans, options, rules, cancellationToken).GetTextChanges(cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs index 662e591c5012e..22865e0751e08 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormatting.cs @@ -7,14 +7,12 @@ using System.Collections.Immutable; using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; @@ -22,10 +20,6 @@ internal abstract class AbstractSyntaxFormatting : ISyntaxFormatting { private static readonly Func s_notEmpty = s => !s.IsEmpty; - protected AbstractSyntaxFormatting() - { - } - public abstract SyntaxFormattingOptions DefaultOptions { get; } public abstract SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions); @@ -33,9 +27,9 @@ protected AbstractSyntaxFormatting() protected abstract IFormattingResult CreateAggregatedFormattingResult(SyntaxNode node, IList results, TextSpanIntervalTree? formattingSpans = null); - protected abstract AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, IEnumerable rules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken); + protected abstract AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, ImmutableArray rules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken); - public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken) { IReadOnlyList spansToFormat; @@ -53,7 +47,8 @@ public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? results = null; foreach (var (startToken, endToken) in node.ConvertToTokenPairs(spansToFormat)) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs index ef25534b4e233..aa3bf201c3742 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormatting.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; @@ -18,5 +17,5 @@ internal interface ISyntaxFormatting SyntaxFormattingOptions GetFormattingOptions(IOptionsReader options, SyntaxFormattingOptions? fallbackOptions); ImmutableArray GetDefaultFormattingRules(); - IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken); + IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, ImmutableArray rules, CancellationToken cancellationToken); } From f02b7de40193051dc93ceda581d3f897cc900e25 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:49:54 -0700 Subject: [PATCH 284/423] more arrays --- .../Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs | 2 +- .../AbstractRemoveUnusedValuesCodeFixProvider.cs | 4 ++-- .../AbstractUseConditionalExpressionCodeFixProvider.cs | 2 +- .../ChangeSignature/AbstractChangeSignatureService.cs | 2 +- .../AbstractGenerateEqualsAndGetHashCodeService.cs | 2 +- .../MetadataAsSource/AbstractMetadataAsSourceService.cs | 2 +- .../AbstractUseAutoPropertyCodeFixProvider.cs | 6 ++---- .../Core/Portable/Formatting/AbstractFormattingService.cs | 2 +- .../Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs | 6 ++---- .../Portable/Formatting/VisualBasicSyntaxFormatting.vb | 2 +- 10 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs b/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs index 572aa92beeb8c..7eb990634811a 100644 --- a/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs +++ b/src/Analyzers/Core/Analyzers/Formatting/FormattingAnalyzerHelper.cs @@ -24,7 +24,7 @@ internal static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context, Format var root = tree.GetRoot(cancellationToken); var span = context.FilterSpan.HasValue ? context.FilterSpan.GetValueOrDefault() : root.FullSpan; var spans = SpecializedCollections.SingletonEnumerable(span); - var formattingChanges = Formatter.GetFormattedTextChanges(root, spans, formattingProvider, options, rules: null, cancellationToken); + var formattingChanges = Formatter.GetFormattedTextChanges(root, spans, formattingProvider, options, rules: default, cancellationToken); // formattingChanges could include changes that impact a larger section of the original document than // necessary. Before reporting diagnostics, process the changes to minimize the span of individual diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs index c176e52e9c84b..b70f54298a2fd 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs @@ -850,10 +850,10 @@ private async Task AdjustLocalDeclarationsAsync( // Run formatter prior to invoking IMoveDeclarationNearReferenceService. #if CODE_STYLE var provider = GetSyntaxFormatting(); - rootWithTrackedNodes = FormatterHelper.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: null, cancellationToken); + rootWithTrackedNodes = FormatterHelper.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: default, cancellationToken); #else var provider = document.Project.Solution.Services; - rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: null, cancellationToken); + rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: default, cancellationToken); #endif document = document.WithSyntaxRoot(rootWithTrackedNodes); diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs index 5f39393f7b430..93ad981de4bc3 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs @@ -72,7 +72,7 @@ await FixOneAsync( // formatted to explicitly format things. Note: all we will format is the new // conditional expression as that's the only node that has the appropriate // annotation on it. - var rules = new List { GetMultiLineFormattingRule() }; + var rules = ImmutableArray.Create(GetMultiLineFormattingRule()); #if CODE_STYLE var provider = GetSyntaxFormatting(); diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index 260baa4eb4047..fc9ea2a994409 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -58,7 +58,7 @@ public abstract Task ChangeSignatureAsync( LineFormattingOptionsProvider fallbackOptions, CancellationToken cancellationToken); - protected abstract IEnumerable GetFormattingRules(Document document); + protected abstract ImmutableArray GetFormattingRules(Document document); protected abstract T TransferLeadingWhitespaceTrivia(T newArgument, SyntaxNode oldArgument) where T : SyntaxNode; diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs index e39103ea0b9c2..c0f95a1f0105d 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs @@ -33,7 +33,7 @@ public async Task FormatDocumentAsync(Document document, SyntaxFormatt var formattedDocument = await Formatter.FormatAsync( document, s_specializedFormattingAnnotation, - options, rules, cancellationToken).ConfigureAwait(false); + options, rules.ToImmutableArray(), cancellationToken).ConfigureAwait(false); return formattedDocument; } diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs index 874bd38c45b91..912efe1b8e8bf 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs @@ -85,7 +85,7 @@ public async Task AddSourceToAsync( /// /// provide formatting rules to be used when formatting MAS file /// - protected abstract IEnumerable GetFormattingRules(Document document); + protected abstract ImmutableArray GetFormattingRules(Document document); /// /// Prepends a region directive at the top of the document with a name containing diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 5baec78131adb..54fbfad723cda 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -41,7 +41,7 @@ public sealed override ImmutableArray FixableDiagnosticIds protected abstract TPropertyDeclaration GetPropertyDeclaration(SyntaxNode node); protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator); - protected abstract IEnumerable GetFormattingRules(Document document); + protected abstract ImmutableArray GetFormattingRules(Document document); protected abstract Task UpdatePropertyAsync( Document propertyDocument, Compilation compilation, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol, @@ -281,10 +281,8 @@ private static bool CanEditDocument( private async Task FormatAsync(SyntaxNode newRoot, Document document, CodeCleanupOptionsProvider fallbackOptions, CancellationToken cancellationToken) { var formattingRules = GetFormattingRules(document); - if (formattingRules == null) - { + if (formattingRules.IsDefault) return newRoot; - } var options = await document.GetSyntaxFormattingOptionsAsync(fallbackOptions, cancellationToken).ConfigureAwait(false); return Formatter.Format(newRoot, SpecializedFormattingAnnotation, document.Project.Solution.Services, options, formattingRules, cancellationToken); diff --git a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs index 00ea1fc9737f8..e56e065978dd7 100644 --- a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs +++ b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs @@ -18,6 +18,6 @@ internal abstract class AbstractFormattingService : IFormattingService public Task FormatAsync(Document document, IEnumerable? spans, LineFormattingOptions lineFormattingOptions, SyntaxFormattingOptions? syntaxFormattingOptions, CancellationToken cancellationToken) { Contract.ThrowIfNull(syntaxFormattingOptions); - return Formatter.FormatAsync(document, spans, syntaxFormattingOptions, rules: null, cancellationToken); + return Formatter.FormatAsync(document, spans, syntaxFormattingOptions, rules: default, cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs index e4006fad174b4..d56f6b058ed0e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormatting.cs @@ -7,10 +7,8 @@ using System.Threading; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Collections; namespace Microsoft.CodeAnalysis.CSharp.Formatting; @@ -47,6 +45,6 @@ public override SyntaxFormattingOptions GetFormattingOptions(IOptionsReader opti protected override IFormattingResult CreateAggregatedFormattingResult(SyntaxNode node, IList results, TextSpanIntervalTree? formattingSpans = null) => new AggregatedFormattingResult(node, results, formattingSpans); - protected override AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, IEnumerable formattingRules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken) + protected override AbstractFormattingResult Format(SyntaxNode node, SyntaxFormattingOptions options, ImmutableArray formattingRules, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken) => new CSharpFormatEngine(node, options, formattingRules, startToken, endToken).Format(cancellationToken); } diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb index f0b7fee2a86b6..598ce18a2d40b 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormatting.vb @@ -43,7 +43,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Return New AggregatedFormattingResult(node, results, formattingSpans) End Function - Protected Overrides Function Format(root As SyntaxNode, options As SyntaxFormattingOptions, formattingRules As IEnumerable(Of AbstractFormattingRule), startToken As SyntaxToken, endToken As SyntaxToken, cancellationToken As CancellationToken) As AbstractFormattingResult + Protected Overrides Function Format(root As SyntaxNode, options As SyntaxFormattingOptions, formattingRules As ImmutableArray(Of AbstractFormattingRule), startToken As SyntaxToken, endToken As SyntaxToken, cancellationToken As CancellationToken) As AbstractFormattingResult Return New VisualBasicFormatEngine(root, options, formattingRules, startToken, endToken).Format(cancellationToken) End Function End Class From b10e83b4a9a891a41055b8433fe88537094acf48 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:50:44 -0700 Subject: [PATCH 285/423] more arrays --- .../Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs | 4 ++-- .../LanguageServices/CSharpRemoveUnnecessaryImportsService.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs index 2e538e86dd901..cabead6c31c30 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Indentation/CSharpSmartTokenFormatter.cs @@ -103,7 +103,7 @@ public IList FormatToken(SyntaxToken token, CancellationToken cancel } } - var smartTokenformattingRules = new SmartTokenFormattingRule().Concat(_formattingRules); + ImmutableArray smartTokenFormattingRules = [new SmartTokenFormattingRule(), .. _formattingRules]; var adjustedStartPosition = previousToken.SpanStart; if (token.IsKind(SyntaxKind.OpenBraceToken) && _options.IndentStyle != FormattingOptions2.IndentStyle.Smart) @@ -117,7 +117,7 @@ public IList FormatToken(SyntaxToken token, CancellationToken cancel var formatter = CSharpSyntaxFormatting.Instance; var result = formatter.GetFormattingResult( - _root, [TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition)], _options.FormattingOptions, smartTokenformattingRules, cancellationToken); + _root, [TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition)], _options.FormattingOptions, smartTokenFormattingRules, cancellationToken); return result.GetTextChanges(cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs index 74df6ecf17b13..f59a9424eb31b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs @@ -79,7 +79,7 @@ public override async Task RemoveUnnecessaryImportsAsync( #endif var spans = new List(); AddFormattingSpans(newRoot, spans, cancellationToken); - var formattedRoot = Formatter.Format(newRoot, spans, provider, formattingOptions, rules: null, cancellationToken); + var formattedRoot = Formatter.Format(newRoot, spans, provider, formattingOptions, rules: default, cancellationToken); return document.WithSyntaxRoot(formattedRoot); } From 61a050da4dbdee34b6bb0f95d742a45630fc1030 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:51:41 -0700 Subject: [PATCH 286/423] more arrays --- .../Portable/Formatting/CSharpSyntaxFormattingService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs index 5df82749fae48..050baddb41cdd 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs @@ -331,8 +331,7 @@ public ImmutableArray GetFormattingChangesOnPaste(ParsedDocument doc var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(document.Root, textSpan); var service = _services.GetRequiredService(); - var rules = new List() { new PasteFormattingRule() }; - rules.AddRange(service.GetDefaultFormattingRules()); + ImmutableArray rules = [new PasteFormattingRule(), .. service.GetDefaultFormattingRules()]; var result = service.GetFormattingResult(document.Root, [formattingSpan], options, rules, cancellationToken); return [.. result.GetTextChanges(cancellationToken)]; From e2c91b3233543cd70de6804eadd40237fa0695d0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:53:25 -0700 Subject: [PATCH 287/423] more arrays --- .../Formatting/AbstractFormatDocumentHandlerBase.cs | 2 +- .../ChangeSignature/VisualBasicChangeSignatureService.vb | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index 806e79597b426..778a3dd2ac3ea 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs @@ -42,7 +42,7 @@ internal abstract class AbstractFormatDocumentHandlerBase(); edits.AddRange(textChanges.Select(change => ProtocolConversions.TextChangeToTextEdit(change, text))); diff --git a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb index 94694f171c585..e4144cbdb1143 100644 --- a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb +++ b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb @@ -7,6 +7,7 @@ Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ChangeSignature +Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.FindSymbols Imports Microsoft.CodeAnalysis.Formatting @@ -734,8 +735,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Return results.ToImmutableAndFree() End Function - Protected Overrides Function GetFormattingRules(document As Document) As IEnumerable(Of AbstractFormattingRule) - Return SpecializedCollections.SingletonEnumerable(Of AbstractFormattingRule)(New ChangeSignatureFormattingRule()).Concat(Formatter.GetDefaultFormattingRules(document)) + Protected Overrides Function GetFormattingRules(document As Document) As ImmutableArray(Of AbstractFormattingRule) + Dim coreRules = Formatter.GetDefaultFormattingRules(document) + Dim result = New FixedSizeArrayBuilder(Of AbstractFormattingRule)(1 + coreRules.Length) + result.Add(New ChangeSignatureFormattingRule()) + result.AddRange(coreRules) + Return result.MoveToImmutable() End Function Protected Overrides Function TransferLeadingWhitespaceTrivia(Of T As SyntaxNode)(newArgument As T, oldArgument As SyntaxNode) As T From 2f2e3eeefd74a40466eb58baf913418105ad3c52 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:54:14 -0700 Subject: [PATCH 288/423] more arrays --- .../VisualBasicMetadataAsSourceService.vb | 9 +++++++-- .../VisualBasicUseAutoPropertyCodeFixProvider.vb | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb b/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb index b7bbdec9277cd..0d6a04128eb5a 100644 --- a/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb +++ b/src/Features/VisualBasic/Portable/MetadataAsSource/VisualBasicMetadataAsSourceService.vb @@ -11,6 +11,7 @@ Imports Microsoft.CodeAnalysis.Formatting.Rules Imports Microsoft.CodeAnalysis.MetadataAsSource Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Imports Microsoft.CodeAnalysis.VisualBasic.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -63,8 +64,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MetadataAsSource Return document.WithSyntaxRoot(newSyntaxRoot) End Function - Protected Overrides Function GetFormattingRules(document As Document) As IEnumerable(Of AbstractFormattingRule) - Return _memberSeparationRule.Concat(Formatter.GetDefaultFormattingRules(document)) + Protected Overrides Function GetFormattingRules(document As Document) As ImmutableArray(Of AbstractFormattingRule) + Dim coreRules = Formatter.GetDefaultFormattingRules(document) + Dim result = New FixedSizeArrayBuilder(Of AbstractFormattingRule)(1 + coreRules.Length) + result.Add(_memberSeparationRule) + result.AddRange(coreRules) + Return result.MoveToImmutable() End Function Protected Overrides Function GetReducers() As ImmutableArray(Of AbstractReducer) diff --git a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb index 32aae58938712..244c4a39c372c 100644 --- a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb @@ -2,6 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Collections.Immutable Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading @@ -33,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Return Utilities.GetNodeToRemove(identifier) End Function - Protected Overrides Function GetFormattingRules(document As Document) As IEnumerable(Of AbstractFormattingRule) + Protected Overrides Function GetFormattingRules(document As Document) As ImmutableArray(Of AbstractFormattingRule) Return Nothing End Function From b35b481b17e8e68eb0cb2a5291d809e3b5119df4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:55:53 -0700 Subject: [PATCH 289/423] more arrays --- .../ChangeSignature/CSharpChangeSignatureService.cs | 4 ++-- .../DecompiledSource/CSharpDecompiledSourceService.cs | 2 +- .../MetadataAsSource/CSharpMetadataAsSourceService.cs | 4 ++-- .../CSharpUseAutoPropertyCodeFixProvider.cs | 10 +++------- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs index e12e26279379c..d22b7ece85de4 100644 --- a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs +++ b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs @@ -888,8 +888,8 @@ public override async Task> DetermineCascadedSymbolsFrom return convertedMethodGroups; } - protected override IEnumerable GetFormattingRules(Document document) - => Formatter.GetDefaultFormattingRules(document).Concat(new ChangeSignatureFormattingRule()); + protected override ImmutableArray GetFormattingRules(Document document) + => [.. Formatter.GetDefaultFormattingRules(document), new ChangeSignatureFormattingRule()]; protected override TArgumentSyntax AddNameToArgument(TArgumentSyntax newArgument, string name) { diff --git a/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs b/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs index 6b4a2e9bd14db..9cdedae3a1579 100644 --- a/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs +++ b/src/Features/CSharp/Portable/DecompiledSource/CSharpDecompiledSourceService.cs @@ -63,7 +63,7 @@ public static async Task FormatDocumentAsync(Document document, Syntax document, [node.FullSpan], options, - CSharpDecompiledSourceFormattingRule.Instance.Concat(Formatter.GetDefaultFormattingRules(document)), + [CSharpDecompiledSourceFormattingRule.Instance, .. Formatter.GetDefaultFormattingRules(document)], cancellationToken).ConfigureAwait(false); return formattedDoc; diff --git a/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs b/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs index 3f51710433897..e8681c83af413 100644 --- a/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs +++ b/src/Features/CSharp/Portable/MetadataAsSource/CSharpMetadataAsSourceService.cs @@ -57,8 +57,8 @@ protected override async Task AddAssemblyInfoRegionAsync(Document docu return document.WithSyntaxRoot(newRoot); } - protected override IEnumerable GetFormattingRules(Document document) - => s_memberSeparationRule.Concat(Formatter.GetDefaultFormattingRules(document)); + protected override ImmutableArray GetFormattingRules(Document document) + => [s_memberSeparationRule, .. Formatter.GetDefaultFormattingRules(document)]; protected override async Task ConvertDocCommentsToRegularCommentsAsync(Document document, IDocumentationCommentFormattingService docCommentFormattingService, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs index c96dbb0c61400..adf0adae32d83 100644 --- a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs @@ -5,6 +5,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -84,13 +85,8 @@ protected override async Task UpdatePropertyAsync( return updatedProperty.WithTrailingTrivia(trailingTrivia).WithAdditionalAnnotations(SpecializedFormattingAnnotation); } - protected override IEnumerable GetFormattingRules(Document document) - { - var rules = new List { new SingleLinePropertyFormattingRule() }; - rules.AddRange(Formatter.GetDefaultFormattingRules(document)); - - return rules; - } + protected override ImmutableArray GetFormattingRules(Document document) + => [new SingleLinePropertyFormattingRule(), .. Formatter.GetDefaultFormattingRules(document)]; private class SingleLinePropertyFormattingRule : AbstractFormattingRule { From ff47eec8496c4b6e176904c0561e05012152b24d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:57:56 -0700 Subject: [PATCH 290/423] more arrays --- .../AutomaticLineEnderCommandHandler.cs | 2 +- .../CommentSelection/AbstractCommentSelectionBase.cs | 2 +- .../VisualBasic/LineCommit/CommitFormatter.vb | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 7f5a193236ab7..c3fcbfbd57629 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -105,7 +105,7 @@ protected override IList FormatBasedOnEndToken(ParsedDocument docume root, [CommonFormattingHelpers.GetFormattingSpan(root, span.Value)], options, - rules: null, + rules: default, cancellationToken).GetTextChanges(cancellationToken); } diff --git a/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs b/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs index 785c8d8e8eb64..0503a8d028d8f 100644 --- a/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs +++ b/src/EditorFeatures/Core/CommentSelection/AbstractCommentSelectionBase.cs @@ -154,7 +154,7 @@ private void ApplyEdits(Document document, ITextView textView, ITextBuffer subje var formattingOptions = subjectBuffer.GetSyntaxFormattingOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); var formattingSpans = trackingSnapshotSpans.Select(change => CommonFormattingHelpers.GetFormattingSpan(newRoot, change.Span.ToTextSpan())); - var formattedChanges = Formatter.GetFormattedTextChanges(newRoot, formattingSpans, document.Project.Solution.Services, formattingOptions, rules: null, cancellationToken); + var formattedChanges = Formatter.GetFormattedTextChanges(newRoot, formattingSpans, document.Project.Solution.Services, formattingOptions, rules: default, cancellationToken); subjectBuffer.ApplyChanges(formattedChanges); diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 5d857106ba245..6fa92b52053b2 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -166,7 +166,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit oldTree As SyntaxTree, newDirtySpan As SnapshotSpan, newTree As SyntaxTree, - cancellationToken As CancellationToken) As IEnumerable(Of AbstractFormattingRule) + cancellationToken As CancellationToken) As ImmutableArray(Of AbstractFormattingRule) ' if the span we are going to format is same as the span that got changed, don't bother to do anything special. ' just do full format of the span. @@ -212,12 +212,17 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit ' for now, do very simple checking. basically, we see whether we get same number of indent operation for the give span. alternative, but little bit ' more expensive and complex, we can actually calculate indentation right after the span, and see whether that is changed. not sure whether that much granularity ' is needed. + Dim coreRules = Formatter.GetDefaultFormattingRules(languageServices) + If GetNumberOfIndentOperations(languageServices, options, oldTree, oldDirtySpan, cancellationToken) = GetNumberOfIndentOperations(languageServices, options, newTree, newDirtySpan, cancellationToken) Then - Return (New NoAnchorFormatterRule()).Concat(Formatter.GetDefaultFormattingRules(languageServices)) + Dim result = New FixedSizeArrayBuilder(Of AbstractFormattingRule)(coreRules.Length + 1) + result.Add(New NoAnchorFormatterRule()) + result.AddRange(coreRules) + Return result.MoveToImmutable() End If - Return Formatter.GetDefaultFormattingRules(languageServices) + Return coreRules End Function Private Shared Function GetNumberOfIndentOperations( From 8b5d1a2a2efebbfde4cb9ca495e33dd982ec8389 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 14:59:21 -0700 Subject: [PATCH 291/423] more arrays --- .../AbstractLanguageService`2.IVsLanguageTextOps.cs | 3 ++- src/VisualStudio/Core/Def/Venus/ContainedDocument.cs | 3 ++- .../Core/Def/Venus/ContainedLanguageCodeSupport.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index e097e22eeee74..6025817315a52 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -4,6 +4,7 @@ #nullable disable +using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; @@ -66,7 +67,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // Since we know we are on the UI thread, lets get the base indentation now, so that there is less // cleanup work to do later in Venus. var ruleFactory = Workspace.Services.GetService(); - var rules = ruleFactory.CreateRule(documentSyntax, start).Concat(Formatter.GetDefaultFormattingRules(document.Project.Services)); + ImmutableArray rules = [ruleFactory.CreateRule(documentSyntax, start), .. Formatter.GetDefaultFormattingRules(document.Project.Services)]; // use formatting that return text changes rather than tree rewrite which is more expensive var formatter = document.GetRequiredLanguageService(); diff --git a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs index 114576ac819e8..bba7f5940399a 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; @@ -804,7 +805,7 @@ private void AdjustIndentationForSpan( venusFormattingRules.Add(baseIndentationRule); venusFormattingRules.Add(ContainedDocumentPreserveFormattingRule.Instance); - var formattingRules = venusFormattingRules.Concat(Formatter.GetDefaultFormattingRules(document)); + ImmutableArray formattingRules = [.. venusFormattingRules, .. Formatter.GetDefaultFormattingRules(document)]; var services = document.Project.Solution.Services; var formatter = document.GetRequiredLanguageService(); diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs index d3aa181cca369..159638fcab369 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs @@ -228,7 +228,7 @@ public static Tuple EnsureEventHandler( newRoot = Simplifier.ReduceAsync( targetDocument.WithSyntaxRoot(newRoot), Simplifier.Annotation, options.CleanupOptions.SimplifierOptions, cancellationToken).WaitAndGetResult_Venus(cancellationToken).GetSyntaxRootSynchronously(cancellationToken); - var formattingRules = additionalFormattingRule.Concat(Formatter.GetDefaultFormattingRules(targetDocument)); + ImmutableArray formattingRules = [additionalFormattingRule, .. Formatter.GetDefaultFormattingRules(targetDocument)]; newRoot = Formatter.Format( newRoot, From 664395b615659083f556ae63eec53de4c87ef4e6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:03:20 -0700 Subject: [PATCH 292/423] Cache --- .../Formatting/Engine/AbstractFormatEngine.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 60cd1f2e7f4fa..566799d52e5d1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Collections; @@ -45,21 +46,35 @@ internal abstract partial class AbstractFormatEngine internal readonly SyntaxFormattingOptions Options; internal readonly TreeData TreeData; + private static readonly object s_gate = new(); + private static (ImmutableArray formattingRules, SyntaxFormattingOptions options, ChainedFormattingRules chainedRules) s_lastRulesAndOptions; + public AbstractFormatEngine( TreeData treeData, SyntaxFormattingOptions options, - IEnumerable formattingRules, + ImmutableArray formattingRules, SyntaxToken startToken, SyntaxToken endToken) : this( treeData, options, - new ChainedFormattingRules(formattingRules, options), + GetChainedFormattingRules(formattingRules, options), startToken, endToken) { } + private static ChainedFormattingRules GetChainedFormattingRules(ImmutableArray formattingRules, SyntaxFormattingOptions options) + { + lock (s_gate) + { + if (formattingRules != s_lastRulesAndOptions.formattingRules || options != s_lastRulesAndOptions.options) + s_lastRulesAndOptions = (formattingRules, options, new ChainedFormattingRules(formattingRules, options)); + + return s_lastRulesAndOptions.chainedRules; + } + } + internal AbstractFormatEngine( TreeData treeData, SyntaxFormattingOptions options, From 5617542d1d38de241941d781663f14909869e854 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:04:38 -0700 Subject: [PATCH 293/423] Formatting --- .../Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs index d34b6473a4e55..ed0ce48fd8c46 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs @@ -85,17 +85,13 @@ private static ImmutableArray FilterToRulesImplementingM { var type = GetTypeImplementingMethod(rule, name); if (type == typeof(AbstractFormattingRule)) - { return false; - } if (type == typeof(CompatAbstractFormattingRule)) { type = GetTypeImplementingMethod(rule, name + "Slow"); if (type == typeof(CompatAbstractFormattingRule)) - { return false; - } } return true; From 4c97927e4b0de9b0a87ca64fd3fe5771bf3039cb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:05:53 -0700 Subject: [PATCH 294/423] Arrays --- .../Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs | 5 ++--- .../Portable/Formatting/Engine/VisualBasicFormatEngine.vb | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs index 8800b22006f67..11c3eb47cef74 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpFormatEngine.cs @@ -2,9 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.LanguageService; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.LanguageService; @@ -16,7 +15,7 @@ internal class CSharpFormatEngine : AbstractFormatEngine public CSharpFormatEngine( SyntaxNode node, SyntaxFormattingOptions options, - IEnumerable formattingRules, + ImmutableArray formattingRules, SyntaxToken startToken, SyntaxToken endToken) : base(TreeData.Create(node), diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb index ab3418217b4c8..d6b00a74ecdb5 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/Engine/VisualBasicFormatEngine.vb @@ -2,7 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. -Imports Microsoft.CodeAnalysis.Diagnostics +Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules Imports Microsoft.CodeAnalysis.LanguageService @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Public Sub New(node As SyntaxNode, options As SyntaxFormattingOptions, - formattingRules As IEnumerable(Of AbstractFormattingRule), + formattingRules As ImmutableArray(Of AbstractFormattingRule), startToken As SyntaxToken, endToken As SyntaxToken) MyBase.New(TreeData.Create(node), From 7c5115d1a35924e7953cddc7b8d0686e95f356b5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:15:08 -0700 Subject: [PATCH 295/423] inline --- .../Core/Def/Venus/ContainedLanguageCodeSupport.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs index 159638fcab369..091bb01bbda55 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs @@ -228,14 +228,12 @@ public static Tuple EnsureEventHandler( newRoot = Simplifier.ReduceAsync( targetDocument.WithSyntaxRoot(newRoot), Simplifier.Annotation, options.CleanupOptions.SimplifierOptions, cancellationToken).WaitAndGetResult_Venus(cancellationToken).GetSyntaxRootSynchronously(cancellationToken); - ImmutableArray formattingRules = [additionalFormattingRule, .. Formatter.GetDefaultFormattingRules(targetDocument)]; - newRoot = Formatter.Format( newRoot, Formatter.Annotation, targetDocument.Project.Solution.Services, options.CleanupOptions.FormattingOptions, - formattingRules, + [additionalFormattingRule, .. Formatter.GetDefaultFormattingRules(targetDocument)], cancellationToken); var newMember = newRoot.GetAnnotatedNodesAndTokens(annotation).Single(); From 49422b86bfc4a45c8db30fa33237c936f2e89f7e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:15:32 -0700 Subject: [PATCH 296/423] inline --- src/VisualStudio/Core/Def/Venus/ContainedDocument.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs index bba7f5940399a..fe4d67f41a20f 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedDocument.cs @@ -805,13 +805,13 @@ private void AdjustIndentationForSpan( venusFormattingRules.Add(baseIndentationRule); venusFormattingRules.Add(ContainedDocumentPreserveFormattingRule.Instance); - ImmutableArray formattingRules = [.. venusFormattingRules, .. Formatter.GetDefaultFormattingRules(document)]; - var services = document.Project.Solution.Services; var formatter = document.GetRequiredLanguageService(); var changes = formatter.GetFormattingResult( root, new TextSpan[] { CommonFormattingHelpers.GetFormattingSpan(root, visibleSpan) }, - options, formattingRules, CancellationToken.None).GetTextChanges(CancellationToken.None); + options, + [.. venusFormattingRules, .. Formatter.GetDefaultFormattingRules(document)], + CancellationToken.None).GetTextChanges(CancellationToken.None); visibleSpans.Add(visibleSpan); var newChanges = FilterTextChanges(document.GetTextSynchronously(CancellationToken.None), visibleSpans, changes.ToReadOnlyCollection()).Where(t => visibleSpan.Contains(t.Span)); From 960f7adb44e08a49729d1e1bdd02e4f6d3372529 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:15:59 -0700 Subject: [PATCH 297/423] inline --- .../AbstractLanguageService`2.IVsLanguageTextOps.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index 6025817315a52..180174c688ae6 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -67,12 +67,13 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // Since we know we are on the UI thread, lets get the base indentation now, so that there is less // cleanup work to do later in Venus. var ruleFactory = Workspace.Services.GetService(); - ImmutableArray rules = [ruleFactory.CreateRule(documentSyntax, start), .. Formatter.GetDefaultFormattingRules(document.Project.Services)]; // use formatting that return text changes rather than tree rewrite which is more expensive var formatter = document.GetRequiredLanguageService(); - var originalChanges = formatter.GetFormattingResult(root, [adjustedSpan], formattingOptions, rules, cancellationToken) - .GetTextChanges(cancellationToken); + var originalChanges = formatter.GetFormattingResult( + root, [adjustedSpan], formattingOptions, + [ruleFactory.CreateRule(documentSyntax, start), .. Formatter.GetDefaultFormattingRules(document.Project.Services)], + cancellationToken).GetTextChanges(cancellationToken); var originalSpan = RoslynTextSpan.FromBounds(start, end); var formattedChanges = ruleFactory.FilterFormattedChanges(document.Id, originalSpan, originalChanges); From 981b6aa032b1ed2fc94af899e517bf2a92ccd1d3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:16:23 -0700 Subject: [PATCH 298/423] inline --- .../Portable/Formatting/CSharpSyntaxFormattingService.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs index 050baddb41cdd..61015f6f2af0a 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs @@ -331,9 +331,8 @@ public ImmutableArray GetFormattingChangesOnPaste(ParsedDocument doc var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(document.Root, textSpan); var service = _services.GetRequiredService(); - ImmutableArray rules = [new PasteFormattingRule(), .. service.GetDefaultFormattingRules()]; - - var result = service.GetFormattingResult(document.Root, [formattingSpan], options, rules, cancellationToken); + var result = service.GetFormattingResult( + document.Root, [formattingSpan], options, [new PasteFormattingRule(), .. service.GetDefaultFormattingRules()], cancellationToken); return [.. result.GetTextChanges(cancellationToken)]; } From be76b217a7b5bce8377839aae218d60647bebba4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 15:17:52 -0700 Subject: [PATCH 299/423] Docs --- .../Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 566799d52e5d1..d9e2032fb1408 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -46,6 +46,11 @@ internal abstract partial class AbstractFormatEngine internal readonly SyntaxFormattingOptions Options; internal readonly TreeData TreeData; + /// + /// It is very common to be formatting lots of documents at teh same time, with the same set of formatting rules and + /// options. To help with that, cache the last set of ChainedFormattingRules that was produced, as it is not a cheap + /// type to create. + /// private static readonly object s_gate = new(); private static (ImmutableArray formattingRules, SyntaxFormattingOptions options, ChainedFormattingRules chainedRules) s_lastRulesAndOptions; From db8efa88680353519ef3be8dbeaa0e9325c7a476 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 16:14:54 -0700 Subject: [PATCH 300/423] Simplify --- .../AbstractGenerateEqualsAndGetHashCodeService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs index c0f95a1f0105d..1d835f2358fea 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/AbstractGenerateEqualsAndGetHashCodeService.cs @@ -28,12 +28,12 @@ protected abstract bool TryWrapWithUnchecked( public async Task FormatDocumentAsync(Document document, SyntaxFormattingOptions options, CancellationToken cancellationToken) { - var rules = new List { new FormatLargeBinaryExpressionRule(document.GetRequiredLanguageService()) }; - rules.AddRange(Formatter.GetDefaultFormattingRules(document)); - + var formatBinaryRule = new FormatLargeBinaryExpressionRule(document.GetRequiredLanguageService()); var formattedDocument = await Formatter.FormatAsync( document, s_specializedFormattingAnnotation, - options, rules.ToImmutableArray(), cancellationToken).ConfigureAwait(false); + options, + [formatBinaryRule, .. Formatter.GetDefaultFormattingRules(document)], + cancellationToken).ConfigureAwait(false); return formattedDocument; } From e97fcbe5131508400209a56ba8a76c05f3713319 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 16:17:58 -0700 Subject: [PATCH 301/423] switch to tuple --- .../OmniSharp/Formatting/OmniSharpFormatter.cs | 2 +- .../Formatting/FormattingTestBase.cs | 2 +- .../Formatting/Engine/AbstractFormatEngine.cs | 18 +++++++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs index 23a9bfe717622..f8b81391c6405 100644 --- a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs +++ b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting internal static class OmniSharpFormatter { public static Task FormatAsync(Document document, IEnumerable? spans, OmniSharpSyntaxFormattingOptionsWrapper options, CancellationToken cancellationToken) - => Formatter.FormatAsync(document, spans, options.CleanupOptions.FormattingOptions, rules: null, cancellationToken); + => Formatter.FormatAsync(document, spans, options.CleanupOptions.FormattingOptions, rules: default, cancellationToken); public static async Task OrganizeImportsAsync(Document document, OmniSharpOrganizeImportsOptionsWrapper options, CancellationToken cancellationToken) { diff --git a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs index 84c250d2915d1..6f738bcc3613f 100644 --- a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs +++ b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs @@ -72,7 +72,7 @@ private protected async Task AssertFormatAsync( internal void AssertFormatWithTransformation( SolutionServices services, string expected, SyntaxNode root, IEnumerable spans, SyntaxFormattingOptions options, bool treeCompare = true, ParseOptions? parseOptions = null) { - var newRootNode = Formatter.Format(root, spans, services, options, rules: null, CancellationToken.None); + var newRootNode = Formatter.Format(root, spans, services, options, rules: default, CancellationToken.None); Assert.Equal(expected, newRootNode.ToFullString()); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index d9e2032fb1408..bf75821fe11f2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -51,8 +51,11 @@ internal abstract partial class AbstractFormatEngine /// options. To help with that, cache the last set of ChainedFormattingRules that was produced, as it is not a cheap /// type to create. /// - private static readonly object s_gate = new(); - private static (ImmutableArray formattingRules, SyntaxFormattingOptions options, ChainedFormattingRules chainedRules) s_lastRulesAndOptions; + /// + /// Stored as a instead of a so we don't have + /// to worry about torn write concerns. + /// + private static Tuple, SyntaxFormattingOptions, ChainedFormattingRules>? s_lastRulesAndOptions; public AbstractFormatEngine( TreeData treeData, @@ -71,13 +74,14 @@ public AbstractFormatEngine( private static ChainedFormattingRules GetChainedFormattingRules(ImmutableArray formattingRules, SyntaxFormattingOptions options) { - lock (s_gate) + var lastRulesAndOptions = s_lastRulesAndOptions; + if (formattingRules != lastRulesAndOptions?.Item1 || options != s_lastRulesAndOptions?.Item2) { - if (formattingRules != s_lastRulesAndOptions.formattingRules || options != s_lastRulesAndOptions.options) - s_lastRulesAndOptions = (formattingRules, options, new ChainedFormattingRules(formattingRules, options)); - - return s_lastRulesAndOptions.chainedRules; + lastRulesAndOptions = Tuple.Create(formattingRules, options, new ChainedFormattingRules(formattingRules, options)); + s_lastRulesAndOptions = lastRulesAndOptions; } + + return lastRulesAndOptions.Item3; } internal AbstractFormatEngine( From 773d22235f8e7788f33b3ac5c08a440308d62cfa Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 16:23:32 -0700 Subject: [PATCH 302/423] Fix --- .../TestUtilities/Formatting/CoreFormatterTestsBase.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index ab0b2ed0739c0..44cc65b75a962 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -204,14 +204,14 @@ private protected async Task AssertFormatAsync(string expected, string code, IEn ? formattingService.GetFormattingOptions(options, fallbackOptions: null) : formattingService.DefaultOptions; - var rules = formattingRuleProvider.CreateRule(documentSyntax, 0).Concat(Formatter.GetDefaultFormattingRules(document)); + ImmutableArray rules = [formattingRuleProvider.CreateRule(documentSyntax, 0), .. Formatter.GetDefaultFormattingRules(document)]; AssertFormat(workspace, expected, formattingOptions, rules, clonedBuffer, documentSyntax.Root, spans); // format with node and transform AssertFormatWithTransformation(workspace, expected, formattingOptions, rules, documentSyntax.Root, spans); } - internal void AssertFormatWithTransformation(Workspace workspace, string expected, SyntaxFormattingOptions options, IEnumerable rules, SyntaxNode root, IEnumerable spans) + internal void AssertFormatWithTransformation(Workspace workspace, string expected, SyntaxFormattingOptions options, ImmutableArray rules, SyntaxNode root, IEnumerable spans) { var newRootNode = Formatter.Format(root, spans, workspace.Services.SolutionServices, options, rules, CancellationToken.None); @@ -224,7 +224,7 @@ internal void AssertFormatWithTransformation(Workspace workspace, string expecte Assert.True(newRootNodeFromString.IsEquivalentTo(newRootNode)); } - internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattingOptions options, IEnumerable rules, ITextBuffer clonedBuffer, SyntaxNode root, IEnumerable spans) + internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattingOptions options, ImmutableArray rules, ITextBuffer clonedBuffer, SyntaxNode root, IEnumerable spans) { var result = Formatter.GetFormattedTextChanges(root, spans, workspace.Services.SolutionServices, options, rules, CancellationToken.None); var actual = ApplyResultAndGetFormattedText(clonedBuffer, result); From f087e25a8fa34072e8631d2de9a8f67d38d33b4f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 16:23:58 -0700 Subject: [PATCH 303/423] Fix --- .../CSharpTest/Formatting/FormattingEngineTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index ef5d1071b242c..2ee4eedd0421f 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -441,7 +441,7 @@ public void M() var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); var syntaxRoot = await document.GetRequiredSyntaxRootAsync(CancellationToken.None); var options = CSharpSyntaxFormattingOptions.Default; - var node = Formatter.Format(syntaxRoot, spans, workspace.Services.SolutionServices, options, rules: null, CancellationToken.None); + var node = Formatter.Format(syntaxRoot, spans, workspace.Services.SolutionServices, options, rules: default, CancellationToken.None); Assert.Equal(expected, node.ToFullString()); } From 078225928549cfbded9761c6916f720048b32178 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 17:35:17 -0700 Subject: [PATCH 304/423] Fix lint --- .../VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb index 3b9aceb35410f..be91476713e7e 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb @@ -2,6 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.UnitTests @@ -66,7 +67,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting workspace.Documents.First(Function(d) d.SelectedSpans.Any()).SelectedSpans, workspace.Services.SolutionServices, options, - rules, + rules.ToImmutableArray(), CancellationToken.None) AssertResult(expected, clonedBuffer, changes) From 60cc9719c45aa3907d75fccf00f24bfff76a67a5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 20:49:51 -0700 Subject: [PATCH 305/423] Properly dispose the SymbolSearchEngine --- .../SymbolSearchUpdateNoOpEngine.cs | 5 ++++ .../Windows/SymbolSearchUpdateEngine.cs | 5 ++++ .../VisualStudioSymbolSearchService.cs | 25 +++++++++++++------ .../SymbolSearch/ISymbolSearchUpdateEngine.cs | 3 ++- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs b/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs index 1646c549e47cb..ea2a0b7184685 100644 --- a/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs +++ b/src/Features/Core/Portable/SymbolSearch/SymbolSearchUpdateNoOpEngine.cs @@ -13,6 +13,11 @@ internal sealed class SymbolSearchUpdateNoOpEngine : ISymbolSearchUpdateEngine { public static readonly SymbolSearchUpdateNoOpEngine Instance = new(); + public void Dispose() + { + // Nothing to do for the no-op version. + } + public ValueTask> FindPackagesWithAssemblyAsync(string source, string assemblyName, CancellationToken cancellationToken) => ValueTaskFactory.FromResult(ImmutableArray.Empty); diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs index d827000bfa327..e5db69773fdd4 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.cs @@ -65,6 +65,11 @@ internal SymbolSearchUpdateEngine( _reportAndSwallowExceptionUnlessCanceled = reportAndSwallowExceptionUnlessCanceled; } + public void Dispose() + { + // Nothing to do for the core symbol search engine. + } + public ValueTask> FindPackagesWithTypeAsync( string source, string name, int arity, CancellationToken cancellationToken) { diff --git a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs index a658f1d2e748b..be065a81685d3 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.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.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,7 +22,6 @@ using Microsoft.VisualStudio.LanguageServices.Storage; using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Settings; using Roslyn.Utilities; using VSShell = Microsoft.VisualStudio.Shell; @@ -40,18 +36,18 @@ namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch; /// date by downloading patches on a daily basis. /// [ExportWorkspaceService(typeof(ISymbolSearchService), ServiceLayer.Host), Shared] -internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedService, ISymbolSearchService +internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedService, ISymbolSearchService, IDisposable { private readonly SemaphoreSlim _gate = new(initialCount: 1); // Note: A remote engine is disposable as it maintains a connection with ServiceHub, // but we want to keep it alive until the VS is closed, so we don't dispose it. - private ISymbolSearchUpdateEngine _lazyUpdateEngine; + private ISymbolSearchUpdateEngine? _lazyUpdateEngine; private readonly SVsServiceProvider _serviceProvider; private readonly IPackageInstallerService _installerService; - private string _localSettingsDirectory; + private string? _localSettingsDirectory; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -69,7 +65,20 @@ public VisualStudioSymbolSearchService( [SymbolSearchOptionsStorage.SearchReferenceAssemblies, SymbolSearchOptionsStorage.SearchNuGetPackages]) { _serviceProvider = serviceProvider; - _installerService = workspace.Services.GetService(); + _installerService = workspace.Services.GetRequiredService(); + } + + public void Dispose() + { + ISymbolSearchUpdateEngine? updateEngine; + using (_gate.DisposableWait()) + { + // Once we're disposed, swap out our engine with a no-op one so we don't try to do any more work. + updateEngine = _lazyUpdateEngine; + _lazyUpdateEngine = SymbolSearchUpdateNoOpEngine.Instance; + } + + updateEngine?.Dispose(); } protected override async Task EnableServiceAsync(CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs index 33a083cc0f12f..2c5f5529dff5d 100644 --- a/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs +++ b/src/Workspaces/Core/Portable/SymbolSearch/ISymbolSearchUpdateEngine.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -14,7 +15,7 @@ namespace Microsoft.CodeAnalysis.SymbolSearch; /// Service that allows you to query the SymbolSearch database and which keeps /// the database up to date. /// -internal interface ISymbolSearchUpdateEngine +internal interface ISymbolSearchUpdateEngine : IDisposable { ValueTask UpdateContinuouslyAsync(string sourceName, string localSettingsDirectory, CancellationToken cancellationToken); From 3738d09c1f6766cf324a9259ab8bb998f6b78d55 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 13 May 2024 21:39:21 -0700 Subject: [PATCH 306/423] Remove unused parts of RemoteHostClient --- .../Core/Portable/Remote/RemoteHostClient.cs | 13 +------------ .../Remote/InProcRemostHostClient.cs | 4 ---- .../Remote/Core/ServiceHubRemoteHostClient.cs | 3 --- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs index 4f8bb3b9ddc17..fc5b311d973ef 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteHostClient.cs @@ -20,18 +20,7 @@ namespace Microsoft.CodeAnalysis.Remote; /// internal abstract class RemoteHostClient : IDisposable { - public event EventHandler? StatusChanged; - - protected void Started() - { - OnStatusChanged(started: true); - } - - public virtual void Dispose() - => OnStatusChanged(started: false); - - private void OnStatusChanged(bool started) - => StatusChanged?.Invoke(this, started); + public abstract void Dispose(); public static Task TryGetClientAsync(Project project, CancellationToken cancellationToken) { diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index 9119e54bdab19..83cc1ba8d7589 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -29,8 +29,6 @@ public static RemoteHostClient Create(SolutionServices services, RemoteServiceCa var inprocServices = new InProcRemoteServices(services, traceListener, testData); var instance = new InProcRemoteHostClient(services, inprocServices, callbackDispatchers); - instance.Started(); - // return instance return instance; } @@ -76,8 +74,6 @@ public override RemoteServiceConnection CreateConnection(object? callbackT public override void Dispose() { _inprocServices.Dispose(); - - base.Dispose(); } public sealed class ServiceProvider : IServiceProvider diff --git a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs index 003a7f9ac5e8d..8060625ad45c5 100644 --- a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs +++ b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs @@ -105,7 +105,6 @@ await client.TryInvokeAsync( (service, cancellationToken) => service.EnableAsync(AsynchronousOperationListenerProvider.IsEnabled, listenerProvider.DiagnosticTokensEnabled, cancellationToken), cancellationToken).ConfigureAwait(false); - client.Started(); return client; } } @@ -141,8 +140,6 @@ public override void Dispose() _hubClient.Dispose(); _serviceBrokerClient.Dispose(); - - base.Dispose(); } } } From cc82c057576e78b75cc281334b4ed3888fa9e75d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 14 May 2024 08:50:59 +0200 Subject: [PATCH 307/423] Test local functions with goto out of scope (#73402) --- .../CSharp/Test/Emit/CodeGen/GotoTest.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs index 8aa5ab4c25073..bd68eefaa1e3e 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/GotoTest.cs @@ -606,6 +606,59 @@ public void GotoInLambda_NonExistent() Diagnostic(ErrorCode.ERR_LabelNotFound, "x").WithArguments("x").WithLocation(4, 10)); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73397")] + public void GotoInLocalFunc_OutOfScope_Backward() + { + var code = """ + #pragma warning disable CS8321 // local function unused + x: + void localFunc() + { + using System.IDisposable d = null; + goto x; + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (6,5): error CS0159: No such label 'x' within the scope of the goto statement + // goto x; + Diagnostic(ErrorCode.ERR_LabelNotFound, "goto").WithArguments("x").WithLocation(6, 5)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73397")] + public void GotoInLocalFunc_OutOfScope_Forward() + { + var code = """ + #pragma warning disable CS8321 // local function unused + void localFunc() + { + using System.IDisposable d = null; + goto x; + } + x:; + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (5,5): error CS0159: No such label 'x' within the scope of the goto statement + // goto x; + Diagnostic(ErrorCode.ERR_LabelNotFound, "goto").WithArguments("x").WithLocation(5, 5)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73397")] + public void GotoInLocalFunc_NonExistent() + { + var code = """ + #pragma warning disable CS8321 // local function unused + void localFunc() + { + using System.IDisposable d = null; + goto x; + } + """; + CreateCompilation(code).VerifyEmitDiagnostics( + // (5,10): error CS0159: No such label 'x' within the scope of the goto statement + // goto x; + Diagnostic(ErrorCode.ERR_LabelNotFound, "x").WithArguments("x").WithLocation(5, 10)); + } + // Definition same label in different lambdas [WorkItem(5991, "DevDiv_Projects/Roslyn")] [Fact] From 0396154ea7010211d8bf65c168b9cf57ec8a5e55 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 14 May 2024 08:51:12 +0200 Subject: [PATCH 308/423] Add "First-class Span Types" feature status (#73446) --- docs/Language Feature Status.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 47fb6533a7218..43ae84c0cc63f 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -10,6 +10,7 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | +| [First-class Span Types](https://github.com/dotnet/csharplang/issues/7905) | [FirstClassSpan](https://github.com/dotnet/roslyn/tree/features/FirstClassSpan) | [In Progress](https://github.com/dotnet/roslyn/issues/73445) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [333fred](https://github.com/333fred) | | [333fred](https://github.com/333fred), [stephentoub](https://github.com/stephentoub) | | [Partial properties](https://github.com/dotnet/csharplang/issues/6420) | [partial-properties](https://github.com/dotnet/roslyn/tree/features/partial-properties) | [In Progress](https://github.com/dotnet/roslyn/issues/73090) | [RikkiGibson](https://github.com/RikkiGibson) | [jcouv](https://github.com/jcouv), [333fred](https://github.com/333fred) | [Cosifne](https://github.com/Cosifne) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | | Ref/unsafe in iterators/async | [RefInAsync](https://github.com/dotnet/roslyn/tree/features/RefInAsync) | [In Progress](https://github.com/dotnet/roslyn/issues/72662) | [jjonescz](https://github.com/jjonescz) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | (no IDE impact) | | | [Ref Struct Interfaces](https://github.com/dotnet/csharplang/issues/7608) | [RefStructInterfaces](https://github.com/dotnet/roslyn/tree/features/RefStructInterfaces) | [In Progress](https://github.com/dotnet/roslyn/issues/72124) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | [ToddGrun](https://github.com/ToddGrun) | [agocke](https://github.com/agocke), [jaredpar](https://github.com/jaredpar) | From 874a204d1854b0fea28fd9dbe730ca05d7417684 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 14 May 2024 17:43:55 +0200 Subject: [PATCH 309/423] Stop overbuilding SemanticSearch.ReferenceAssemblies (#73468) * Stop overbuilding SemanticSearch.ReferenceAssemblies Fixes https://github.com/dotnet/source-build/issues/4390 * Update Roslyn.VisualStudio.Setup.csproj * Update Roslyn.VisualStudio.Setup.csproj --- src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index 7b49bf81a0c9a..6b93eb53390b8 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -307,11 +307,8 @@ SemanticSearchRefs - - TargetFramework=$(NetRoslyn) true false - false SemanticSearchRefs PublishProjectOutputGroup From c1e2051396d77134f1d1c81710597759a4d2c21e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:20:50 -0700 Subject: [PATCH 310/423] Switch to cleaner patterns than explicit Task.Run --- .../Core/GoToDefinition/AbstractGoToCommandHandler`2.cs | 8 +++++--- .../Features/Diagnostics/DiagnosticAnalyzerService.cs | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs index b1f8f6233b662..8c0b23e04ede9 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs @@ -27,7 +27,6 @@ using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GoToDefinition; @@ -106,7 +105,7 @@ public bool ExecuteCommand(TCommandArgs args, CommandExecutionContext context) if (service == null) return false; - Contract.ThrowIfNull(document); + Roslyn.Utilities.Contract.ThrowIfNull(document); // cancel any prior find-refs that might be in progress. _cancellationTokenSource.Cancel(); @@ -173,7 +172,7 @@ private async Task ExecuteCommandWorkerAsync( var cancellationToken = cancellationTokenSource.Token; var delayTask = DelayAsync(cancellationToken); - var findTask = Task.Run(() => FindResultsAsync(findContext, document, position, cancellationToken), cancellationToken); + var findTask = FindResultsAsync(findContext, document, position, cancellationToken); var firstFinishedTask = await Task.WhenAny(delayTask, findTask).ConfigureAwait(false); if (cancellationToken.IsCancellationRequested) @@ -253,6 +252,9 @@ private async Task PresentResultsInStreamingPresenterAsync( private async Task FindResultsAsync( IFindUsagesContext findContext, Document document, int position, CancellationToken cancellationToken) { + // Ensure that we relinquish the thread so that the caller can proceed with their work. + await Task.Yield().ConfigureAwait(false); + using (Logger.LogBlock(FunctionId, KeyValueLogMessage.Create(LogType.UserAction), cancellationToken)) { await findContext.SetSearchTitleAsync(DisplayName, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index be32623daf749..5c5c2c24e181e 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics @@ -94,7 +95,7 @@ public void RequestDiagnosticRefresh() }, cancellationToken); } - public Task> GetDiagnosticsForSpanAsync( + public async Task> GetDiagnosticsForSpanAsync( TextDocument document, TextSpan? range, Func? shouldIncludeDiagnostic, @@ -110,9 +111,10 @@ public Task> GetDiagnosticsForSpanAsync( priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); // always make sure that analyzer is called on background thread. - return Task.Run(() => analyzer.GetDiagnosticsForSpanAsync( + await TaskScheduler.Default; + return await analyzer.GetDiagnosticsForSpanAsync( document, range, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics, - priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken), cancellationToken); + priorityProvider, blockForData: true, addOperationScope, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); } public Task> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) From 97798edd82c094098f53892996ad4f569032c622 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:22:33 -0700 Subject: [PATCH 311/423] Switch to cleaner patterns than explicit Task.Run --- .../SuggestedActions/SuggestedAction.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs index 799e31259d1c3..4e07cb0701ed5 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.cs @@ -25,6 +25,7 @@ using Microsoft.VisualStudio.Imaging.Interop; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -74,25 +75,27 @@ public virtual bool TryGetTelemetryId(out Guid telemetryId) return true; } - // NOTE: We want to avoid computing the operations on the UI thread. So we use Task.Run() to do this work on the background thread. - protected Task> GetOperationsAsync( + protected async Task> GetOperationsAsync( IProgress progressTracker, CancellationToken cancellationToken) { - return Task.Run( - () => CodeAction.GetOperationsAsync(this.OriginalSolution, progressTracker, cancellationToken), cancellationToken); + // Avoid computing the operations on the UI thread + await TaskScheduler.Default; + return await CodeAction.GetOperationsAsync(this.OriginalSolution, progressTracker, cancellationToken).ConfigureAwait(false); } - protected Task> GetOperationsAsync( + protected async Task> GetOperationsAsync( CodeActionWithOptions actionWithOptions, object options, IProgress progressTracker, CancellationToken cancellationToken) { - return Task.Run( - () => actionWithOptions.GetOperationsAsync(this.OriginalSolution, options, progressTracker, cancellationToken), cancellationToken); + // Avoid computing the operations on the UI thread + await TaskScheduler.Default; + return await actionWithOptions.GetOperationsAsync(this.OriginalSolution, options, progressTracker, cancellationToken).ConfigureAwait(false); } - protected Task> GetPreviewOperationsAsync(CancellationToken cancellationToken) + protected async Task> GetPreviewOperationsAsync(CancellationToken cancellationToken) { - return Task.Run( - () => CodeAction.GetPreviewOperationsAsync(this.OriginalSolution, cancellationToken), cancellationToken); + // Avoid computing the operations on the UI thread + await TaskScheduler.Default; + return await CodeAction.GetPreviewOperationsAsync(this.OriginalSolution, cancellationToken).ConfigureAwait(false); } public void Invoke(CancellationToken cancellationToken) From b35cea5446294076c4dd8b5e18a12cb423c1154a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:31:51 -0700 Subject: [PATCH 312/423] Switch to producer/consumer --- ...odImportCompletionHelper.SymbolComputer.cs | 99 +++++++++---------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs index 979bed9aade96..0c26cb606acb0 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; @@ -87,43 +88,40 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke try { // Find applicable symbols in parallel - using var _1 = ArrayBuilder?>>.GetInstance(out var tasks); + var peReferenceMethodSymbolsTask = ProducerConsumer.RunParallelAsync( + source: GetAllRelevantPeReferences(_originatingDocument.Project), + produceItems: (peReference, callback, args, cancellationToken) => + args.@this.GetExtensionMethodSymbolsFromPeReferenceAsync(peReference, callback, args.forceCacheCreation, cancellationToken), + args: (@this: this, forceCacheCreation), + cancellationToken); + + var projectMethodSymbolsTask = ProducerConsumer.RunParallelAsync( + source: GetAllRelevantProjects(_originatingDocument.Project), + produceItems: (project, callback, args, cancellationToken) => + args.@this.GetExtensionMethodSymbolsFromProjectAsync(project, callback, args.forceCacheCreation, cancellationToken), + args: (@this: this, forceCacheCreation), + cancellationToken); + + var results = await Task.WhenAll(peReferenceMethodSymbolsTask, projectMethodSymbolsTask).ConfigureAwait(false); - foreach (var peReference in GetAllRelevantPeReferences(_originatingDocument.Project)) - { - tasks.Add(Task.Run(() => GetExtensionMethodSymbolsFromPeReferenceAsync( - peReference, - forceCacheCreation, - cancellationToken).AsTask(), cancellationToken)); - } - - foreach (var project in GetAllRelevantProjects(_originatingDocument.Project)) - { - tasks.Add(Task.Run(() => GetExtensionMethodSymbolsFromProjectAsync( - project, - forceCacheCreation, - cancellationToken), cancellationToken)); - } - - using var _2 = ArrayBuilder.GetInstance(out var symbols); var isPartialResult = false; - var results = await Task.WhenAll(tasks).ConfigureAwait(false); - + using var _ = ArrayBuilder.GetInstance(results[0].Length + results[1].Length, out var symbols); foreach (var result in results) { // `null` indicates we don't have the index ready for the corresponding project/PE. // returns what we have even it means we only show partial results. - if (result == null) + if (result.Any(static s => s is null)) { isPartialResult = true; continue; } - symbols.AddRange(result); + symbols.AddRange(result!); } - var browsableSymbols = symbols.ToImmutable() + var browsableSymbols = symbols + .ToImmutable() .FilterToVisibleAndBrowsableSymbols(hideAdvancedMembers, _originatingSemanticModel.Compilation); return (browsableSymbols, isPartialResult); @@ -148,8 +146,9 @@ private static ImmutableArray GetAllRelevantProjects(Project project) private static ImmutableArray GetAllRelevantPeReferences(Project project) => project.MetadataReferences.OfType().ToImmutableArray(); - private async Task?> GetExtensionMethodSymbolsFromProjectAsync( + private async Task GetExtensionMethodSymbolsFromProjectAsync( Project project, + Action callback, bool forceCacheCreation, CancellationToken cancellationToken) { @@ -161,13 +160,12 @@ private static ImmutableArray GetAllRelevantPeRefer else if (!_cacheService.ProjectItemsCache.TryGetValue(project.Id, out cacheEntry)) { // Use cached data if available, even checksum doesn't match. otherwise, returns null indicating cache not ready. - return null; + callback(null); + return; } if (!cacheEntry.ContainsExtensionMethod) - { - return ImmutableArray.Empty; - } + return; var originatingAssembly = _originatingSemanticModel.Compilation.Assembly; var filter = CreateAggregatedFilter(cacheEntry); @@ -183,13 +181,19 @@ private static ImmutableArray GetAllRelevantPeRefer var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly( compilation.Assembly, filter, internalsVisible, cancellationToken); - return project == _originatingDocument.Project - ? GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken) - : GetExtensionMethodsForSymbolsFromDifferentCompilation(matchingMethodSymbols, cancellationToken); + if (project == _originatingDocument.Project) + { + GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, callback, cancellationToken); + } + else + { + GetExtensionMethodsForSymbolsFromDifferentCompilation(matchingMethodSymbols, callback, cancellationToken); + } } - private async ValueTask?> GetExtensionMethodSymbolsFromPeReferenceAsync( + private async Task GetExtensionMethodSymbolsFromPeReferenceAsync( PortableExecutableReference peReference, + Action callback, bool forceCacheCreation, CancellationToken cancellationToken) { @@ -210,7 +214,8 @@ private static ImmutableArray GetAllRelevantPeRefer else { // No cached data immediately available, returns null to indicate index not ready - return null; + callback(null); + return; } } @@ -218,7 +223,7 @@ private static ImmutableArray GetAllRelevantPeRefer !symbolInfo.ContainsExtensionMethod || _originatingSemanticModel.Compilation.GetAssemblyOrModuleSymbol(peReference) is not IAssemblySymbol assembly) { - return ImmutableArray.Empty; + return; } var filter = CreateAggregatedFilter(symbolInfo); @@ -226,15 +231,14 @@ private static ImmutableArray GetAllRelevantPeRefer var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly(assembly, filter, internalsVisible, cancellationToken); - return GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken); + GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, callback, cancellationToken); } - private ImmutableArray GetExtensionMethodsForSymbolsFromDifferentCompilation( + private void GetExtensionMethodsForSymbolsFromDifferentCompilation( MultiDictionary matchingMethodSymbols, + Action callback, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var builder); - // Matching extension method symbols are grouped based on their receiver type. foreach (var (declaredReceiverType, methodSymbols) in matchingMethodSymbols) { @@ -292,21 +296,16 @@ private ImmutableArray GetExtensionMethodsForSymbolsFromDifferent } if (_originatingSemanticModel.IsAccessible(_position, methodInOriginatingCompilation)) - { - builder.Add(methodInOriginatingCompilation); - } + callback(methodInOriginatingCompilation); } } - - return builder.ToImmutableAndClear(); } - private ImmutableArray GetExtensionMethodsForSymbolsFromSameCompilation( + private void GetExtensionMethodsForSymbolsFromSameCompilation( MultiDictionary matchingMethodSymbols, + Action callback, CancellationToken cancellationToken) { - using var _ = ArrayBuilder.GetInstance(out var builder); - // Matching extension method symbols are grouped based on their receiver type. foreach (var (receiverType, methodSymbols) in matchingMethodSymbols) { @@ -315,9 +314,7 @@ private ImmutableArray GetExtensionMethodsForSymbolsFromSameCompi // If we already checked an extension method with same receiver type before, and we know it can't be applied // to the receiverTypeSymbol, then no need to proceed further. if (_checkedReceiverTypes.TryGetValue(receiverType, out var cachedResult) && !cachedResult) - { continue; - } // We haven't seen this type yet. Try to check by reducing one extension method // to the given receiver type and save the result. @@ -335,14 +332,10 @@ private ImmutableArray GetExtensionMethodsForSymbolsFromSameCompi foreach (var methodSymbol in methodSymbols) { if (_originatingSemanticModel.IsAccessible(_position, methodSymbol)) - { - builder.Add(methodSymbol); - } + callback(methodSymbol); } } } - - return builder.ToImmutableAndClear(); } private MultiDictionary GetPotentialMatchingSymbolsFromAssembly( From f6ac61179e226ed62fd514aacd0ec686dc95d285 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:33:15 -0700 Subject: [PATCH 313/423] Cleanup --- ...ExtensionMethodImportCompletionHelper.SymbolComputer.cs | 7 ++++--- .../Core/Portable/Shared/Extensions/ISymbolExtensions.cs | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs index 0c26cb606acb0..1b747f2a81b5e 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs @@ -107,17 +107,18 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke var isPartialResult = false; using var _ = ArrayBuilder.GetInstance(results[0].Length + results[1].Length, out var symbols); - foreach (var result in results) + foreach (var methodArray in results) { // `null` indicates we don't have the index ready for the corresponding project/PE. // returns what we have even it means we only show partial results. - if (result.Any(static s => s is null)) + if (methodArray is [null]) { isPartialResult = true; continue; } - symbols.AddRange(result!); + foreach (var method in methodArray) + symbols.AddIfNotNull(method); } var browsableSymbols = symbols diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index aba239ec1cb92..f9ae9b7f0b3fd 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -658,11 +658,10 @@ public static ImmutableArray FilterToVisibleAndBrowsableSymbols( // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first // check to see if we're referencing a symbol defined in source. - static bool isSymbolDefinedInSource(Location l) => l.IsInSource; return symbols.WhereAsArray((s, arg) => // Check if symbol is namespace (which is always visible) first to avoid realizing all locations // of each namespace symbol, which might end up allocating in LOH - (s.IsNamespace() || s.Locations.Any(isSymbolDefinedInSource) || !s.HasUnsupportedMetadata) && + (s.IsNamespace() || s.Locations.Any(static loc => loc.IsInSource) || !s.HasUnsupportedMetadata) && !s.IsDestructor() && s.IsEditorBrowsable( arg.hideAdvancedMembers, From 9d4f015c5260a515d54211118c06e2e5e1bdeeba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:35:37 -0700 Subject: [PATCH 314/423] Switch to cleaner patterns than explicit Task.Run --- .../Protocol/Features/CodeFixes/CodeFixService.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index 52f031d5db3a5..3ea1ac7bbe425 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -141,8 +141,8 @@ public async Task GetMostSevereFixAsync( } } - var errorFixTask = Task.Run(() => GetFirstFixAsync(spanToErrorDiagnostics, cancellationToken), cancellationToken); - var otherFixTask = Task.Run(() => GetFirstFixAsync(spanToOtherDiagnostics, linkedToken), linkedToken); + var errorFixTask = GetFirstFixAsync(spanToErrorDiagnostics, cancellationToken); + var otherFixTask = GetFirstFixAsync(spanToOtherDiagnostics, linkedToken); // If the error diagnostics task happens to complete with a non-null result before // the other diagnostics task, we can cancel the other task. @@ -156,6 +156,9 @@ public async Task GetMostSevereFixAsync( SortedDictionary> spanToDiagnostics, CancellationToken cancellationToken) { + // Ensure we yield here so the caller can continue on. + await AwaitExtensions.ConfigureAwait(Task.Yield(), false); + await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, priorityProvider, fallbackOptions, _ => null, cancellationToken).ConfigureAwait(false)) From 1313b364797c4146d451b3c3ba653f8a066cfff5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:36:12 -0700 Subject: [PATCH 315/423] Switch to cleaner patterns than explicit Task.Run --- .../Diagnostics/DiagnosticAnalyzerService.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs index 5c5c2c24e181e..b7841f2bc4d43 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/DiagnosticAnalyzerService.cs @@ -69,7 +69,7 @@ public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option) public void RequestDiagnosticRefresh() => _diagnosticsRefresher?.RequestWorkspaceRefresh(); - public Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( + public async Task<(ImmutableArray diagnostics, bool upToDate)> TryGetDiagnosticsForSpanAsync( TextDocument document, TextSpan range, Func? shouldIncludeDiagnostic, @@ -82,17 +82,15 @@ public void RequestDiagnosticRefresh() var analyzer = CreateIncrementalAnalyzer(document.Project.Solution.Workspace); // always make sure that analyzer is called on background thread. - return Task.Run(async () => - { - priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); - - using var _ = ArrayBuilder.GetInstance(out var diagnostics); - var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( - document, range, diagnostics, shouldIncludeDiagnostic, - includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, - addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); - return (diagnostics.ToImmutable(), upToDate); - }, cancellationToken); + await TaskScheduler.Default; + priorityProvider ??= new DefaultCodeActionRequestPriorityProvider(); + + using var _ = ArrayBuilder.GetInstance(out var diagnostics); + var upToDate = await analyzer.TryAppendDiagnosticsForSpanAsync( + document, range, diagnostics, shouldIncludeDiagnostic, + includeSuppressedDiagnostics, true, priorityProvider, blockForData: false, + addOperationScope: null, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false); + return (diagnostics.ToImmutable(), upToDate); } public async Task> GetDiagnosticsForSpanAsync( From db635f37452aaaa3a41ee9f6719a02289275452e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:36:53 -0700 Subject: [PATCH 316/423] Switch to cleaner patterns than explicit Task.Run --- .../UnifiedSuggestions/UnifiedSuggestedActionsSource.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index cf1b8f407a589..4f2fc704e0e9d 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; using CodeFixGroupKey = System.Tuple; @@ -45,13 +46,14 @@ public static async ValueTask> GetFilt // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally // run any of this on the UI thread and potentially allow any code to take a dependency on that. - var fixes = await Task.Run(() => codeFixService.GetFixesAsync( + await TaskScheduler.Default; + var fixes = await codeFixService.GetFixesAsync( document, selection, priorityProvider, fallbackOptions, addOperationScope, - cancellationToken), cancellationToken).ConfigureAwait(false); + cancellationToken).ConfigureAwait(false); var filteredFixes = fixes.WhereAsArray(c => c.Fixes.Length > 0); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); From 093fb969414659a7adea2eecab3cad387ebe2f58 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:37:46 -0700 Subject: [PATCH 317/423] Switch to cleaner patterns than explicit Task.Run --- .../UnifiedSuggestedActionsSource.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index 4f2fc704e0e9d..b37ddde7869e0 100644 --- a/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/LanguageServer/Protocol/Features/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -44,8 +44,8 @@ public static async ValueTask> GetFilt { var originalSolution = document.Project.Solution; - // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally - // run any of this on the UI thread and potentially allow any code to take a dependency on that. + // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally run any of + // this on the UI thread and potentially allow any code to take a dependency on that. await TaskScheduler.Default; var fixes = await codeFixService.GetFixesAsync( document, @@ -445,14 +445,12 @@ public static async Task> GetFilterAnd bool filterOutsideSelection, CancellationToken cancellationToken) { - // It may seem strange that we kick off a task, but then immediately 'Wait' on - // it. However, it's deliberate. We want to make sure that the code runs on - // the background so that no one takes an accidentally dependency on running on - // the UI thread. - var refactorings = await Task.Run( - () => codeRefactoringService.GetRefactoringsAsync( - document, selection, priority, options, addOperationScope, - cancellationToken), cancellationToken).ConfigureAwait(false); + // Intentionally switch to a threadpool thread to compute fixes. We do not want to accidentally run any of + // this on the UI thread and potentially allow any code to take a dependency on that. + await TaskScheduler.Default; + var refactorings = await codeRefactoringService.GetRefactoringsAsync( + document, selection, priority, options, addOperationScope, + cancellationToken).ConfigureAwait(false); var filteredRefactorings = FilterOnAnyThread(refactorings, selection, filterOutsideSelection); From adfac45281be0fea5305180c48746c122d8a1185 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:38:37 -0700 Subject: [PATCH 318/423] Switch to cleaner patterns than explicit Task.Run --- .../AbstractObjectBrowserLibraryManager.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs index 545a528028c67..83d9905b2b025 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Text; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor; @@ -21,6 +22,7 @@ using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; using IServiceProvider = System.IServiceProvider; using Task = System.Threading.Tasks.Task; @@ -519,11 +521,10 @@ private static async Task FindReferencesAsync( try { - // Kick off the work to do the actual finding on a BG thread. That way we don' - // t block the calling (UI) thread too long if we happen to do our work on this - // thread. - await Task.Run( - () => FindReferencesAsync(symbolListItem, project, context, classificationOptions, cancellationToken), cancellationToken).ConfigureAwait(false); + // Kick off the work to do the actual finding on a BG thread. That way we don' t block the calling (UI) + // thread too long if we happen to do our work on this thread. + await TaskScheduler.Default; + await FindReferencesAsync(symbolListItem, project, context, classificationOptions, cancellationToken).ConfigureAwait(false); } finally { From 25318ba905d4923df6f18c2387d026ca6b873d51 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:39:20 -0700 Subject: [PATCH 319/423] Switch to cleaner patterns than explicit Task.Run --- .../Core/Def/NavigateTo/RoslynSearchItemsSource.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs index c0d8b86af61c9..3d32fc0b23cf3 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.VisualStudio.Search.Data; +using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.NavigateTo; @@ -46,7 +47,7 @@ public override async Task PerformSearchAsync(ISearchQuery searchQuery, ISearchC var cancellationTriggeredTask = Task.Delay(-1, cancellationToken); // Now, kick off the actual search work concurrently with the waiting task. - var searchTask = Task.Run(() => PerformSearchWorkerAsync(searchQuery, searchCallback, cancellationToken), cancellationToken); + var searchTask = PerformSearchWorkerAsync(searchQuery, searchCallback, cancellationToken); // Now wait for either task to complete. This allows us to bail out of the call into us once the // cancellation token is signaled, even if search work is still happening. This is desirable as the @@ -65,6 +66,9 @@ private async Task PerformSearchWorkerAsync( ISearchCallback searchCallback, CancellationToken cancellationToken) { + // Ensure we yield immedaitely so our caller can proceed with other work. + await Task.Yield().ConfigureAwait(false); + var searchValue = searchQuery.QueryString.Trim(); if (string.IsNullOrWhiteSpace(searchValue)) return; From ae306f1178cb2f9c39292b561ad27eebd2042d77 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:47:11 -0700 Subject: [PATCH 320/423] Switch to producer/consumer --- ...odImportCompletionHelper.SymbolComputer.cs | 4 +- .../FixAllOccurrences/BatchFixAllProvider.cs | 47 +++++++------------ 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs index 1b747f2a81b5e..57d053dc697d6 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs @@ -90,14 +90,14 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke // Find applicable symbols in parallel var peReferenceMethodSymbolsTask = ProducerConsumer.RunParallelAsync( source: GetAllRelevantPeReferences(_originatingDocument.Project), - produceItems: (peReference, callback, args, cancellationToken) => + produceItems: static (peReference, callback, args, cancellationToken) => args.@this.GetExtensionMethodSymbolsFromPeReferenceAsync(peReference, callback, args.forceCacheCreation, cancellationToken), args: (@this: this, forceCacheCreation), cancellationToken); var projectMethodSymbolsTask = ProducerConsumer.RunParallelAsync( source: GetAllRelevantProjects(_originatingDocument.Project), - produceItems: (project, callback, args, cancellationToken) => + produceItems: static (project, callback, args, cancellationToken) => args.@this.GetExtensionMethodSymbolsFromProjectAsync(project, callback, args.forceCacheCreation, cancellationToken), args: (@this: this, forceCacheCreation), cancellationToken); diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs index c5fccb6afbcfb..daffdbf5e850f 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs @@ -139,22 +139,23 @@ private static async Task AddDocumentChangesAsync( private static async Task> GetAllChangedDocumentsInDiagnosticsOrderAsync( FixAllContext fixAllContext, ImmutableArray orderedDiagnostics) { - var solution = fixAllContext.Solution; var cancellationToken = fixAllContext.CancellationToken; - // Process each diagnostic, determine the code actions to fix it, then figure out the document changes - // produced by that code action. - using var _1 = ArrayBuilder>>.GetInstance(out var tasks); - foreach (var diagnostic in orderedDiagnostics) - { - var document = solution.GetRequiredDocument(diagnostic.Location.SourceTree!); - - cancellationToken.ThrowIfCancellationRequested(); - tasks.Add(Task.Run(async () => + // Process each diagnostic, determine the code actions to fix it, then figure out the document changes produced + // by that code action. + return await ProducerConsumer.RunParallelAsync( + source: orderedDiagnostics, + produceItems: static async (diagnostic, callback, args, cancellationToken) => { + var fixAllContext = args.fixAllContext; + var solution = fixAllContext.Solution; + + var document = solution.GetRequiredDocument(diagnostic.Location.SourceTree!); + // Create a context that will add the reported code actions into this - using var _2 = ArrayBuilder.GetInstance(out var codeActions); - var action = GetRegisterCodeFixAction(fixAllContext.CodeActionEquivalenceKey, codeActions); + using var _ = ArrayBuilder.GetInstance(out var codeActions); + + var action = GetRegisterCodeFixAction(args.fixAllContext.CodeActionEquivalenceKey, codeActions); var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, [diagnostic], action, fixAllContext.State.CodeActionOptionsProvider, cancellationToken); // Wait for the all the code actions to be reported for this diagnostic. @@ -162,7 +163,6 @@ private static async Task> GetAllChangedDocumentsInDiag await registerTask.ConfigureAwait(false); // Now, process each code action and find out all the document changes caused by it. - using var _3 = ArrayBuilder.GetInstance(out var changedDocuments); foreach (var codeAction in codeActions) { @@ -171,24 +171,13 @@ private static async Task> GetAllChangedDocumentsInDiag if (changedSolution != null) { var changedDocumentIds = new SolutionChanges(changedSolution, solution).GetProjectChanges().SelectMany(p => p.GetChangedDocuments()); - changedDocuments.AddRange(changedDocumentIds.Select(id => changedSolution.GetRequiredDocument(id))); + foreach (var changedDocumentId in changedDocumentIds) + callback(changedSolution.GetRequiredDocument(changedDocumentId)); } } - - return changedDocuments.ToImmutableAndClear(); - }, cancellationToken)); - } - - // Wait for all that work to finish. - await Task.WhenAll(tasks).ConfigureAwait(false); - - // Flatten the set of changed documents. These will naturally still be ordered by the diagnostic that - // caused the change. - using var _4 = ArrayBuilder.GetInstance(out var result); - foreach (var task in tasks) - result.AddRange(await task.ConfigureAwait(false)); - - return result.ToImmutableAndClear(); + }, + args: (fixAllContext, true), + cancellationToken).ConfigureAwait(false); } /// From 50fd48a6e2601b37867ca1054da09bef0a350a79 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:51:31 -0700 Subject: [PATCH 321/423] move to roslynparallel --- .../AbstractSimplificationService.cs | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index 3a3bf94dfb4a5..9ea91e2805004 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -179,17 +180,13 @@ private async Task ReduceAsync( ConcurrentDictionary reducedTokensMap, CancellationToken cancellationToken) { - // Debug flag to help processing things serially instead of parallel. - var executeSerially = Debugger.IsAttached; - Contract.ThrowIfFalse(nodesAndTokensToReduce.Any()); // Reduce each node or token in the given list by running it through each reducer. - var simplifyTasks = new Task[nodesAndTokensToReduce.Length]; - for (var i = 0; i < nodesAndTokensToReduce.Length; i++) - { - var nodeOrTokenToReduce = nodesAndTokensToReduce[i]; - simplifyTasks[i] = Task.Run(async () => + await RoslynParallel.ForEachAsync( + source: nodesAndTokensToReduce, + cancellationToken, + async (nodeOrTokenToReduce, cancellationToken) => { var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; @@ -273,13 +270,7 @@ private async Task ReduceAsync( reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); } } - }, cancellationToken); - - if (executeSerially) - await simplifyTasks[i].ConfigureAwait(false); - } - - await Task.WhenAll(simplifyTasks).ConfigureAwait(false); + }).ConfigureAwait(false); } // find any namespace imports / using directives marked for simplification in the specified spans From 23c6a2d8dddb9dcdf523f056be94fad912901fea Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 10:52:10 -0700 Subject: [PATCH 322/423] Simplify --- ...pilationState.TranslationAction_Actions.cs | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index cb8e4c37c351b..7e06aec5997e0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -144,30 +145,11 @@ internal sealed class AddDocumentsAction( public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) { -#if NETSTANDARD - using var _1 = ArrayBuilder.GetInstance(this.Documents.Length, out var tasks); - - // We want to parse in parallel. But we don't want to have too many parses going on at the same time. - // So we use a semaphore here to only allow that many in at a time. Once we hit that amount, it will - // block further parallel work. However, as the semaphore is released, new work will be let in. - var semaphore = new SemaphoreSlim(initialCount: AddDocumentsBatchSize); - foreach (var document in this.Documents) - { - tasks.Add(Task.Run(async () => - { - using (await semaphore.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) - await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - }, cancellationToken)); - } - - await Task.WhenAll(tasks).ConfigureAwait(false); -#else - await Parallel.ForEachAsync( + await RoslynParallel.ForEachAsync( this.Documents, cancellationToken, static async (document, cancellationToken) => await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); -#endif using var _2 = ArrayBuilder.GetInstance(this.Documents.Length, out var trees); From 858a25d7585823c87157f5a925f197993e1d6483 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:03:21 -0700 Subject: [PATCH 323/423] Add back --- .../AbstractSimplificationService.cs | 163 ++++++++++-------- 1 file changed, 90 insertions(+), 73 deletions(-) diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index 9ea91e2805004..f852e4a0ef741 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -180,97 +180,114 @@ private async Task ReduceAsync( ConcurrentDictionary reducedTokensMap, CancellationToken cancellationToken) { + // Debug flag to help processing things serially instead of parallel. + var executeSerially = Debugger.IsAttached; + Contract.ThrowIfFalse(nodesAndTokensToReduce.Any()); - // Reduce each node or token in the given list by running it through each reducer. - await RoslynParallel.ForEachAsync( - source: nodesAndTokensToReduce, - cancellationToken, - async (nodeOrTokenToReduce, cancellationToken) => - { - var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; - var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; - var semanticModelForReduce = semanticModel; - var currentNodeOrToken = nodeOrTokenToReduce.NodeOrToken; - var isNode = nodeOrToken.IsNode; + if (executeSerially) + { + foreach (var nodeOrTokenToReduce in nodesAndTokensToReduce) + await ReduceOneNodeOrTokenAsync(nodeOrTokenToReduce, cancellationToken).ConfigureAwait(false); + } + else + { + await RoslynParallel.ForEachAsync( + source: nodesAndTokensToReduce, + cancellationToken, + ReduceOneNodeOrTokenAsync).ConfigureAwait(false); + } - foreach (var reducer in reducers) - { - cancellationToken.ThrowIfCancellationRequested(); + return; - using var rewriter = reducer.GetOrCreateRewriter(); - rewriter.Initialize(document.Project.ParseOptions, options, cancellationToken); + async ValueTask ReduceOneNodeOrTokenAsync( + NodeOrTokenToReduce nodeOrTokenToReduce, CancellationToken cancellationToken) + { + // Reduce each node or token in the given list by running it through each reducer. + + var nodeOrToken = nodeOrTokenToReduce.OriginalNodeOrToken; + var simplifyAllDescendants = nodeOrTokenToReduce.SimplifyAllDescendants; + var semanticModelForReduce = semanticModel; + var currentNodeOrToken = nodeOrTokenToReduce.NodeOrToken; + var isNode = nodeOrToken.IsNode; + + foreach (var reducer in reducers) + { + cancellationToken.ThrowIfCancellationRequested(); - do + using var rewriter = reducer.GetOrCreateRewriter(); + rewriter.Initialize(document.Project.ParseOptions, options, cancellationToken); + + do + { + if (currentNodeOrToken.SyntaxTree != semanticModelForReduce.SyntaxTree) { - if (currentNodeOrToken.SyntaxTree != semanticModelForReduce.SyntaxTree) + // currentNodeOrToken was simplified either by a previous reducer or + // a previous iteration of the current reducer. + // Create a speculative semantic model for the simplified node for semantic queries. + + // Certain node kinds (expressions/statements) require non-null parent nodes during simplification. + // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. + if (currentNodeOrToken.Parent == null && + nodeOrToken.Parent != null && + (currentNodeOrToken.IsToken || + currentNodeOrToken.AsNode() is TExpressionSyntax || + currentNodeOrToken.AsNode() is TStatementSyntax || + currentNodeOrToken.AsNode() is TCrefSyntax)) { - // currentNodeOrToken was simplified either by a previous reducer or - // a previous iteration of the current reducer. - // Create a speculative semantic model for the simplified node for semantic queries. - - // Certain node kinds (expressions/statements) require non-null parent nodes during simplification. - // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. - if (currentNodeOrToken.Parent == null && - nodeOrToken.Parent != null && - (currentNodeOrToken.IsToken || - currentNodeOrToken.AsNode() is TExpressionSyntax || - currentNodeOrToken.AsNode() is TStatementSyntax || - currentNodeOrToken.AsNode() is TCrefSyntax)) - { - var annotation = new SyntaxAnnotation(); - currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); + var annotation = new SyntaxAnnotation(); + currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); - var replacedParent = isNode - ? nodeOrToken.Parent.ReplaceNode(nodeOrToken.AsNode()!, currentNodeOrToken.AsNode()!) - : nodeOrToken.Parent.ReplaceToken(nodeOrToken.AsToken(), currentNodeOrToken.AsToken()); + var replacedParent = isNode + ? nodeOrToken.Parent.ReplaceNode(nodeOrToken.AsNode()!, currentNodeOrToken.AsNode()!) + : nodeOrToken.Parent.ReplaceToken(nodeOrToken.AsToken(), currentNodeOrToken.AsToken()); - currentNodeOrToken = replacedParent - .ChildNodesAndTokens() - .Single(c => c.HasAnnotation(annotation)); - } + currentNodeOrToken = replacedParent + .ChildNodesAndTokens() + .Single(c => c.HasAnnotation(annotation)); + } - if (isNode) + if (isNode) + { + var currentNode = currentNodeOrToken.AsNode()!; + if (this.NodeRequiresNonSpeculativeSemanticModel(nodeOrToken.AsNode()!)) + { + // Since this node cannot be speculated, we are replacing the Document with the changes and get a new SemanticModel + var marker = new SyntaxAnnotation(); + var newRoot = root.ReplaceNode(nodeOrToken.AsNode()!, currentNode.WithAdditionalAnnotations(marker)); + var newDocument = document.WithSyntaxRoot(newRoot); + semanticModelForReduce = await newDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + newRoot = await semanticModelForReduce.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + currentNodeOrToken = newRoot.DescendantNodes().Single(c => c.HasAnnotation(marker)); + } + else { - var currentNode = currentNodeOrToken.AsNode()!; - if (this.NodeRequiresNonSpeculativeSemanticModel(nodeOrToken.AsNode()!)) - { - // Since this node cannot be speculated, we are replacing the Document with the changes and get a new SemanticModel - var marker = new SyntaxAnnotation(); - var newRoot = root.ReplaceNode(nodeOrToken.AsNode()!, currentNode.WithAdditionalAnnotations(marker)); - var newDocument = document.WithSyntaxRoot(newRoot); - semanticModelForReduce = await newDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - newRoot = await semanticModelForReduce.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - currentNodeOrToken = newRoot.DescendantNodes().Single(c => c.HasAnnotation(marker)); - } - else - { - // Create speculative semantic model for simplified node. - semanticModelForReduce = GetSpeculativeSemanticModel(ref currentNode, semanticModel, nodeOrToken.AsNode()!); - currentNodeOrToken = currentNode; - } + // Create speculative semantic model for simplified node. + semanticModelForReduce = GetSpeculativeSemanticModel(ref currentNode, semanticModel, nodeOrToken.AsNode()!); + currentNodeOrToken = currentNode; } } - - // Reduce the current node or token. - currentNodeOrToken = rewriter.VisitNodeOrToken(currentNodeOrToken, semanticModelForReduce, simplifyAllDescendants); } - while (rewriter.HasMoreWork); + + // Reduce the current node or token. + currentNodeOrToken = rewriter.VisitNodeOrToken(currentNodeOrToken, semanticModelForReduce, simplifyAllDescendants); } + while (rewriter.HasMoreWork); + } - // If nodeOrToken was simplified, add it to the appropriate dictionary of replaced nodes/tokens. - if (currentNodeOrToken != nodeOrToken) + // If nodeOrToken was simplified, add it to the appropriate dictionary of replaced nodes/tokens. + if (currentNodeOrToken != nodeOrToken) + { + if (isNode) { - if (isNode) - { - reducedNodesMap[nodeOrToken.AsNode()!] = currentNodeOrToken.AsNode()!; - } - else - { - reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); - } + reducedNodesMap[nodeOrToken.AsNode()!] = currentNodeOrToken.AsNode()!; } - }).ConfigureAwait(false); + else + { + reducedTokensMap[nodeOrToken.AsToken()] = currentNodeOrToken.AsToken(); + } + } + } } // find any namespace imports / using directives marked for simplification in the specified spans From 1a2f73fe0cd6b020d7aae0233f7d77316eb61284 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:08:13 -0700 Subject: [PATCH 324/423] Move off UI thread --- .../VisualStudioSymbolSearchService.cs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs index be065a81685d3..b29accb7d3325 100644 --- a/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs +++ b/src/VisualStudio/Core/Def/SymbolSearch/VisualStudioSymbolSearchService.cs @@ -23,6 +23,7 @@ using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Settings; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using VSShell = Microsoft.VisualStudio.Shell; @@ -70,15 +71,27 @@ public VisualStudioSymbolSearchService( public void Dispose() { - ISymbolSearchUpdateEngine? updateEngine; - using (_gate.DisposableWait()) + // Once we're disposed, swap out our engine with a no-op one so we don't try to do any more work, and dispose of + // our connection to the OOP server so it can be cleaned up. + // + // Kick off a Task for this so we don't block MEF from proceeding (as it will be calling us on the UI thread). + _ = DisposeAsync(); + return; + + async Task DisposeAsync() { - // Once we're disposed, swap out our engine with a no-op one so we don't try to do any more work. - updateEngine = _lazyUpdateEngine; - _lazyUpdateEngine = SymbolSearchUpdateNoOpEngine.Instance; - } + // Make sure we get off the UI thread so that Dispose can return immediately. + await TaskScheduler.Default; - updateEngine?.Dispose(); + ISymbolSearchUpdateEngine? updateEngine; + using (await _gate.DisposableWaitAsync().ConfigureAwait(false)) + { + updateEngine = _lazyUpdateEngine; + _lazyUpdateEngine = SymbolSearchUpdateNoOpEngine.Instance; + } + + updateEngine?.Dispose(); + } } protected override async Task EnableServiceAsync(CancellationToken cancellationToken) From d99e593aac3d0166e1a1bb866dfc99aea3c551f0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:27:41 -0700 Subject: [PATCH 325/423] remove unread local --- .../Core/GoToDefinition/AbstractGoToCommandHandler`2.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs index 8c0b23e04ede9..8253bca56b7e1 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Tagging; @@ -24,7 +23,6 @@ using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor.Commanding; -using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Threading; using Microsoft.VisualStudio.Utilities; @@ -188,7 +186,7 @@ private async Task ExecuteCommandWorkerAsync( if (definitions.Length > 0) { var title = await findContext.GetSearchTitleAsync(cancellationToken).ConfigureAwait(false); - var location = await _streamingPresenter.TryPresentLocationOrNavigateIfOneAsync( + await _streamingPresenter.TryPresentLocationOrNavigateIfOneAsync( _threadingContext, document.Project.Solution.Workspace, title ?? DisplayName, From 2b2eb570b5a0613c2fb075fc4d3ba04b4767917e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:32:13 -0700 Subject: [PATCH 326/423] Move off UI thread --- src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs index b9dfcf5fcf18f..3f0061ae429f4 100644 --- a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs +++ b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs @@ -28,6 +28,7 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; +using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.Editor.InlineHints { @@ -266,7 +267,8 @@ bool KeepOpen() private async Task StartToolTipServiceAsync(IToolTipPresenter toolTipPresenter) { var threadingContext = _taggerProvider.ThreadingContext; - var uiList = await Task.Run(() => CreateDescriptionAsync(threadingContext.DisposalToken)).ConfigureAwait(false); + await TaskScheduler.Default; + var uiList = await CreateDescriptionAsync(threadingContext.DisposalToken).ConfigureAwait(false); await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(threadingContext.DisposalToken); toolTipPresenter.StartOrUpdate(_textView.TextSnapshot.CreateTrackingSpan(_span.Start, _span.Length, SpanTrackingMode.EdgeInclusive), uiList); From 0ee5ff408245e778bf078096e707a790f03268dc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:35:40 -0700 Subject: [PATCH 327/423] Switch to explicit parallel --- .../ValueTracking/ValueTracker.OperationCollector.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs b/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs index 24a0440e06b94..a62f976c6c69b 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs +++ b/src/Features/Core/Portable/ValueTracking/ValueTracker.OperationCollector.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking; @@ -186,10 +187,10 @@ private async Task TrackArgumentsAsync(ImmutableArray argume .Select(argument => (collector: Clone(), argument)) .ToImmutableArray(); - var tasks = collectorsAndArgumentMap - .Select(pair => Task.Run(() => pair.collector.VisitAsync(pair.argument, cancellationToken))); - - await Task.WhenAll(tasks).ConfigureAwait(false); + await RoslynParallel.ForEachAsync( + collectorsAndArgumentMap, + cancellationToken, + async (pair, cancellationToken) => await pair.collector.VisitAsync(pair.argument.Value, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); var items = collectorsAndArgumentMap .Select(pair => pair.collector.ProgressCollector) From cf5b7ff466ea995200e7daa23d8c84f251e64ada Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:36:42 -0700 Subject: [PATCH 328/423] Switch to explicit parallel --- .../LanguageServerProjectSystem.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs index 30e61e87b462c..91e035d68c011 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; @@ -159,23 +160,18 @@ private async ValueTask LoadOrReloadProjectsAsync(ImmutableSegmentedList(); - var projectsThatNeedRestore = new ConcurrentSet(); - foreach (var projectToLoad in projectPathsToLoadOrReload) - { - tasks.Add(Task.Run(async () => + await RoslynParallel.ForEachAsync( + projectPathsToLoadOrReload, + cancellationToken, + async (projectToLoad, cancellationToken) => { var projectNeedsRestore = await LoadOrReloadProjectAsync(projectToLoad, toastErrorReporter, buildHostProcessManager, cancellationToken); if (projectNeedsRestore) - { projectsThatNeedRestore.Add(projectToLoad.Path); - } - }, cancellationToken)); - } - - await Task.WhenAll(tasks); + }).ConfigureAwait(false); + var tasks = new List(); if (_globalOptionService.GetOption(LanguageServerProjectSystemOptionsStorage.EnableAutomaticRestore) && projectsThatNeedRestore.Any()) { From 7c4a99ad78bc3cad8034ef257c24b5bb8a500c03 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:38:53 -0700 Subject: [PATCH 329/423] move off of task.run --- .../Utilities/BrokeredServiceProxy.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs index bff88eebd4630..de5f1376515eb 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Nerdbank.Streams; +using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; @@ -25,8 +26,14 @@ public BrokeredServiceProxy(T service) { var (serverStream, clientStream) = FullDuplexStream.CreatePair(); - var serverTask = Task.Run(async () => + _createConnectionTask = Task.WhenAll(CreateServerAsync(), CreateClientAsync()); + return; + + async Task CreateServerAsync() { + // Ensure caller can proceed. + await Task.Yield().ConfigureAwait(false); + var serverMultiplexingStream = await MultiplexingStream.CreateAsync(serverStream); var serverChannel = await serverMultiplexingStream.AcceptChannelAsync(""); @@ -35,10 +42,13 @@ public BrokeredServiceProxy(T service) _serverRpc.AddLocalRpcTarget(service, options: null); _serverRpc.StartListening(); - }); + } - var clientTask = Task.Run(async () => + async Task CreateClientAsync() { + // Ensure caller can proceed. + await Task.Yield().ConfigureAwait(false); + var clientMultiplexingStream = await MultiplexingStream.CreateAsync(clientStream); var clientChannel = await clientMultiplexingStream.OfferChannelAsync(""); @@ -47,9 +57,7 @@ public BrokeredServiceProxy(T service) _clientFactoryProxy = _clientRpc.Attach(); _clientRpc.StartListening(); - }); - - _createConnectionTask = Task.WhenAll(serverTask, clientTask); + } } public async ValueTask DisposeAsync() From 20fccbc16c22ec7de8ad07997c01a35f6da2cb7e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:43:13 -0700 Subject: [PATCH 330/423] Switch to explicit parallel --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index f24820a50f801..95689047fff91 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 @@ -68,7 +69,7 @@ public DiagnosticGetter( protected ImmutableArray GetDiagnosticData() => (_lazyDataBuilder != null) ? _lazyDataBuilder.ToImmutableArray() : []; - protected abstract Task AppendDiagnosticsAsync(Project project, IEnumerable documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken); + protected abstract ValueTask AppendDiagnosticsAsync(Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken); public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { @@ -96,19 +97,11 @@ protected async Task AppendDiagnosticsAsync(Solution solution, CancellationToken { // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. // We do this to not get into thread starvation causing hundreds of threads to be spawned. - var includeProjectNonLocalResult = true; - - var tasks = new Task[solution.ProjectIds.Count]; - var index = 0; - foreach (var project in solution.Projects) - { - var localProject = project; - tasks[index++] = Task.Run( - () => AppendDiagnosticsAsync( - localProject, localProject.DocumentIds, includeProjectNonLocalResult, cancellationToken), cancellationToken); - } - - await Task.WhenAll(tasks).ConfigureAwait(false); + await RoslynParallel.ForEachAsync( + solution.Projects, + cancellationToken, + (project, cancellationToken) => AppendDiagnosticsAsync( + project, project.DocumentIds, includeProjectNonLocalResult: true, cancellationToken)).ConfigureAwait(false); } protected void AppendDiagnostics(ImmutableArray items) @@ -137,7 +130,8 @@ public IdeCachedDiagnosticGetter(DiagnosticIncrementalAnalyzer owner, Solution s { } - protected override async Task AppendDiagnosticsAsync(Project project, IEnumerable documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) + protected override async ValueTask AppendDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { foreach (var stateSet in StateManager.GetStateSets(project.Id)) { @@ -261,7 +255,8 @@ public async Task> GetProjectDiagnosticsAsync(Can protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); - protected override async Task AppendDiagnosticsAsync(Project project, IEnumerable documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) + protected override async ValueTask AppendDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { // get analyzers that are not suppressed. var stateSets = StateManager.GetOrCreateStateSets(project).Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); From 65ce70b251fd3c56eedbb8461dc5de011fcb70e7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 11:57:53 -0700 Subject: [PATCH 331/423] move off of task.run --- .../Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index 5d5366d40dd6b..f2ae7f89e5f48 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -124,7 +124,7 @@ public VisualStudioWorkspaceImpl(ExportProvider exportProvider, IAsyncServicePro ProjectSystemProjectFactory = new ProjectSystemProjectFactory(this, FileChangeWatcher, CheckForAddedFileBeingOpenMaybeAsync, RemoveProjectFromMaps); - _ = Task.Run(() => InitializeUIAffinitizedServicesAsync(asyncServiceProvider)); + InitializeUIAffinitizedServicesAsync(asyncServiceProvider).Forget(); _lazyExternalErrorDiagnosticUpdateSource = new Lazy(() => new ExternalErrorDiagnosticUpdateSource( @@ -183,6 +183,9 @@ internal void SubscribeExternalErrorDiagnosticUpdateSourceToSolutionBuildEvents( public async Task InitializeUIAffinitizedServicesAsync(IAsyncServiceProvider asyncServiceProvider) { + // Yield the thread, so the caller can proceed and return immediately. + await Task.Yield(); + // Create services that are bound to the UI thread await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); From 26db6c51fefe749cfd22b2db32a735801f387ce6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 12:16:59 -0700 Subject: [PATCH 332/423] Fix logic --- ...odImportCompletionHelper.SymbolComputer.cs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs index 57d053dc697d6..3fc61edaab320 100644 --- a/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs +++ b/src/Features/Core/Portable/Completion/Providers/ImportCompletionProvider/ExtensionMethodImportCompletionHelper.SymbolComputer.cs @@ -109,16 +109,19 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke using var _ = ArrayBuilder.GetInstance(results[0].Length + results[1].Length, out var symbols); foreach (var methodArray in results) { - // `null` indicates we don't have the index ready for the corresponding project/PE. - // returns what we have even it means we only show partial results. - if (methodArray is [null]) + foreach (var method in methodArray) { - isPartialResult = true; - continue; + // `null` indicates we don't have the index ready for the corresponding project/PE. + // returns what we have even it means we only show partial results. + if (method is null) + { + isPartialResult = true; + } + else + { + symbols.Add(method); + } } - - foreach (var method in methodArray) - symbols.AddIfNotNull(method); } var browsableSymbols = symbols From 6494491abc3fd66b278df45e6146a89a5ed8b68c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 12:33:51 -0700 Subject: [PATCH 333/423] Switch to producer consumer --- .../LanguageServerProjectSystem.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs index 91e035d68c011..6a042ba89753d 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs @@ -160,18 +160,18 @@ private async ValueTask LoadOrReloadProjectsAsync(ImmutableSegmentedList(); - await RoslynParallel.ForEachAsync( - projectPathsToLoadOrReload, - cancellationToken, - async (projectToLoad, cancellationToken) => + var projectsThatNeedRestore = await ProducerConsumer.RunParallelAsync( + source: projectPathsToLoadOrReload, + produceItems: static async (projectToLoad, callback, args, cancellationToken) => { - var projectNeedsRestore = await LoadOrReloadProjectAsync(projectToLoad, toastErrorReporter, buildHostProcessManager, cancellationToken); + var projectNeedsRestore = await args.@this.LoadOrReloadProjectAsync( + projectToLoad, args.toastErrorReporter, args.buildHostProcessManager, cancellationToken); if (projectNeedsRestore) - projectsThatNeedRestore.Add(projectToLoad.Path); - }).ConfigureAwait(false); - var tasks = new List(); + callback(projectToLoad.Path); + }, + args: (@this: this, toastErrorReporter, buildHostProcessManager), + cancellationToken).ConfigureAwait(false); if (_globalOptionService.GetOption(LanguageServerProjectSystemOptionsStorage.EnableAutomaticRestore) && projectsThatNeedRestore.Any()) { From f85f1b23599e6ce07f76f8576013d6fb16bfeab7 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 14 May 2024 12:36:12 -0700 Subject: [PATCH 334/423] Remove virtual from 'SourceMethodSymbolWithAttributes.SyntaxNode' (#73433) --- .../Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 70bbd0a42458f..ee43658655c77 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -82,7 +82,7 @@ internal SyntaxReference SyntaxRef } } - internal virtual CSharpSyntaxNode SyntaxNode + internal CSharpSyntaxNode SyntaxNode { get { From a944cf892fa81e816cc70d8242c8b3cedcca3554 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:13:15 -0700 Subject: [PATCH 335/423] Switch to callback style --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 95689047fff91..b8d65303b06a7 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -67,9 +68,10 @@ public DiagnosticGetter( protected virtual bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => true; protected ImmutableArray GetDiagnosticData() - => (_lazyDataBuilder != null) ? _lazyDataBuilder.ToImmutableArray() : []; + => _lazyDataBuilder != null ? _lazyDataBuilder.ToImmutableArray() : []; - protected abstract ValueTask AppendDiagnosticsAsync(Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken); + protected abstract Task ProduceDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, Action callback, CancellationToken cancellationToken); public async Task> GetDiagnosticsAsync(CancellationToken cancellationToken) { @@ -81,27 +83,35 @@ public async Task> GetDiagnosticsAsync(Cancellati return GetDiagnosticData(); } - var documentIds = _getDocuments(project, DocumentId); - // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; - await AppendDiagnosticsAsync(project, documentIds, includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + await ProduceProjectDiagnosticsAsync( + [project], project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); + return GetDiagnosticData(); } - await AppendDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); + await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); return GetDiagnosticData(); } - protected async Task AppendDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) + protected Task ProduceSolutionDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) + => ProduceProjectDiagnosticsAsync(solution.Projects, static project => project.DocumentIds, includeProjectNonLocalResult: true, cancellationToken); + + protected async Task ProduceProjectDiagnosticsAsync( + IEnumerable projects, Func> getDocumentIds, + bool includeProjectNonLocalResult, CancellationToken cancellationToken) { // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. // We do this to not get into thread starvation causing hundreds of threads to be spawned. - await RoslynParallel.ForEachAsync( - solution.Projects, - cancellationToken, - (project, cancellationToken) => AppendDiagnosticsAsync( - project, project.DocumentIds, includeProjectNonLocalResult: true, cancellationToken)).ConfigureAwait(false); + var diagnostics = await ProducerConsumer.RunParallelAsync( + source: projects, + produceItems: static (project, callback, args, cancellationToken) => args.@this.ProduceDiagnosticsAsync( + project, args.getDocumentIds(project), args.includeProjectNonLocalResult, callback, cancellationToken), + args: (@this: this, getDocumentIds, includeProjectNonLocalResult), + cancellationToken).ConfigureAwait(false); + + AppendDiagnostics(diagnostics); } protected void AppendDiagnostics(ImmutableArray items) @@ -109,13 +119,18 @@ protected void AppendDiagnostics(ImmutableArray items) Debug.Assert(!items.IsDefault); if (_lazyDataBuilder == null) - { Interlocked.CompareExchange(ref _lazyDataBuilder, ImmutableArray.CreateBuilder(), null); - } lock (_lazyDataBuilder) + _lazyDataBuilder.AddRange(items); + } + + protected void InvokeCallback(Action callback, ImmutableArray diagnostics) + { + foreach (var diagnostic in diagnostics) { - _lazyDataBuilder.AddRange(items.Where(ShouldIncludeSuppressedDiagnostic).Where(ShouldIncludeDiagnostic)); + if (ShouldIncludeSuppressedDiagnostic(diagnostic) && ShouldIncludeDiagnostic(diagnostic)) + callback(diagnostic); } } @@ -130,8 +145,9 @@ public IdeCachedDiagnosticGetter(DiagnosticIncrementalAnalyzer owner, Solution s { } - protected override async ValueTask AppendDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) + protected override async Task ProduceDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, + Action callback, CancellationToken cancellationToken) { foreach (var stateSet in StateManager.GetStateSets(project.Id)) { @@ -139,18 +155,18 @@ protected override async ValueTask AppendDiagnosticsAsync( { if (IncludeLocalDocumentDiagnostics) { - AppendDiagnostics(await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); - AppendDiagnostics(await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Syntax, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.Semantic, cancellationToken).ConfigureAwait(false)); } if (IncludeNonLocalDocumentDiagnostics) - AppendDiagnostics(await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetDiagnosticsAsync(stateSet, project, documentId, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - AppendDiagnostics(await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); + InvokeCallback(callback, await GetProjectStateDiagnosticsAsync(stateSet, project, documentId: null, AnalysisKind.NonLocal, cancellationToken).ConfigureAwait(false)); } } } @@ -242,21 +258,23 @@ public async Task> GetProjectDiagnosticsAsync(Can var project = Solution.GetProject(ProjectId); if (project != null) { - await AppendDiagnosticsAsync(project, documentIds: [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); + await ProduceProjectDiagnosticsAsync( + [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } return GetDiagnosticData(); } - await AppendDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); + await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); return GetDiagnosticData(); } protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => _diagnosticIds == null || _diagnosticIds.Contains(diagnostic.Id); - protected override async ValueTask AppendDiagnosticsAsync( - Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) + protected override async Task ProduceDiagnosticsAsync( + Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, + Action callback, CancellationToken cancellationToken) { // get analyzers that are not suppressed. var stateSets = StateManager.GetOrCreateStateSets(project).Where(s => ShouldIncludeStateSet(project, s)).ToImmutableArrayOrEmpty(); @@ -276,18 +294,18 @@ protected override async ValueTask AppendDiagnosticsAsync( { if (IncludeLocalDocumentDiagnostics) { - AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); - AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); + InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax)); + InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic)); } if (IncludeNonLocalDocumentDiagnostics) - AppendDiagnostics(analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); + InvokeCallback(callback, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal)); } if (includeProjectNonLocalResult) { // include project diagnostics if there is no target document - AppendDiagnostics(analysisResult.GetOtherDiagnostics()); + InvokeCallback(callback, analysisResult.GetOtherDiagnostics()); } } } From 6ce65bc9f83ec367a159a70e52b8ab9aaa6e9602 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:15:44 -0700 Subject: [PATCH 336/423] Fix comment --- .../ObjectBrowser/AbstractObjectBrowserLibraryManager.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs index 83d9905b2b025..17a4c3c03891b 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager.cs @@ -521,8 +521,7 @@ private static async Task FindReferencesAsync( try { - // Kick off the work to do the actual finding on a BG thread. That way we don' t block the calling (UI) - // thread too long if we happen to do our work on this thread. + // Switch to teh background so we don't block the calling thread (the UI thread) while we're doing this work. await TaskScheduler.Default; await FindReferencesAsync(symbolListItem, project, context, classificationOptions, cancellationToken).ConfigureAwait(false); } From b303702483b2463fd22f88e84a036ea191d21016 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:17:20 -0700 Subject: [PATCH 337/423] swithc how we yield --- .../Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index f2ae7f89e5f48..f6c0795f1e344 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -184,10 +184,8 @@ internal void SubscribeExternalErrorDiagnosticUpdateSourceToSolutionBuildEvents( public async Task InitializeUIAffinitizedServicesAsync(IAsyncServiceProvider asyncServiceProvider) { // Yield the thread, so the caller can proceed and return immediately. - await Task.Yield(); - // Create services that are bound to the UI thread - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(alwaysYield: true, _threadingContext.DisposalToken); // Fetch the session synchronously on the UI thread; if this doesn't happen before we try using this on // the background thread then we will experience hangs like we see in this bug: From 4c609b36ba2ecd9b9b5307b85fad464a1598c713 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:17:31 -0700 Subject: [PATCH 338/423] revert --- .../FixAllOccurrences/BatchFixAllProvider.cs | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs index daffdbf5e850f..c5fccb6afbcfb 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs @@ -139,23 +139,22 @@ private static async Task AddDocumentChangesAsync( private static async Task> GetAllChangedDocumentsInDiagnosticsOrderAsync( FixAllContext fixAllContext, ImmutableArray orderedDiagnostics) { + var solution = fixAllContext.Solution; var cancellationToken = fixAllContext.CancellationToken; - // Process each diagnostic, determine the code actions to fix it, then figure out the document changes produced - // by that code action. - return await ProducerConsumer.RunParallelAsync( - source: orderedDiagnostics, - produceItems: static async (diagnostic, callback, args, cancellationToken) => - { - var fixAllContext = args.fixAllContext; - var solution = fixAllContext.Solution; - - var document = solution.GetRequiredDocument(diagnostic.Location.SourceTree!); + // Process each diagnostic, determine the code actions to fix it, then figure out the document changes + // produced by that code action. + using var _1 = ArrayBuilder>>.GetInstance(out var tasks); + foreach (var diagnostic in orderedDiagnostics) + { + var document = solution.GetRequiredDocument(diagnostic.Location.SourceTree!); + cancellationToken.ThrowIfCancellationRequested(); + tasks.Add(Task.Run(async () => + { // Create a context that will add the reported code actions into this - using var _ = ArrayBuilder.GetInstance(out var codeActions); - - var action = GetRegisterCodeFixAction(args.fixAllContext.CodeActionEquivalenceKey, codeActions); + using var _2 = ArrayBuilder.GetInstance(out var codeActions); + var action = GetRegisterCodeFixAction(fixAllContext.CodeActionEquivalenceKey, codeActions); var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, [diagnostic], action, fixAllContext.State.CodeActionOptionsProvider, cancellationToken); // Wait for the all the code actions to be reported for this diagnostic. @@ -163,6 +162,7 @@ private static async Task> GetAllChangedDocumentsInDiag await registerTask.ConfigureAwait(false); // Now, process each code action and find out all the document changes caused by it. + using var _3 = ArrayBuilder.GetInstance(out var changedDocuments); foreach (var codeAction in codeActions) { @@ -171,13 +171,24 @@ private static async Task> GetAllChangedDocumentsInDiag if (changedSolution != null) { var changedDocumentIds = new SolutionChanges(changedSolution, solution).GetProjectChanges().SelectMany(p => p.GetChangedDocuments()); - foreach (var changedDocumentId in changedDocumentIds) - callback(changedSolution.GetRequiredDocument(changedDocumentId)); + changedDocuments.AddRange(changedDocumentIds.Select(id => changedSolution.GetRequiredDocument(id))); } } - }, - args: (fixAllContext, true), - cancellationToken).ConfigureAwait(false); + + return changedDocuments.ToImmutableAndClear(); + }, cancellationToken)); + } + + // Wait for all that work to finish. + await Task.WhenAll(tasks).ConfigureAwait(false); + + // Flatten the set of changed documents. These will naturally still be ordered by the diagnostic that + // caused the change. + using var _4 = ArrayBuilder.GetInstance(out var result); + foreach (var task in tasks) + result.AddRange(await task.ConfigureAwait(false)); + + return result.ToImmutableAndClear(); } /// From 5e5e448927f6152f1cf0f0876cd7519841c55d83 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:28:14 -0700 Subject: [PATCH 339/423] Switch to producer consumer --- ...pilationState.TranslationAction_Actions.cs | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 7e06aec5997e0..3296a9cd44ce1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -135,28 +135,27 @@ internal sealed class AddDocumentsAction( ImmutableArray documents) : TranslationAction(oldProjectState, newProjectState) { - /// - /// Amount to break batches of documents into. That allows us to process things in parallel, without also - /// creating too many individual actions that then need to be processed. - /// - public const int AddDocumentsBatchSize = 32; - public readonly ImmutableArray Documents = documents; public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) { - await RoslynParallel.ForEachAsync( - this.Documents, - cancellationToken, - static async (document, cancellationToken) => - await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false); - - using var _2 = ArrayBuilder.GetInstance(this.Documents.Length, out var trees); - + using var _ = PooledDictionary.GetInstance(out var documentToIndex); foreach (var document in this.Documents) - trees.Add(await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false)); + { + cancellationToken.ThrowIfCancellationRequested(); + documentToIndex.Add(document, documentToIndex.Count); + } + + var documentsAndTrees = await ProducerConsumer<(DocumentState document, SyntaxTree tree)>.RunParallelAsync( + source: this.Documents, + produceItems: static async (document, callback, _, cancellationToken) => + callback((document, await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false))), + args: default(VoidResult), + cancellationToken).ConfigureAwait(false); - return oldCompilation.AddSyntaxTrees(trees); + return oldCompilation.AddSyntaxTrees(documentsAndTrees + .Sort((dt1, dt2) => documentToIndex[dt1.document] - documentToIndex[dt2.document]) + .Select(static dt => dt.tree)); } // This action adds the specified trees, but leaves the generated trees untouched. From 4dbbe08637745f3070a4da599f2cd085fb012fbf Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:29:01 -0700 Subject: [PATCH 340/423] Add comment --- .../SolutionCompilationState.TranslationAction_Actions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 3296a9cd44ce1..acb8c860cd0cd 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -139,6 +139,9 @@ internal sealed class AddDocumentsAction( public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) { + // TODO(cyrusn): Do we need to ensure that the syntax trees we add to the compilation are in the same + // order as the documents array we have added to the project? If not, we can remove this map and the + // sorting below. using var _ = PooledDictionary.GetInstance(out var documentToIndex); foreach (var document in this.Documents) { From 15640656e6a9ca9b944ee4f4751925d365c49d96 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:32:06 -0700 Subject: [PATCH 341/423] Make private --- .../EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index b8d65303b06a7..3c0faf6fff485 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -114,7 +114,7 @@ protected async Task ProduceProjectDiagnosticsAsync( AppendDiagnostics(diagnostics); } - protected void AppendDiagnostics(ImmutableArray items) + private void AppendDiagnostics(ImmutableArray items) { Debug.Assert(!items.IsDefault); From f0950ddadce4275a91be41b26ccc8dd080d7e7ba Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:33:09 -0700 Subject: [PATCH 342/423] Add back --- .../SolutionCompilationState.TranslationAction_Actions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index acb8c860cd0cd..785fc9debad7a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -135,6 +135,12 @@ internal sealed class AddDocumentsAction( ImmutableArray documents) : TranslationAction(oldProjectState, newProjectState) { + /// + /// Amount to break batches of documents into. That allows us to process things in parallel, without also + /// creating too many individual actions that then need to be processed. + /// + public const int AddDocumentsBatchSize = 32; + public readonly ImmutableArray Documents = documents; public override async Task TransformCompilationAsync(Compilation oldCompilation, CancellationToken cancellationToken) From f74d01834c69188732146b62e52f0c29ab25839c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:37:18 -0700 Subject: [PATCH 343/423] Simplify state --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 50 ++++--------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 3c0faf6fff485..cfa31bf16670f 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -5,11 +5,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -39,8 +37,6 @@ private abstract class DiagnosticGetter private readonly Func> _getDocuments; - private ImmutableArray.Builder? _lazyDataBuilder; - public DiagnosticGetter( DiagnosticIncrementalAnalyzer owner, Solution solution, @@ -67,9 +63,6 @@ public DiagnosticGetter( protected virtual bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) => true; - protected ImmutableArray GetDiagnosticData() - => _lazyDataBuilder != null ? _lazyDataBuilder.ToImmutableArray() : []; - protected abstract Task ProduceDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, Action callback, CancellationToken cancellationToken); @@ -79,50 +72,32 @@ public async Task> GetDiagnosticsAsync(Cancellati { var project = Solution.GetProject(ProjectId); if (project == null) - { - return GetDiagnosticData(); - } + return []; // return diagnostics specific to one project or document var includeProjectNonLocalResult = DocumentId == null; - await ProduceProjectDiagnosticsAsync( + return await ProduceProjectDiagnosticsAsync( [project], project => _getDocuments(project, DocumentId), includeProjectNonLocalResult, cancellationToken).ConfigureAwait(false); - - return GetDiagnosticData(); } - await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); - return GetDiagnosticData(); + return await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); } - protected Task ProduceSolutionDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) + protected Task> ProduceSolutionDiagnosticsAsync(Solution solution, CancellationToken cancellationToken) => ProduceProjectDiagnosticsAsync(solution.Projects, static project => project.DocumentIds, includeProjectNonLocalResult: true, cancellationToken); - protected async Task ProduceProjectDiagnosticsAsync( + protected async Task> ProduceProjectDiagnosticsAsync( IEnumerable projects, Func> getDocumentIds, bool includeProjectNonLocalResult, CancellationToken cancellationToken) { // PERF: run projects in parallel rather than running CompilationWithAnalyzer with concurrency == true. // We do this to not get into thread starvation causing hundreds of threads to be spawned. - var diagnostics = await ProducerConsumer.RunParallelAsync( + return await ProducerConsumer.RunParallelAsync( source: projects, produceItems: static (project, callback, args, cancellationToken) => args.@this.ProduceDiagnosticsAsync( project, args.getDocumentIds(project), args.includeProjectNonLocalResult, callback, cancellationToken), args: (@this: this, getDocumentIds, includeProjectNonLocalResult), cancellationToken).ConfigureAwait(false); - - AppendDiagnostics(diagnostics); - } - - private void AppendDiagnostics(ImmutableArray items) - { - Debug.Assert(!items.IsDefault); - - if (_lazyDataBuilder == null) - Interlocked.CompareExchange(ref _lazyDataBuilder, ImmutableArray.CreateBuilder(), null); - - lock (_lazyDataBuilder) - _lazyDataBuilder.AddRange(items); } protected void InvokeCallback(Action callback, ImmutableArray diagnostics) @@ -256,17 +231,14 @@ public async Task> GetProjectDiagnosticsAsync(Can if (ProjectId != null) { var project = Solution.GetProject(ProjectId); - if (project != null) - { - await ProduceProjectDiagnosticsAsync( - [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); - } + if (project is null) + return []; - return GetDiagnosticData(); + return await ProduceProjectDiagnosticsAsync( + [project], static _ => [], includeProjectNonLocalResult: true, cancellationToken).ConfigureAwait(false); } - await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); - return GetDiagnosticData(); + return await ProduceSolutionDiagnosticsAsync(Solution, cancellationToken).ConfigureAwait(false); } protected override bool ShouldIncludeDiagnostic(DiagnosticData diagnostic) From 04dcd375381b6b13209b2d4994cfbf54c8dfa806 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:39:01 -0700 Subject: [PATCH 344/423] Remove ct --- .../SolutionCompilationState.TranslationAction_Actions.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs index 785fc9debad7a..6d624d7c540f0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.TranslationAction_Actions.cs @@ -150,10 +150,7 @@ public override async Task TransformCompilationAsync(Compilation ol // sorting below. using var _ = PooledDictionary.GetInstance(out var documentToIndex); foreach (var document in this.Documents) - { - cancellationToken.ThrowIfCancellationRequested(); documentToIndex.Add(document, documentToIndex.Count); - } var documentsAndTrees = await ProducerConsumer<(DocumentState document, SyntaxTree tree)>.RunParallelAsync( source: this.Documents, From 5f6d8d2c2f94d673d0db12f165019ee566aa92d4 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:47:22 -0700 Subject: [PATCH 345/423] Avoid hashset --- .../HostWorkspace/LanguageServerProjectSystem.cs | 3 ++- .../HostWorkspace/ProjectDependencyHelper.cs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs index 6a042ba89753d..5d3c0fce8e5ed 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs @@ -173,6 +173,7 @@ private async ValueTask LoadOrReloadProjectsAsync(ImmutableSegmentedList projectPaths, CancellationToken cancellationToken) + internal static async Task RestoreProjectsAsync(ImmutableArray projectPaths, CancellationToken cancellationToken) { if (projectPaths.IsEmpty) - { return; - } + Contract.ThrowIfTrue(projectPaths.Distinct().Length != projectPaths.Length, "Duplicate project paths are not allowed."); Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); + var languageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); var unresolvedParams = new UnresolvedDependenciesParams([.. projectPaths]); await languageServerManager.SendRequestAsync(ProjectNeedsRestoreName, unresolvedParams, cancellationToken); From b42c857fbf48c5a8e126fb25a0539166d4459db7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 13:54:06 -0700 Subject: [PATCH 346/423] One distinct call --- .../HostWorkspace/LanguageServerProjectSystem.cs | 1 - .../HostWorkspace/ProjectDependencyHelper.cs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs index 5d3c0fce8e5ed..c920f014a8327 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerProjectSystem.cs @@ -173,7 +173,6 @@ private async ValueTask LoadOrReloadProjectsAsync(ImmutableSegmentedList projectPa if (projectPaths.IsEmpty) return; - Contract.ThrowIfTrue(projectPaths.Distinct().Length != projectPaths.Length, "Duplicate project paths are not allowed."); Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); var languageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); - var unresolvedParams = new UnresolvedDependenciesParams([.. projectPaths]); + + // Ensure we only pass unique paths back to be restored. + var unresolvedParams = new UnresolvedDependenciesParams([.. projectPaths.Distinct()]); await languageServerManager.SendRequestAsync(ProjectNeedsRestoreName, unresolvedParams, cancellationToken); } From 5a5da6f44bc680f652881879f338b361ee83254a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 14:01:25 -0700 Subject: [PATCH 347/423] primary constructors --- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 91 ++++++++----------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index cfa31bf16670f..6675a828608cc 100644 --- a/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -24,40 +24,26 @@ public Task> GetDiagnosticsForIdsAsync(Solution s public Task> GetProjectDiagnosticsForIdsAsync(Solution solution, ProjectId? projectId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken) => new IdeLatestDiagnosticGetter(this, solution, projectId, documentId: null, diagnosticIds, shouldIncludeAnalyzer, getDocuments: null, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics: false, includeNonLocalDocumentDiagnostics).GetProjectDiagnosticsAsync(cancellationToken); - private abstract class DiagnosticGetter + private abstract class DiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, + Solution solution, + ProjectId? projectId, + DocumentId? documentId, + Func>? getDocuments, + bool includeSuppressedDiagnostics, + bool includeLocalDocumentDiagnostics, + bool includeNonLocalDocumentDiagnostics) { - protected readonly DiagnosticIncrementalAnalyzer Owner; - - protected readonly Solution Solution; - protected readonly ProjectId? ProjectId; - protected readonly DocumentId? DocumentId; - protected readonly bool IncludeSuppressedDiagnostics; - protected readonly bool IncludeLocalDocumentDiagnostics; - protected readonly bool IncludeNonLocalDocumentDiagnostics; - - private readonly Func> _getDocuments; - - public DiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, - Solution solution, - ProjectId? projectId, - DocumentId? documentId, - Func>? getDocuments, - bool includeSuppressedDiagnostics, - bool includeLocalDocumentDiagnostics, - bool includeNonLocalDocumentDiagnostics) - { - Owner = owner; - Solution = solution; + protected readonly DiagnosticIncrementalAnalyzer Owner = owner; - DocumentId = documentId; - _getDocuments = getDocuments ?? (static (project, documentId) => documentId != null ? [documentId] : project.DocumentIds); - ProjectId = projectId ?? documentId?.ProjectId; + protected readonly Solution Solution = solution; + protected readonly ProjectId? ProjectId = projectId ?? documentId?.ProjectId; + protected readonly DocumentId? DocumentId = documentId; + protected readonly bool IncludeSuppressedDiagnostics = includeSuppressedDiagnostics; + protected readonly bool IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; + protected readonly bool IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - IncludeSuppressedDiagnostics = includeSuppressedDiagnostics; - IncludeLocalDocumentDiagnostics = includeLocalDocumentDiagnostics; - IncludeNonLocalDocumentDiagnostics = includeNonLocalDocumentDiagnostics; - } + private readonly Func> _getDocuments = getDocuments ?? (static (project, documentId) => documentId != null ? [documentId] : project.DocumentIds); protected StateManager StateManager => Owner._stateManager; @@ -113,13 +99,16 @@ private bool ShouldIncludeSuppressedDiagnostic(DiagnosticData diagnostic) => IncludeSuppressedDiagnostics || !diagnostic.IsSuppressed; } - private sealed class IdeCachedDiagnosticGetter : DiagnosticGetter + private sealed class IdeCachedDiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, + Solution solution, + ProjectId? projectId, + DocumentId? documentId, + bool includeSuppressedDiagnostics, + bool includeLocalDocumentDiagnostics, + bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( + owner, solution, projectId, documentId, getDocuments: null, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) { - public IdeCachedDiagnosticGetter(DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) - : base(owner, solution, projectId, documentId, getDocuments: null, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) - { - } - protected override async Task ProduceDiagnosticsAsync( Project project, IReadOnlyList documentIds, bool includeProjectNonLocalResult, Action callback, CancellationToken cancellationToken) @@ -210,21 +199,21 @@ private static async Task> GetProjectStateDiagnos } } - private sealed class IdeLatestDiagnosticGetter : DiagnosticGetter + private sealed class IdeLatestDiagnosticGetter( + DiagnosticIncrementalAnalyzer owner, + Solution solution, + ProjectId? projectId, + DocumentId? documentId, + ImmutableHashSet? diagnosticIds, + Func? shouldIncludeAnalyzer, + Func>? getDocuments, + bool includeSuppressedDiagnostics, + bool includeLocalDocumentDiagnostics, + bool includeNonLocalDocumentDiagnostics) : DiagnosticGetter( + owner, solution, projectId, documentId, getDocuments, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) { - private readonly ImmutableHashSet? _diagnosticIds; - private readonly Func? _shouldIncludeAnalyzer; - - public IdeLatestDiagnosticGetter( - DiagnosticIncrementalAnalyzer owner, Solution solution, ProjectId? projectId, - DocumentId? documentId, ImmutableHashSet? diagnosticIds, Func? shouldIncludeAnalyzer, - Func>? getDocuments, - bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics) - : base(owner, solution, projectId, documentId, getDocuments, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics) - { - _diagnosticIds = diagnosticIds; - _shouldIncludeAnalyzer = shouldIncludeAnalyzer; - } + private readonly ImmutableHashSet? _diagnosticIds = diagnosticIds; + private readonly Func? _shouldIncludeAnalyzer = shouldIncludeAnalyzer; public async Task> GetProjectDiagnosticsAsync(CancellationToken cancellationToken) { From 0fcf786e807342b4b7212579227559a74496b480 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 14:41:39 -0700 Subject: [PATCH 348/423] Avoid unnecessary full tree walks in the simplifier system --- .../CSharpSimplificationService.cs | 35 +++++++++++++----- .../AbstractSimplificationService.cs | 36 +++++++++---------- ...nService.NodesAndTokensToReduceComputer.vb | 5 +-- .../VisualBasicSimplificationService.vb | 7 ++-- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs index f3075ecfe3cea..7b46d9a52c515 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs @@ -10,18 +10,21 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification; [ExportLanguageService(typeof(ISimplificationService), LanguageNames.CSharp), Shared] -internal partial class CSharpSimplificationService : AbstractSimplificationService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal partial class CSharpSimplificationService() + : AbstractSimplificationService(s_reducers) { // 1. the cast simplifier should run earlier then everything else to minimize the type expressions // 2. Extension method reducer may insert parentheses. So run it before the parentheses remover. @@ -40,12 +43,6 @@ internal partial class CSharpSimplificationService : AbstractSimplificationServi new CSharpDefaultExpressionReducer(), ]; - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpSimplificationService() : base(s_reducers) - { - } - public override SimplifierOptions DefaultOptions => CSharpSimplifierOptions.Default; @@ -230,4 +227,26 @@ private static bool IsTupleInDeconstruction(SyntaxNode tuple) } while (true); } + + protected override void AddImportDeclarations(CompilationUnitSyntax root, ArrayBuilder importDeclarations) + { + importDeclarations.AddRange(root.Usings); + + foreach (var member in root.Members) + { + if (member is BaseNamespaceDeclarationSyntax baseNamespace) + AddImportDeclarations(baseNamespace, importDeclarations); + } + + static void AddImportDeclarations(BaseNamespaceDeclarationSyntax baseNamespace, ArrayBuilder importDeclarations) + { + importDeclarations.AddRange(baseNamespace.Usings); + + foreach (var member in baseNamespace.Members) + { + if (member is BaseNamespaceDeclarationSyntax childNamespace) + AddImportDeclarations(childNamespace, importDeclarations); + } + } + } } diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index 3a3bf94dfb4a5..61bcc467df4e2 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -10,10 +10,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -21,7 +21,8 @@ namespace Microsoft.CodeAnalysis.Simplification; -internal abstract class AbstractSimplificationService : ISimplificationService +internal abstract class AbstractSimplificationService : ISimplificationService + where TCompilationUnitSyntax : SyntaxNode where TExpressionSyntax : SyntaxNode where TStatementSyntax : SyntaxNode where TCrefSyntax : SyntaxNode @@ -37,6 +38,7 @@ protected AbstractSimplificationService(ImmutableArray reducers protected abstract ImmutableArray GetNodesAndTokensToReduce(SyntaxNode root, Func isNodeOrTokenOutsideSimplifySpans); protected abstract SemanticModel GetSpeculativeSemanticModel(ref SyntaxNode nodeToSpeculate, SemanticModel originalSemanticModel, SyntaxNode originalNode); protected abstract bool NodeRequiresNonSpeculativeSemanticModel(SyntaxNode node); + protected abstract void AddImportDeclarations(TCompilationUnitSyntax root, ArrayBuilder importDeclarations); public abstract SimplifierOptions DefaultOptions { get; } public abstract SimplifierOptions GetSimplifierOptions(IOptionsReader options, SimplifierOptions? fallbackOptions); @@ -102,27 +104,24 @@ private async Task ReduceCoreAsync( // Create a simple interval tree for simplification spans. var spansTree = new TextSpanIntervalTree(spans); - bool isNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) - => !spansTree.HasIntervalThatOverlapsWith(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length); - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var root = await semanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var root = (TCompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // prep namespace imports marked for simplification var removeIfUnusedAnnotation = new SyntaxAnnotation(); var originalRoot = root; - root = PrepareNamespaceImportsForRemovalIfUnused(document, root, removeIfUnusedAnnotation, isNodeOrTokenOutsideSimplifySpans); + root = PrepareNamespaceImportsForRemovalIfUnused(root, removeIfUnusedAnnotation, IsNodeOrTokenOutsideSimplifySpans); var hasImportsToSimplify = root != originalRoot; if (hasImportsToSimplify) { document = document.WithSyntaxRoot(root); semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - root = await semanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + root = (TCompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); } // Get the list of syntax nodes and tokens that need to be reduced. - var nodesAndTokensToReduce = this.GetNodesAndTokensToReduce(root, isNodeOrTokenOutsideSimplifySpans); + var nodesAndTokensToReduce = this.GetNodesAndTokensToReduce(root, IsNodeOrTokenOutsideSimplifySpans); if (nodesAndTokensToReduce.Any()) { @@ -166,6 +165,9 @@ bool isNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) } return document; + + bool IsNodeOrTokenOutsideSimplifySpans(SyntaxNodeOrToken nodeOrToken) + => !spansTree.HasIntervalThatOverlapsWith(nodeOrToken.FullSpan.Start, nodeOrToken.FullSpan.Length); } private async Task ReduceAsync( @@ -284,20 +286,18 @@ private async Task ReduceAsync( // find any namespace imports / using directives marked for simplification in the specified spans // and add removeIfUnused annotation - private static SyntaxNode PrepareNamespaceImportsForRemovalIfUnused( - Document document, - SyntaxNode root, + private TCompilationUnitSyntax PrepareNamespaceImportsForRemovalIfUnused( + TCompilationUnitSyntax root, SyntaxAnnotation removeIfUnusedAnnotation, Func isNodeOrTokenOutsideSimplifySpan) { - var gen = SyntaxGenerator.GetGenerator(document); + using var _ = ArrayBuilder.GetInstance(out var importDeclarations); - var importsToSimplify = root.DescendantNodes().Where(n => - !isNodeOrTokenOutsideSimplifySpan(n) - && gen.GetDeclarationKind(n) == DeclarationKind.NamespaceImport - && n.HasAnnotation(Simplifier.Annotation)); + this.AddImportDeclarations(root, importDeclarations); - return root.ReplaceNodes(importsToSimplify, (o, r) => r.WithAdditionalAnnotations(removeIfUnusedAnnotation)); + return root.ReplaceNodes( + importDeclarations.Where(n => !isNodeOrTokenOutsideSimplifySpan(n) && n.HasAnnotation(Simplifier.Annotation)), + (o, r) => r.WithAdditionalAnnotations(removeIfUnusedAnnotation)); } private async Task RemoveUnusedNamespaceImportsAsync( diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb index 12ab6a61cad72..c313960a29eed 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.NodesAndTokensToReduceComputer.vb @@ -9,10 +9,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification - Partial Friend Class VisualBasicSimplificationService - Inherits AbstractSimplificationService(Of ExpressionSyntax, ExecutableStatementSyntax, CrefReferenceSyntax) - Private Class NodesAndTokensToReduceComputer Inherits VisualBasicSyntaxRewriter @@ -98,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Me._simplifyAllDescendants = Me._simplifyAllDescendants OrElse token.HasAnnotation(Simplifier.Annotation) If Me._simplifyAllDescendants AndAlso Not Me._insideSpeculatedNode AndAlso token.Kind <> SyntaxKind.None Then - Me._nodesAndTokensToReduce.Add(New NodeOrTokenToReduce(token, simplifyAllDescendants:=True, originalNodeOrToken:=token)) + Me._nodesAndTokensToReduce.Add(New NodeOrTokenToReduce(token, SimplifyAllDescendants:=True, OriginalNodeOrToken:=token)) End If If token.ContainsAnnotations OrElse savedSimplifyAllDescendants Then diff --git a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb index e4d912a910015..9246a9f11d22e 100644 --- a/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb +++ b/src/Workspaces/VisualBasic/Portable/Simplification/VisualBasicSimplificationService.vb @@ -6,10 +6,10 @@ Imports System.Collections.Immutable Imports System.Composition Imports System.Threading Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Internal.Log Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Utilities @@ -17,7 +17,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Partial Friend Class VisualBasicSimplificationService - Inherits AbstractSimplificationService(Of ExpressionSyntax, ExecutableStatementSyntax, CrefReferenceSyntax) + Inherits AbstractSimplificationService(Of CompilationUnitSyntax, ExpressionSyntax, ExecutableStatementSyntax, CrefReferenceSyntax) Private Shared ReadOnly s_reducers As ImmutableArray(Of AbstractReducer) = ImmutableArray.Create(Of AbstractReducer)( @@ -177,5 +177,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification Next End Sub + Protected Overrides Sub AddImportDeclarations(root As CompilationUnitSyntax, importDeclarations As ArrayBuilder(Of SyntaxNode)) + importDeclarations.AddRange(root.Imports) + End Sub End Class End Namespace From 257723006cf7210368c5378165d4026de5e85e2c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 14 May 2024 14:50:53 -0700 Subject: [PATCH 349/423] Update local deployment instructions (#73417) --- .../contributing/Building, Debugging, and Testing on Windows.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/Building, Debugging, and Testing on Windows.md b/docs/contributing/Building, Debugging, and Testing on Windows.md index 43408387bddba..0482960a9d56c 100644 --- a/docs/contributing/Building, Debugging, and Testing on Windows.md +++ b/docs/contributing/Building, Debugging, and Testing on Windows.md @@ -86,7 +86,7 @@ give it a try. ### Deploying with command-line (recommended method) You can build and deploy with the following command: -`.\Build.cmd -Configuration Release -deployExtensions -launch`. +`.\Build.cmd -Restore -Configuration Release -deployExtensions -launch`. Then you can launch the `RoslynDev` hive with `devenv /rootSuffix RoslynDev`. From 3e78bf65f2b5b7d5703201996e1e9d27415d9081 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 14:58:20 -0700 Subject: [PATCH 350/423] remove semantic model --- .../Simplification/AbstractSimplificationService.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index 61bcc467df4e2..6e79981f9cc10 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -104,7 +104,6 @@ private async Task ReduceCoreAsync( // Create a simple interval tree for simplification spans. var spansTree = new TextSpanIntervalTree(spans); - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var root = (TCompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // prep namespace imports marked for simplification @@ -116,7 +115,6 @@ private async Task ReduceCoreAsync( if (hasImportsToSimplify) { document = document.WithSyntaxRoot(root); - semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); root = (TCompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); } @@ -141,7 +139,7 @@ private async Task ReduceCoreAsync( // Reduce all the nodesAndTokensToReduce using the given reducers/rewriters and // store the reduced nodes and/or tokens in the reduced nodes/tokens maps. // Note that this method doesn't update the original syntax tree. - await this.ReduceAsync(document, root, nodesAndTokensToReduce, reducers, options, semanticModel, reducedNodesMap, reducedTokensMap, cancellationToken).ConfigureAwait(false); + await this.ReduceAsync(document, root, nodesAndTokensToReduce, reducers, options, reducedNodesMap, reducedTokensMap, cancellationToken).ConfigureAwait(false); if (reducedNodesMap.Any() || reducedTokensMap.Any()) { @@ -176,7 +174,6 @@ private async Task ReduceAsync( ImmutableArray nodesAndTokensToReduce, ImmutableArray reducers, SimplifierOptions options, - SemanticModel semanticModel, ConcurrentDictionary reducedNodesMap, ConcurrentDictionary reducedTokensMap, CancellationToken cancellationToken) @@ -188,6 +185,8 @@ private async Task ReduceAsync( // Reduce each node or token in the given list by running it through each reducer. var simplifyTasks = new Task[nodesAndTokensToReduce.Length]; + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + for (var i = 0; i < nodesAndTokensToReduce.Length; i++) { var nodeOrTokenToReduce = nodesAndTokensToReduce[i]; From 75004a93d90960026f3a116697f57ecb1b983939 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 15:01:22 -0700 Subject: [PATCH 351/423] Remove unnecessary node walk --- .../Core/Formatting/Engine/TokenData.cs | 39 +++---------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs index e50090dda0738..31b0114466792 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -63,50 +64,20 @@ public int CompareTo(TokenData other) Contract.ThrowIfFalse(this.TokenStream == other.TokenStream); if (this.IndexInStream >= 0 && other.IndexInStream >= 0) - { return this.IndexInStream - other.IndexInStream; - } + + if (this.Token == other.Token) + return 0; var start = this.Token.SpanStart - other.Token.SpanStart; if (start != 0) - { return start; - } var end = this.Token.Span.End - other.Token.Span.End; if (end != 0) - { return end; - } - - // this is expansive check. but there is no other way to check. - var commonRoot = this.Token.GetCommonRoot(other.Token); - RoslynDebug.Assert(commonRoot != null); - - var tokens = commonRoot.DescendantTokens(); - - var index1 = Index(tokens, this.Token); - var index2 = Index(tokens, other.Token); - Contract.ThrowIfFalse(index1 >= 0 && index2 >= 0); - - return index1 - index2; - } - - private static int Index(IEnumerable tokens, SyntaxToken token) - { - var index = 0; - - foreach (var current in tokens) - { - if (current == token) - { - return index; - } - - index++; - } - return -1; + return 0; } public static bool operator <(TokenData left, TokenData right) From ae3ecd29c8de62c35be30884b3452ab3bfa9653b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 15:17:38 -0700 Subject: [PATCH 352/423] Avoid enumeration allocation in formatter --- .../Core/Formatting/Engine/TokenData.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs index 31b0114466792..546e8a4f2ff42 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs @@ -77,7 +77,34 @@ public int CompareTo(TokenData other) if (end != 0) return end; - return 0; + // this is expansive check. but there is no other way to check. + var commonRoot = this.Token.GetCommonRoot(other.Token); + RoslynDebug.Assert(commonRoot != null); + + using var _ = ArrayBuilder.GetInstance(out var stack); + stack.Push(commonRoot); + + // Do a DFS walk to see which of the two tokens comes first. + while (stack.TryPop(out var current)) + { + if (current.IsNode) + { + // Push children in reverse order to ensure a DFS walk. + foreach (var child in current.AsNode()!.ChildNodesAndTokens().Reverse()) + stack.Push(child); + } + else if (current.IsToken) + { + var currentToken = current.AsToken(); + + if (currentToken == this.Token) + return -1; + else if (currentToken == other.Token) + return 1; + } + } + + throw ExceptionUtilities.Unreachable(); } public static bool operator <(TokenData left, TokenData right) From ab0e8fc0b878744cbdec7cfa36046f773b01c2e5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 15:19:21 -0700 Subject: [PATCH 353/423] Docs --- .../Compiler/Core/Formatting/Engine/TokenData.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs index 546e8a4f2ff42..f90001f59a56a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs @@ -77,14 +77,15 @@ public int CompareTo(TokenData other) if (end != 0) return end; - // this is expansive check. but there is no other way to check. + // We have two different tokens, which are at the same location. This can happen with things like empty/missing + // tokens. In order to give a strict ordering, we need to walk up the tree to find the first common ancestor + // and see which token we hit first in that ancestor. var commonRoot = this.Token.GetCommonRoot(other.Token); RoslynDebug.Assert(commonRoot != null); using var _ = ArrayBuilder.GetInstance(out var stack); stack.Push(commonRoot); - // Do a DFS walk to see which of the two tokens comes first. while (stack.TryPop(out var current)) { if (current.IsNode) From a6ec110a1a8118c3d1a61d6ad862a487ee59c44d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 14 May 2024 15:40:45 -0700 Subject: [PATCH 354/423] Simplify --- .../Portable/Simplification/AbstractSimplificationService.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs index dc6bb89eab0f0..2ad302dcb1782 100644 --- a/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/Core/Portable/Simplification/AbstractSimplificationService.cs @@ -232,10 +232,7 @@ async ValueTask ReduceOneNodeOrTokenAsync( // However, the reduced nodes haven't been parented yet, so do the required parenting using the original node's parent. if (currentNodeOrToken.Parent == null && nodeOrToken.Parent != null && - (currentNodeOrToken.IsToken || - currentNodeOrToken.AsNode() is TExpressionSyntax || - currentNodeOrToken.AsNode() is TStatementSyntax || - currentNodeOrToken.AsNode() is TCrefSyntax)) + (currentNodeOrToken.IsToken || currentNodeOrToken.AsNode() is TExpressionSyntax or TStatementSyntax or TCrefSyntax)) { var annotation = new SyntaxAnnotation(); currentNodeOrToken = currentNodeOrToken.WithAdditionalAnnotations(annotation); From 0a0da2d5399c2255bc72e8fa827277830ac5d61f Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Tue, 14 May 2024 15:51:41 -0700 Subject: [PATCH 355/423] Net9 Reference Assemblies (#73423) * net9.0 preview3 * more * more * pr feedback * fix baselines --- eng/Directory.Packages.props | 16 +- .../CodeGenSpanBasedStringConcatTests.cs | 232 +++++++++--------- .../Semantics/CollectionExpressionTests.cs | 66 ++--- .../Test/Emit2/Semantics/InlineArrayTests.cs | 12 +- ...CodeAnalysis.CSharp.Emit3.UnitTests.csproj | 1 + .../Test/Core/TargetFrameworkUtil.cs | 4 +- 6 files changed, 168 insertions(+), 163 deletions(-) diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index 8adaa8740c2ac..39a0493e4ff24 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -5,6 +5,7 @@ 8.0.0-preview.23468.1 1.1.2-beta1.24121.1 0.1.187-beta + <_BasicReferenceAssembliesVersion>1.7.2 4.8.0-3.final 17.10.72-preview @@ -293,13 +294,14 @@ - - - - - - - + + + + + + + + - + https://github.com/dotnet/arcade - 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 + 15eea424d3b2dd25a5c0b10e8adc8aeed50129a1 @@ -144,9 +144,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 67d23f4ba1813b315e7e33c71d18b63475f5c5f8 + 15eea424d3b2dd25a5c0b10e8adc8aeed50129a1 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/common/build.cmd b/eng/common/build.cmd new file mode 100644 index 0000000000000..99daf368abae7 --- /dev/null +++ b/eng/common/build.cmd @@ -0,0 +1,3 @@ +@echo off +powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0build.ps1""" %*" +exit /b %ErrorLevel% diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 33a6f2d0e2481..438f9920c43e4 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -19,6 +19,7 @@ Param( [switch] $pack, [switch] $publish, [switch] $clean, + [switch][Alias('pb')]$productBuild, [switch][Alias('bl')]$binaryLog, [switch][Alias('nobl')]$excludeCIBinarylog, [switch] $ci, @@ -58,6 +59,7 @@ function Print-Usage() { Write-Host " -sign Sign build outputs" Write-Host " -publish Publish artifacts (e.g. symbols)" Write-Host " -clean Clean the solution" + Write-Host " -productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" Write-Host "" Write-Host "Advanced settings:" @@ -120,6 +122,7 @@ function Build { /p:Deploy=$deploy ` /p:Test=$test ` /p:Pack=$pack ` + /p:DotNetBuildRepo=$productBuild ` /p:IntegrationTest=$integrationTest ` /p:PerformanceTest=$performanceTest ` /p:Sign=$sign ` diff --git a/eng/common/build.sh b/eng/common/build.sh index 50af40cdd2ce6..ac1ee8620cd2a 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -22,6 +22,9 @@ usage() echo " --sourceBuild Source-build the solution (short: -sb)" echo " Will additionally trigger the following actions: --restore, --build, --pack" echo " If --configuration is not set explicitly, will also set it to 'Release'" + echo " --productBuild Build the solution in the way it will be built in the full .NET product (VMR) build (short: -pb)" + echo " Will additionally trigger the following actions: --restore, --build, --pack" + echo " If --configuration is not set explicitly, will also set it to 'Release'" echo " --rebuild Rebuild solution" echo " --test Run all unit tests in the solution (short: -t)" echo " --integrationTest Run all integration tests in the solution" @@ -59,6 +62,7 @@ scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" restore=false build=false source_build=false +product_build=false rebuild=false test=false integration_test=false @@ -105,7 +109,7 @@ while [[ $# > 0 ]]; do -binarylog|-bl) binary_log=true ;; - -excludeCIBinarylog|-nobl) + -excludecibinarylog|-nobl) exclude_ci_binary_log=true ;; -pipelineslog|-pl) @@ -126,6 +130,13 @@ while [[ $# > 0 ]]; do -sourcebuild|-sb) build=true source_build=true + product_build=true + restore=true + pack=true + ;; + -productBuild|-pb) + build=true + product_build=true restore=true pack=true ;; @@ -219,7 +230,9 @@ function Build { /p:RepoRoot="$repo_root" \ /p:Restore=$restore \ /p:Build=$build \ + /p:DotNetBuildRepo=$product_build \ /p:ArcadeBuildFromSource=$source_build \ + /p:DotNetBuildSourceOnly=$source_build \ /p:Rebuild=$rebuild \ /p:Test=$test \ /p:Pack=$pack \ diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml new file mode 100644 index 0000000000000..dc3bd560a50e2 --- /dev/null +++ b/eng/common/core-templates/job/job.yml @@ -0,0 +1,266 @@ +parameters: +# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + cancelTimeoutInMinutes: '' + condition: '' + container: '' + continueOnError: false + dependsOn: '' + displayName: '' + pool: '' + steps: [] + strategy: '' + timeoutInMinutes: '' + variables: [] + workspace: '' + templateContext: {} + +# Job base template specific parameters + # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md + # publishing defaults + artifacts: '' + enableMicrobuild: false + enablePublishBuildArtifacts: false + enablePublishBuildAssets: false + enablePublishTestResults: false + enablePublishUsingPipelines: false + enableBuildRetry: false + disableComponentGovernance: '' + componentGovernanceIgnoreDirectories: '' + mergeTestResults: false + testRunTitle: '' + testResultsFormat: '' + name: '' + preSteps: [] + artifactPublishSteps: [] + runAsPublic: false + +# Sbom related params + enableSbom: true + PackageVersion: 9.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + +# 1es specific parameters + is1ESPipeline: '' + +jobs: +- job: ${{ parameters.name }} + + ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: + cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} + + ${{ if ne(parameters.condition, '') }}: + condition: ${{ parameters.condition }} + + ${{ if ne(parameters.container, '') }}: + container: ${{ parameters.container }} + + ${{ if ne(parameters.continueOnError, '') }}: + continueOnError: ${{ parameters.continueOnError }} + + ${{ if ne(parameters.dependsOn, '') }}: + dependsOn: ${{ parameters.dependsOn }} + + ${{ if ne(parameters.displayName, '') }}: + displayName: ${{ parameters.displayName }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + + ${{ if ne(parameters.strategy, '') }}: + strategy: ${{ parameters.strategy }} + + ${{ if ne(parameters.timeoutInMinutes, '') }}: + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + + ${{ if ne(parameters.templateContext, '') }}: + templateContext: ${{ parameters.templateContext }} + + variables: + - ${{ if ne(parameters.enableTelemetry, 'false') }}: + - name: DOTNET_CLI_TELEMETRY_PROFILE + value: '$(Build.Repository.Uri)' + - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: + - name: EnableRichCodeNavigation + value: 'true' + # Retry signature validation up to three times, waiting 2 seconds between attempts. + # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures + - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY + value: 3,2000 + - ${{ each variable in parameters.variables }}: + # handle name-value variable syntax + # example: + # - name: [key] + # value: [value] + - ${{ if ne(variable.name, '') }}: + - name: ${{ variable.name }} + value: ${{ variable.value }} + + # handle variable groups + - ${{ if ne(variable.group, '') }}: + - group: ${{ variable.group }} + + # handle template variable syntax + # example: + # - template: path/to/template.yml + # parameters: + # [key]: [value] + - ${{ if ne(variable.template, '') }}: + - template: ${{ variable.template }} + ${{ if ne(variable.parameters, '') }}: + parameters: ${{ variable.parameters }} + + # handle key-value variable syntax. + # example: + # - [key]: [value] + - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: + - ${{ each pair in variable }}: + - name: ${{ pair.key }} + value: ${{ pair.value }} + + # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds + - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: DotNet-HelixApi-Access + + ${{ if ne(parameters.workspace, '') }}: + workspace: ${{ parameters.workspace }} + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if ne(parameters.preSteps, '') }}: + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if eq(parameters.enableMicrobuild, 'true') }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + env: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) + + - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: + - task: NuGetAuthenticate@1 + + - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: + - task: DownloadPipelineArtifact@2 + inputs: + buildType: current + artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} + targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} + itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} + + - ${{ each step in parameters.steps }}: + - ${{ step }} + + - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: + - task: RichCodeNavIndexer@0 + displayName: RichCodeNav Upload + inputs: + languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} + environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'internal') }} + richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin + uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} + continueOnError: true + + - template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ if eq(parameters.disableComponentGovernance, '') }}: + ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: + disableComponentGovernance: false + ${{ else }}: + disableComponentGovernance: true + ${{ else }}: + disableComponentGovernance: ${{ parameters.disableComponentGovernance }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + + - ${{ if eq(parameters.enableMicrobuild, 'true') }}: + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - task: MicroBuildCleanup@1 + displayName: Execute Microbuild cleanup tasks + condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + env: + TeamName: $(_TeamName) + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: + - template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + PackageVersion: ${{ parameters.packageVersion}} + BuildDropPath: ${{ parameters.buildDropPath }} + IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} + publishArtifacts: false + + # Publish test results + - ${{ if and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')) }}: + - ${{ if eq(parameters.testResultsFormat, 'xunit') }}: + - task: PublishTestResults@2 + displayName: Publish XUnit Test Results + inputs: + testResultsFormat: 'xUnit' + testResultsFiles: '*.xml' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + - ${{ if eq(parameters.testResultsFormat, 'vstest') }}: + - task: PublishTestResults@2 + displayName: Publish TRX Test Results + inputs: + testResultsFormat: 'VSTest' + testResultsFiles: '*.trx' + searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' + testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx + mergeTestResults: ${{ parameters.mergeTestResults }} + continueOnError: true + condition: always() + + # gather artifacts + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - task: CopyFiles@2 + displayName: Gather binaries for publish to artifacts + inputs: + SourceFolder: 'artifacts/bin' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' + - task: CopyFiles@2 + displayName: Gather packages for publish to artifacts + inputs: + SourceFolder: 'artifacts/packages' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - task: CopyFiles@2 + displayName: Gather logs for publish to artifacts + inputs: + SourceFolder: 'artifacts/log' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log' + + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: + - task: CopyFiles@2 + displayName: Gather logs for publish to artifacts + inputs: + SourceFolder: 'artifacts/log/$(_BuildConfig)' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - task: CopyFiles@2 + displayName: Gather buildconfiguration for build retry + inputs: + SourceFolder: '$(Build.SourcesDirectory)/eng/common/BuildConfiguration' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/eng/common/BuildConfiguration' + + - ${{ each step in parameters.artifactPublishSteps }}: + - ${{ step }} diff --git a/eng/common/core-templates/job/onelocbuild.yml b/eng/common/core-templates/job/onelocbuild.yml new file mode 100644 index 0000000000000..00feec8ebbc3a --- /dev/null +++ b/eng/common/core-templates/job/onelocbuild.yml @@ -0,0 +1,121 @@ +parameters: + # Optional: dependencies of the job + dependsOn: '' + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: '' + + CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex + GithubPat: $(BotAccount-dotnet-bot-repo-PAT) + + SourcesDirectory: $(Build.SourcesDirectory) + CreatePr: true + AutoCompletePr: false + ReusePr: true + UseLfLineEndings: true + UseCheckedInLocProjectJson: false + SkipLocProjectJsonGeneration: false + LanguageSet: VS_Main_Languages + LclSource: lclFilesInRepo + LclPackageId: '' + RepoType: gitHub + GitHubOrg: dotnet + MirrorRepo: '' + MirrorBranch: main + condition: '' + JobNameSuffix: '' + is1ESPipeline: '' +jobs: +- job: OneLocBuild${{ parameters.JobNameSuffix }} + + dependsOn: ${{ parameters.dependsOn }} + + displayName: OneLocBuild${{ parameters.JobNameSuffix }} + + variables: + - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat + - name: _GenerateLocProjectArguments + value: -SourcesDirectory ${{ parameters.SourcesDirectory }} + -LanguageSet "${{ parameters.LanguageSet }}" + -CreateNeutralXlfs + - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: + - name: _GenerateLocProjectArguments + value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.pool, '') }}: + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: + - task: Powershell@2 + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 + arguments: $(_GenerateLocProjectArguments) + displayName: Generate LocProject.json + condition: ${{ parameters.condition }} + + - task: OneLocBuild@2 + displayName: OneLocBuild + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + inputs: + locProj: eng/Localize/LocProject.json + outDir: $(Build.ArtifactStagingDirectory) + lclSource: ${{ parameters.LclSource }} + lclPackageId: ${{ parameters.LclPackageId }} + isCreatePrSelected: ${{ parameters.CreatePr }} + isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} + ${{ if eq(parameters.CreatePr, true) }}: + isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + isShouldReusePrSelected: ${{ parameters.ReusePr }} + packageSourceAuth: patAuth + patVariable: ${{ parameters.CeapexPat }} + ${{ if eq(parameters.RepoType, 'gitHub') }}: + repoType: ${{ parameters.RepoType }} + gitHubPatVariable: "${{ parameters.GithubPat }}" + ${{ if ne(parameters.MirrorRepo, '') }}: + isMirrorRepoSelected: true + gitHubOrganization: ${{ parameters.GitHubOrg }} + mirrorRepo: ${{ parameters.MirrorRepo }} + mirrorBranch: ${{ parameters.MirrorBranch }} + condition: ${{ parameters.condition }} + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish Localization Files + pathToPublish: '$(Build.ArtifactStagingDirectory)/loc' + publishLocation: Container + artifactName: Loc + condition: ${{ parameters.condition }} + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish LocProject.json + pathToPublish: '$(Build.SourcesDirectory)/eng/Localize/' + publishLocation: Container + artifactName: Loc + condition: ${{ parameters.condition }} \ No newline at end of file diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml new file mode 100644 index 0000000000000..8fe9299542c53 --- /dev/null +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -0,0 +1,172 @@ +parameters: + configuration: 'Debug' + + # Optional: condition for the job to run + condition: '' + + # Optional: 'true' if future jobs should run even if this job fails + continueOnError: false + + # Optional: dependencies of the job + dependsOn: '' + + # Optional: Include PublishBuildArtifacts task + enablePublishBuildArtifacts: false + + # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool + pool: {} + + # Optional: should run as a public build even in the internal project + # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. + runAsPublic: false + + # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing + publishUsingPipelines: false + + # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing + publishAssetsImmediately: false + + artifactsPublishingAdditionalParameters: '' + + signingValidationAdditionalParameters: '' + + is1ESPipeline: '' + +jobs: +- job: Asset_Registry_Publish + + dependsOn: ${{ parameters.dependsOn }} + timeoutInMinutes: 150 + + ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: + displayName: Publish Assets + ${{ else }}: + displayName: Publish to Build Asset Registry + + variables: + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: Publish-Build-Assets + - group: AzureDevOps-Artifact-Feeds-Pats + - name: runCodesignValidationInjection + value: false + # unconditional - needed for logs publishing (redactor tool version) + - template: /eng/common/core-templates/post-build/common-variables.yml + + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: + name: NetCore1ESPool-Publishing-Internal + image: windows.vs2019.amd64 + os: windows + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - checkout: self + fetchDepth: 3 + clean: true + + - task: DownloadBuildArtifacts@0 + displayName: Download artifact + inputs: + artifactName: AssetManifests + downloadPath: '$(Build.StagingDirectory)/Download' + checkDownloadedFiles: true + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + + - task: NuGetAuthenticate@1 + + - task: PowerShell@2 + displayName: Publish Build Assets + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet + /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' + /p:BuildAssetRegistryToken=$(MaestroAccessToken) + /p:MaestroApiEndpoint=https://maestro.dot.net + /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} + /p:OfficialBuildId=$(Build.BuildNumber) + condition: ${{ parameters.condition }} + continueOnError: ${{ parameters.continueOnError }} + + - task: powershell@2 + displayName: Create ReleaseConfigs Artifact + inputs: + targetType: inline + script: | + New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force + $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt" + Add-Content -Path $filePath -Value $(BARBuildId) + Add-Content -Path $filePath -Value "$(DefaultChannels)" + Add-Content -Path $filePath -Value $(IsStableBuild) + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish ReleaseConfigs Artifact + pathToPublish: '$(Build.StagingDirectory)/ReleaseConfigs' + publishLocation: Container + artifactName: ReleaseConfigs + + - task: powershell@2 + displayName: Check if SymbolPublishingExclusionsFile.txt exists + inputs: + targetType: inline + script: | + $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + if(Test-Path -Path $symbolExclusionfile) + { + Write-Host "SymbolExclusionFile exists" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" + } + else{ + Write-Host "Symbols Exclusion file does not exist" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" + } + + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish SymbolPublishingExclusionsFile Artifact + condition: eq(variables['SymbolExclusionFile'], 'true') + pathToPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' + publishLocation: Container + artifactName: ReleaseConfigs + + - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: PowerShell@2 + displayName: Publish Using Darc + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) + -PublishingInfraVersion 3 + -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' + -MaestroToken '$(MaestroApiAccessToken)' + -WaitPublishingFinish true + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + JobLabel: 'Publish_Artifacts_Logs' diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml new file mode 100644 index 0000000000000..c0ce4b3c86186 --- /dev/null +++ b/eng/common/core-templates/job/source-build.yml @@ -0,0 +1,80 @@ +parameters: + # This template adds arcade-powered source-build to CI. The template produces a server job with a + # default ID 'Source_Build_Complete' to put in a dependency list if necessary. + + # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. + jobNamePrefix: 'Source_Build' + + # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for + # managed-only repositories. This is an object with these properties: + # + # name: '' + # The name of the job. This is included in the job ID. + # targetRID: '' + # The name of the target RID to use, instead of the one auto-detected by Arcade. + # nonPortable: false + # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than + # linux-x64), and compiling against distro-provided packages rather than portable ones. + # skipPublishValidation: false + # Disables publishing validation. By default, a check is performed to ensure no packages are + # published by source-build. + # container: '' + # A container to use. Runs in docker. + # pool: {} + # A pool to use. Runs directly on an agent. + # buildScript: '' + # Specifies the build script to invoke to perform the build in the repo. The default + # './build.sh' should work for typical Arcade repositories, but this is customizable for + # difficult situations. + # jobProperties: {} + # A list of job properties to inject at the top level, for potential extensibility beyond + # container and pool. + platform: {} + + is1ESPipeline: '' + +jobs: +- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} + displayName: Source-Build (${{ parameters.platform.name }}) + + ${{ each property in parameters.platform.jobProperties }}: + ${{ property.key }}: ${{ property.value }} + + ${{ if ne(parameters.platform.container, '') }}: + container: ${{ parameters.platform.container }} + + ${{ if eq(parameters.platform.pool, '') }}: + # The default VM host AzDO pool. This should be capable of running Docker containers: almost all + # source-build builds run in Docker, including the default managed platform. + # /eng/common/core-templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic + ${{ if eq(parameters.is1ESPipeline, 'true') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] + demands: ImageOverride -equals build.ubuntu.2004.amd64 + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] + image: 1es-mariner-2 + os: linux + ${{ else }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64.Open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] + demands: ImageOverride -equals Build.Ubuntu.2204.Amd64 + ${{ if ne(parameters.platform.pool, '') }}: + pool: ${{ parameters.platform.pool }} + + workspace: + clean: all + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + platform: ${{ parameters.platform }} diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml new file mode 100644 index 0000000000000..f1938eec10203 --- /dev/null +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -0,0 +1,91 @@ +parameters: + runAsPublic: false + sourceIndexUploadPackageVersion: 2.0.0-20240502.12 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2 + sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json + sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" + preSteps: [] + binlogPath: artifacts/log/Debug/Build.binlog + condition: '' + dependsOn: '' + pool: '' + is1ESPipeline: '' + +jobs: +- job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} + variables: + - name: SourceIndexUploadPackageVersion + value: ${{ parameters.sourceIndexUploadPackageVersion }} + - name: SourceIndexProcessBinlogPackageVersion + value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }} + - name: SourceIndexPackageSource + value: ${{ parameters.sourceIndexPackageSource }} + - name: BinlogPath + value: ${{ parameters.binlogPath }} + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + ${{ if ne(parameters.pool, '') }}: + pool: ${{ parameters.pool }} + ${{ if eq(parameters.pool, '') }}: + pool: + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $(DncEngPublicBuildPool) + image: windows.vs2022.amd64.open + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $(DncEngInternalBuildPool) + image: windows.vs2022.amd64 + + steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - task: UseDotNet@2 + displayName: Use .NET 8 SDK + inputs: + packageType: sdk + version: 8.0.x + installationPath: $(Agent.TempDirectory)/dotnet + workingDirectory: $(Agent.TempDirectory) + + - script: | + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + displayName: Download Tools + # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. + workingDirectory: $(Agent.TempDirectory) + + - script: ${{ parameters.sourceIndexBuildCommand }} + displayName: Build Repository + + - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output + displayName: Process Binlog into indexable sln + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - task: AzureCLI@2 + displayName: Get stage 1 auth token + inputs: + azureSubscription: 'SourceDotNet Stage1 Publish' + addSpnToEnvironment: true + scriptType: 'ps' + scriptLocation: 'inlineScript' + inlineScript: | + echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId" + echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken" + echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId" + + - script: | + echo "Client ID: $(ARM_CLIENT_ID)" + echo "ID Token: $(ARM_ID_TOKEN)" + echo "Tenant ID: $(ARM_TENANT_ID)" + az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN) + displayName: "Login to Azure" + + - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1 + displayName: Upload stage1 artifacts to source index \ No newline at end of file diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml new file mode 100644 index 0000000000000..f2144252cc65c --- /dev/null +++ b/eng/common/core-templates/jobs/codeql-build.yml @@ -0,0 +1,33 @@ +parameters: + # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md + continueOnError: false + # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + jobs: [] + # Optional: if specified, restore and use this version of Guardian instead of the default. + overrideGuardianVersion: '' + is1ESPipeline: '' + +jobs: +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + enableMicrobuild: false + enablePublishBuildArtifacts: false + enablePublishTestResults: false + enablePublishBuildAssets: false + enablePublishUsingPipelines: false + enableTelemetry: true + + variables: + - group: Publish-Build-Assets + # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in + # sync with the packages.config file. + - name: DefaultGuardianVersion + value: 0.109.0 + - name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config + - name: GuardianVersion + value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} + + jobs: ${{ parameters.jobs }} + diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml new file mode 100644 index 0000000000000..ea69be4341c62 --- /dev/null +++ b/eng/common/core-templates/jobs/jobs.yml @@ -0,0 +1,119 @@ +parameters: + # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md + continueOnError: false + + # Optional: Include PublishBuildArtifacts task + enablePublishBuildArtifacts: false + + # Optional: Enable publishing using release pipelines + enablePublishUsingPipelines: false + + # Optional: Enable running the source-build jobs to build repo from source + enableSourceBuild: false + + # Optional: Parameters for source-build template. + # See /eng/common/core-templates/jobs/source-build.yml for options + sourceBuildParameters: [] + + graphFileGeneration: + # Optional: Enable generating the graph files at the end of the build + enabled: false + # Optional: Include toolset dependencies in the generated graph files + includeToolset: false + + # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job + jobs: [] + + # Optional: Override automatically derived dependsOn value for "publish build assets" job + publishBuildAssetsDependsOn: '' + + # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. + publishAssetsImmediately: false + + # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) + artifactsPublishingAdditionalParameters: '' + signingValidationAdditionalParameters: '' + + # Optional: should run as a public build even in the internal project + # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. + runAsPublic: false + + enableSourceIndex: false + sourceIndexParams: {} + + artifacts: {} + is1ESPipeline: '' + +# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, +# and some (Microbuild) should only be applied to non-PR cases for internal builds. + +jobs: +- ${{ each job in parameters.jobs }}: + - ${{ if eq(parameters.is1ESPipeline, 'true') }}: + - template: /eng/common/templates-official/job/job.yml + parameters: + # pass along parameters + ${{ each parameter in parameters }}: + ${{ if ne(parameter.key, 'jobs') }}: + ${{ parameter.key }}: ${{ parameter.value }} + + # pass along job properties + ${{ each property in job }}: + ${{ if ne(property.key, 'job') }}: + ${{ property.key }}: ${{ property.value }} + + name: ${{ job.job }} + + - ${{ else }}: + - template: /eng/common/templates/job/job.yml + parameters: + # pass along parameters + ${{ each parameter in parameters }}: + ${{ if ne(parameter.key, 'jobs') }}: + ${{ parameter.key }}: ${{ parameter.value }} + + # pass along job properties + ${{ each property in job }}: + ${{ if ne(property.key, 'job') }}: + ${{ property.key }}: ${{ property.value }} + + name: ${{ job.job }} + +- ${{ if eq(parameters.enableSourceBuild, true) }}: + - template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + allCompletedJobId: Source_Build_Complete + ${{ each parameter in parameters.sourceBuildParameters }}: + ${{ parameter.key }}: ${{ parameter.value }} + +- ${{ if eq(parameters.enableSourceIndex, 'true') }}: + - template: ../job/source-index-stage1.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + runAsPublic: ${{ parameters.runAsPublic }} + ${{ each parameter in parameters.sourceIndexParams }}: + ${{ parameter.key }}: ${{ parameter.value }} + +- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: + - template: ../job/publish-build-assets.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + continueOnError: ${{ parameters.continueOnError }} + dependsOn: + - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: + - ${{ each job in parameters.publishBuildAssetsDependsOn }}: + - ${{ job.job }} + - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: + - ${{ each job in parameters.jobs }}: + - ${{ job.job }} + - ${{ if eq(parameters.enableSourceBuild, true) }}: + - Source_Build_Complete + + runAsPublic: ${{ parameters.runAsPublic }} + publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} + publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} + enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} + artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} + signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} diff --git a/eng/common/core-templates/jobs/source-build.yml b/eng/common/core-templates/jobs/source-build.yml new file mode 100644 index 0000000000000..d8e5d00852268 --- /dev/null +++ b/eng/common/core-templates/jobs/source-build.yml @@ -0,0 +1,50 @@ +parameters: + # This template adds arcade-powered source-build to CI. A job is created for each platform, as + # well as an optional server job that completes when all platform jobs complete. + + # The name of the "join" job for all source-build platforms. If set to empty string, the job is + # not included. Existing repo pipelines can use this job depend on all source-build jobs + # completing without maintaining a separate list of every single job ID: just depend on this one + # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. + allCompletedJobId: '' + + # See /eng/common/core-templates/job/source-build.yml + jobNamePrefix: 'Source_Build' + + # This is the default platform provided by Arcade, intended for use by a managed-only repo. + defaultManagedPlatform: + name: 'Managed' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + + # Defines the platforms on which to run build jobs. One job is created for each platform, and the + # object in this array is sent to the job template as 'platform'. If no platforms are specified, + # one job runs on 'defaultManagedPlatform'. + platforms: [] + + is1ESPipeline: '' + +jobs: + +- ${{ if ne(parameters.allCompletedJobId, '') }}: + - job: ${{ parameters.allCompletedJobId }} + displayName: Source-Build Complete + pool: server + dependsOn: + - ${{ each platform in parameters.platforms }}: + - ${{ parameters.jobNamePrefix }}_${{ platform.name }} + - ${{ if eq(length(parameters.platforms), 0) }}: + - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} + +- ${{ each platform in parameters.platforms }}: + - template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobNamePrefix: ${{ parameters.jobNamePrefix }} + platform: ${{ platform }} + +- ${{ if eq(length(parameters.platforms), 0) }}: + - template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobNamePrefix: ${{ parameters.jobNamePrefix }} + platform: ${{ parameters.defaultManagedPlatform }} diff --git a/eng/common/core-templates/post-build/common-variables.yml b/eng/common/core-templates/post-build/common-variables.yml new file mode 100644 index 0000000000000..b9ede10bf099a --- /dev/null +++ b/eng/common/core-templates/post-build/common-variables.yml @@ -0,0 +1,24 @@ +variables: + - group: Publish-Build-Assets + + # Whether the build is internal or not + - name: IsInternalBuild + value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} + + # Default Maestro++ API Endpoint and API Version + - name: MaestroApiEndPoint + value: "https://maestro.dot.net" + - name: MaestroApiAccessToken + value: $(MaestroAccessToken) + - name: MaestroApiVersion + value: "2020-02-20" + + - name: SourceLinkCLIVersion + value: 3.0.0 + - name: SymbolToolVersion + value: 1.0.1 + - name: BinlogToolVersion + value: 1.0.11 + + - name: runCodesignValidationInjection + value: false diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml new file mode 100644 index 0000000000000..865bc1ecb4f0f --- /dev/null +++ b/eng/common/core-templates/post-build/post-build.yml @@ -0,0 +1,314 @@ +parameters: + # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. + # Publishing V1 is no longer supported + # Publishing V2 is no longer supported + # Publishing V3 is the default + - name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + + - name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + + - name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + + - name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + + - name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + + - name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + + - name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + + - name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + + - name: SDLValidationParameters + type: object + default: + enable: false + publishGdn: false + continueOnError: false + params: '' + artifactNames: '' + downloadArtifacts: true + + # These parameters let the user customize the call to sdk-task.ps1 for publishing + # symbols & general artifacts as well as for signing validation + - name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + + - name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + + - name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + + # Which stages should finish execution before post-build stages start + - name: validateDependsOn + type: object + default: + - build + + - name: publishDependsOn + type: object + default: + - Validate + + # Optional: Call asset publishing rather than running in a separate stage + - name: publishAssetsImmediately + type: boolean + default: false + + - name: is1ESPipeline + type: boolean + default: false + +stages: +- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + - stage: Validate + dependsOn: ${{ parameters.validateDependsOn }} + displayName: Validate Build Assets + variables: + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobs: + - job: + displayName: NuGet Validation + condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: windows.vs2022.amd64 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ + + - job: + displayName: Signing Validation + condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + itemPattern: | + ** + !**/Microsoft.SourceBuild.Intermediate.*.nupkg + + # This is necessary whenever we want to publish/restore to an AzDO private feed + # Since sdk-task.ps1 tries to restore packages we need to do this authentication here + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore -msbuildEngine vs + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) + + - job: + displayName: SourceLink Validation + condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: $(DncEngInternalBuildPool) + image: 1es-windows-2022 + os: windows + ${{ else }}: + name: $(DncEngInternalBuildPool) + demands: ImageOverride -equals windows.vs2022.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: DownloadBuildArtifacts@0 + displayName: Download Blob Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: BlobArtifacts + checkDownloadedFiles: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 + arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ + -ExtractPath $(Agent.BuildDirectory)/Extract/ + -GHRepoName $(Build.Repository.Name) + -GHCommit $(Build.SourceVersion) + -SourcelinkCliVersion $(SourceLinkCLIVersion) + continueOnError: true + +- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: + - stage: publish_using_darc + ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + dependsOn: ${{ parameters.publishDependsOn }} + ${{ else }}: + dependsOn: ${{ parameters.validateDependsOn }} + displayName: Publish using Darc + variables: + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + jobs: + - job: + displayName: Publish Using Darc + timeoutInMinutes: 120 + pool: + # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + name: AzurePipelines-EO + image: 1ESPT-Windows2022 + demands: Cmd + os: windows + # If it's not devdiv, it's dnceng + ${{ else }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: + name: NetCore1ESPool-Publishing-Internal + image: windows.vs2019.amd64 + os: windows + ${{ else }}: + name: NetCore1ESPool-Publishing-Internal + demands: ImageOverride -equals windows.vs2019.amd64 + steps: + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - task: NuGetAuthenticate@1 + + - task: PowerShell@2 + displayName: Publish Using Darc + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: -BuildId $(BARBuildId) + -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} + -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' + -MaestroToken '$(MaestroApiAccessToken)' + -WaitPublishingFinish true + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' diff --git a/eng/common/core-templates/post-build/setup-maestro-vars.yml b/eng/common/core-templates/post-build/setup-maestro-vars.yml new file mode 100644 index 0000000000000..8d56b5726793f --- /dev/null +++ b/eng/common/core-templates/post-build/setup-maestro-vars.yml @@ -0,0 +1,74 @@ +parameters: + BARBuildId: '' + PromoteToChannelIds: '' + is1ESPipeline: '' + +steps: + - ${{ if eq(parameters.is1ESPipeline, '') }}: + - 'Illegal entry point, is1ESPipeline is not defined. Repository yaml should not directly reference templates in core-templates folder.': error + + - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Release Configs + inputs: + buildType: current + artifactName: ReleaseConfigs + checkDownloadedFiles: true + + - task: PowerShell@2 + name: setReleaseVars + displayName: Set Release Configs Vars + inputs: + targetType: inline + pwsh: true + script: | + try { + if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { + $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt + + $BarId = $Content | Select -Index 0 + $Channels = $Content | Select -Index 1 + $IsStableBuild = $Content | Select -Index 2 + + $AzureDevOpsProject = $Env:System_TeamProject + $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId + $AzureDevOpsBuildId = $Env:Build_BuildId + } + else { + $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" + + $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' + $apiHeaders.Add('Accept', 'application/json') + $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") + + $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } + + $BarId = $Env:BARBuildId + $Channels = $Env:PromoteToMaestroChannels -split "," + $Channels = $Channels -join "][" + $Channels = "[$Channels]" + + $IsStableBuild = $buildInfo.stable + $AzureDevOpsProject = $buildInfo.azureDevOpsProject + $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId + $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId + } + + Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" + Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" + Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" + + Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" + Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" + Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" + } + catch { + Write-Host $_ + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + exit 1 + } + env: + MAESTRO_API_TOKEN: $(MaestroApiAccessToken) + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} diff --git a/eng/common/core-templates/post-build/trigger-subscription.yml b/eng/common/core-templates/post-build/trigger-subscription.yml new file mode 100644 index 0000000000000..da669030daf6e --- /dev/null +++ b/eng/common/core-templates/post-build/trigger-subscription.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Triggering subscriptions + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1 + arguments: -SourceRepo $(Build.Repository.Uri) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/core-templates/steps/add-build-to-channel.yml b/eng/common/core-templates/steps/add-build-to-channel.yml new file mode 100644 index 0000000000000..f67a210d62f3e --- /dev/null +++ b/eng/common/core-templates/steps/add-build-to-channel.yml @@ -0,0 +1,13 @@ +parameters: + ChannelId: 0 + +steps: +- task: PowerShell@2 + displayName: Add Build to Channel + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 + arguments: -BuildId $(BARBuildId) + -ChannelId ${{ parameters.ChannelId }} + -MaestroApiAccessToken $(MaestroApiAccessToken) + -MaestroApiEndPoint $(MaestroApiEndPoint) + -MaestroApiVersion $(MaestroApiVersion) diff --git a/eng/common/core-templates/steps/component-governance.yml b/eng/common/core-templates/steps/component-governance.yml new file mode 100644 index 0000000000000..df449a34c1120 --- /dev/null +++ b/eng/common/core-templates/steps/component-governance.yml @@ -0,0 +1,14 @@ +parameters: + disableComponentGovernance: false + componentGovernanceIgnoreDirectories: '' + is1ESPipeline: false + +steps: +- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: + - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" + displayName: Set skipComponentGovernanceDetection variable +- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: + - task: ComponentGovernanceComponentDetection@0 + continueOnError: true + inputs: + ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml new file mode 100644 index 0000000000000..d938b60e1bb53 --- /dev/null +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -0,0 +1,54 @@ +# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. +# PackageName - The name of the package this SBOM represents. +# PackageVersion - The version of the package this SBOM represents. +# ManifestDirPath - The path of the directory where the generated manifest files will be placed +# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. + +parameters: + PackageVersion: 9.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageName: '.NET' + ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom + IgnoreDirectories: '' + sbomContinueOnError: true + is1ESPipeline: false + # disable publishArtifacts if some other step is publishing the artifacts (like job.yml). + publishArtifacts: true + +steps: +- task: PowerShell@2 + displayName: Prep for SBOM generation in (Non-linux) + condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) + inputs: + filePath: ./eng/common/generate-sbom-prep.ps1 + arguments: ${{parameters.manifestDirPath}} + +# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 +- script: | + chmod +x ./eng/common/generate-sbom-prep.sh + ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} + displayName: Prep for SBOM generation in (Linux) + condition: eq(variables['Agent.Os'], 'Linux') + continueOnError: ${{ parameters.sbomContinueOnError }} + +- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: 'Generate SBOM manifest' + continueOnError: ${{ parameters.sbomContinueOnError }} + inputs: + PackageName: ${{ parameters.packageName }} + BuildDropPath: ${{ parameters.buildDropPath }} + PackageVersion: ${{ parameters.packageVersion }} + ManifestDirPath: ${{ parameters.manifestDirPath }} + ${{ if ne(parameters.IgnoreDirectories, '') }}: + AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' + +- ${{ if eq(parameters.publishArtifacts, 'true')}}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish SBOM manifest + continueOnError: ${{parameters.sbomContinueOnError}} + targetPath: '${{ parameters.manifestDirPath }}' + artifactName: $(ARTIFACT_NAME) + diff --git a/eng/common/core-templates/steps/publish-build-artifacts.yml b/eng/common/core-templates/steps/publish-build-artifacts.yml new file mode 100644 index 0000000000000..f24ce346684e6 --- /dev/null +++ b/eng/common/core-templates/steps/publish-build-artifacts.yml @@ -0,0 +1,20 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false +- name: args + type: object + default: {} +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - template: /eng/common/templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ each parameter in parameters.args }}: + ${{ parameter.key }}: ${{ parameter.value }} +- ${{ else }}: + - template: /eng/common/templates-official/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + ${{ each parameter in parameters.args }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml new file mode 100644 index 0000000000000..8c5ea77b586d2 --- /dev/null +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -0,0 +1,59 @@ +parameters: + StageLabel: '' + JobLabel: '' + CustomSensitiveDataList: '' + # A default - in case value from eng/common/core-templates/post-build/common-variables.yml is not passed + BinlogToolVersion: '1.0.11' + is1ESPipeline: false + +steps: +- task: Powershell@2 + displayName: Prepare Binlogs to Upload + inputs: + targetType: inline + script: | + New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ + continueOnError: true + condition: always() + +- task: PowerShell@2 + displayName: Redact Logs + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/post-build/redact-logs.ps1 + # For now this needs to have explicit list of all sensitive data. Taken from eng/publishing/v3/publish.yml + # Sensitive data can as well be added to $(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + # If the file exists - sensitive data for redaction will be sourced from it + # (single entry per line, lines starting with '# ' are considered comments and skipped) + arguments: -InputPath '$(Build.SourcesDirectory)/PostBuildLogs' + -BinlogToolVersion ${{parameters.BinlogToolVersion}} + -TokensFilePath '$(Build.SourcesDirectory)/eng/BinlogSecretsRedactionFile.txt' + '$(publishing-dnceng-devdiv-code-r-build-re)' + '$(MaestroAccessToken)' + '$(dn-bot-all-orgs-artifact-feeds-rw)' + '$(akams-client-id)' + '$(akams-client-secret)' + '$(microsoft-symbol-server-pat)' + '$(symweb-symbol-server-pat)' + '$(dn-bot-all-orgs-build-rw-code-rw)' + ${{parameters.CustomSensitiveDataList}} + continueOnError: true + condition: always() + +- task: CopyFiles@2 + displayName: Gather post build logs + inputs: + SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + +- template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish Logs + pathToPublish: '$(Build.ArtifactStagingDirectory)/PostBuildLogs' + publishLocation: Container + artifactName: PostBuildLogs + continueOnError: true + condition: always() diff --git a/eng/common/core-templates/steps/publish-pipeline-artifacts.yml b/eng/common/core-templates/steps/publish-pipeline-artifacts.yml new file mode 100644 index 0000000000000..2efec04dc2c16 --- /dev/null +++ b/eng/common/core-templates/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,20 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: args + type: object + default: {} + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - template: /eng/common/templates/steps/publish-pipeline-artifacts.yml + parameters: + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} +- ${{ else }}: + - template: /eng/common/templates-official/steps/publish-pipeline-artifacts.yml + parameters: + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/core-templates/steps/retain-build.yml b/eng/common/core-templates/steps/retain-build.yml new file mode 100644 index 0000000000000..83d97a26a01ff --- /dev/null +++ b/eng/common/core-templates/steps/retain-build.yml @@ -0,0 +1,28 @@ +parameters: + # Optional azure devops PAT with build execute permissions for the build's organization, + # only needed if the build that should be retained ran on a different organization than + # the pipeline where this template is executing from + Token: '' + # Optional BuildId to retain, defaults to the current running build + BuildId: '' + # Azure devops Organization URI for the build in the https://dev.azure.com/ format. + # Defaults to the organization the current pipeline is running on + AzdoOrgUri: '$(System.CollectionUri)' + # Azure devops project for the build. Defaults to the project the current pipeline is running on + AzdoProject: '$(System.TeamProject)' + +steps: + - task: powershell@2 + inputs: + targetType: 'filePath' + filePath: eng/common/retain-build.ps1 + pwsh: true + arguments: > + -AzdoOrgUri: ${{parameters.AzdoOrgUri}} + -AzdoProject ${{parameters.AzdoProject}} + -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} + -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} + displayName: Enable permanent build retention + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + BUILD_ID: $(Build.BuildId) \ No newline at end of file diff --git a/eng/common/core-templates/steps/send-to-helix.yml b/eng/common/core-templates/steps/send-to-helix.yml new file mode 100644 index 0000000000000..68fa739c4ab21 --- /dev/null +++ b/eng/common/core-templates/steps/send-to-helix.yml @@ -0,0 +1,93 @@ +# Please remember to update the documentation if you make changes to these parameters! +parameters: + HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ + HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' + HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number + HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues + HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group + HelixProjectPath: 'eng/common/helixpublish.proj' # optional -- path to the project file to build relative to BUILD_SOURCESDIRECTORY + HelixProjectArguments: '' # optional -- arguments passed to the build command + HelixConfiguration: '' # optional -- additional property attached to a job + HelixPreCommands: '' # optional -- commands to run before Helix work item execution + HelixPostCommands: '' # optional -- commands to run after Helix work item execution + WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects + WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects + WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects + CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload + XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true + XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects + XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects + XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner + XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects + IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion + DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." + IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set + HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) + Creator: '' # optional -- if the build is external, use this to specify who is sending the job + DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO + condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() + continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false + +steps: + - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' + displayName: ${{ parameters.DisplayNamePrefix }} (Windows) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + WorkItemCommand: ${{ parameters.WorkItemCommand }} + WorkItemTimeout: ${{ parameters.WorkItemTimeout }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + XUnitProjects: ${{ parameters.XUnitProjects }} + XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} + XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} + XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} + XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + HelixBaseUri: ${{ parameters.HelixBaseUri }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/${{ parameters.HelixProjectPath }} /restore /p:TreatWarningsAsErrors=false ${{ parameters.HelixProjectArguments }} /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog + displayName: ${{ parameters.DisplayNamePrefix }} (Unix) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixConfiguration: ${{ parameters.HelixConfiguration }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + WorkItemCommand: ${{ parameters.WorkItemCommand }} + WorkItemTimeout: ${{ parameters.WorkItemTimeout }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + XUnitProjects: ${{ parameters.XUnitProjects }} + XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} + XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} + XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} + XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + HelixBaseUri: ${{ parameters.HelixBaseUri }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml new file mode 100644 index 0000000000000..bdd725b496f91 --- /dev/null +++ b/eng/common/core-templates/steps/source-build.yml @@ -0,0 +1,134 @@ +parameters: + # This template adds arcade-powered source-build to CI. + + # This is a 'steps' template, and is intended for advanced scenarios where the existing build + # infra has a careful build methodology that must be followed. For example, a repo + # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline + # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to + # GitHub. Using this steps template leaves room for that infra to be included. + + # Defines the platform on which to run the steps. See 'eng/common/core-templates/job/source-build.yml' + # for details. The entire object is described in the 'job' template for simplicity, even though + # the usage of the properties on this object is split between the 'job' and 'steps' templates. + platform: {} + is1ESPipeline: false + +steps: +# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) +- script: | + set -x + df -h + + # If building on the internal project, the artifact feeds variable may be available (usually only if needed) + # In that case, call the feed setup script to add internal feeds corresponding to public ones. + # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. + # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those + # changes. + internalRestoreArgs= + if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then + # Temporarily work around https://github.com/dotnet/arcade/issues/7709 + chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh + $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) + internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' + + # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. + # This only works if there is a username/email configured, which won't be the case in most CI runs. + git config --get user.email + if [ $? -ne 0 ]; then + git config user.email dn-bot@microsoft.com + git config user.name dn-bot + fi + fi + + # If building on the internal project, the internal storage variable may be available (usually only if needed) + # In that case, add variables to allow the download of internal runtimes if the specified versions are not found + # in the default public locations. + internalRuntimeDownloadArgs= + if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then + internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' + fi + + buildConfig=Release + # Check if AzDO substitutes in a build config from a variable, and use it if so. + if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then + buildConfig='$(_BuildConfig)' + fi + + officialBuildArgs= + if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then + officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' + fi + + targetRidArgs= + if [ '${{ parameters.platform.targetRID }}' != '' ]; then + targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' + fi + + runtimeOsArgs= + if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then + runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' + fi + + baseOsArgs= + if [ '${{ parameters.platform.baseOS }}' != '' ]; then + baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' + fi + + publishArgs= + if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then + publishArgs='--publish' + fi + + assetManifestFileName=SourceBuild_RidSpecific.xml + if [ '${{ parameters.platform.name }}' != '' ]; then + assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml + fi + + ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ + --configuration $buildConfig \ + --restore --build --pack $publishArgs -bl \ + $officialBuildArgs \ + $internalRuntimeDownloadArgs \ + $internalRestoreArgs \ + $targetRidArgs \ + $runtimeOsArgs \ + $baseOsArgs \ + /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ + /p:ArcadeBuildFromSource=true \ + /p:DotNetBuildSourceOnly=true \ + /p:DotNetBuildRepo=true \ + /p:AssetManifestFileName=$assetManifestFileName + displayName: Build + +# Upload build logs for diagnosis. +- task: CopyFiles@2 + displayName: Prepare BuildLogs staging directory + inputs: + SourceFolder: '$(Build.SourcesDirectory)' + Contents: | + **/*.log + **/*.binlog + artifacts/sb/prebuilt-report/** + TargetFolder: '$(Build.StagingDirectory)/BuildLogs' + CleanTargetFolder: true + continueOnError: true + condition: succeededOrFailed() + +- template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + args: + displayName: Publish BuildLogs + targetPath: '$(Build.StagingDirectory)/BuildLogs' + artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) + continueOnError: true + condition: succeededOrFailed() + +# Manually inject component detection so that we can ignore the source build upstream cache, which contains +# a nupkg cache of input packages (a local feed). +# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' +# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets +- task: ComponentGovernanceComponentDetection@0 + displayName: Component Detection (Exclude upstream cache) + inputs: + ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' diff --git a/eng/common/core-templates/variables/pool-providers.yml b/eng/common/core-templates/variables/pool-providers.yml new file mode 100644 index 0000000000000..41053d382a2e1 --- /dev/null +++ b/eng/common/core-templates/variables/pool-providers.yml @@ -0,0 +1,8 @@ +parameters: + is1ESPipeline: false + +variables: + - ${{ if eq(parameters.is1ESPipeline, 'true') }}: + - template: /eng/common/templates-official/variables/pool-providers.yml + - ${{ else }}: + - template: /eng/common/templates/variables/pool-providers.yml \ No newline at end of file diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 9caf9b021dbdd..a8e35df7cee14 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -8,7 +8,7 @@ usage() echo "BuildArch can be: arm(default), arm64, armel, armv6, ppc64le, riscv64, s390x, x64, x86" echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine" echo " for alpine can be specified with version: alpineX.YY or alpineedge" - echo " for FreeBSD can be: freebsd12, freebsd13" + echo " for FreeBSD can be: freebsd13, freebsd14" echo " for illumos can be: illumos" echo " for Haiku can be: haiku." echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD" @@ -71,9 +71,9 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="12.4-RELEASE" +__FreeBSDBase="13.2-RELEASE" __FreeBSDPkg="1.17.0" -__FreeBSDABI="12" +__FreeBSDABI="13" __FreeBSDPackages="libunwind" __FreeBSDPackages+=" icu" __FreeBSDPackages+=" libinotify" @@ -142,7 +142,6 @@ while :; do case $lowerI in -\?|-h|--help) usage - exit 1 ;; arm) __BuildArch=arm @@ -182,12 +181,12 @@ while :; do __AlpinePackages="${__AlpinePackages// lldb-dev/}" __QEMUArch=riscv64 __UbuntuArch=riscv64 - __UbuntuRepo="http://deb.debian.org/debian-ports" + __UbuntuRepo="http://deb.debian.org/debian" __UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}" unset __LLDB_Package - if [[ -e "/usr/share/keyrings/debian-ports-archive-keyring.gpg" ]]; then - __Keyring="--keyring /usr/share/keyrings/debian-ports-archive-keyring.gpg --include=debian-ports-archive-keyring" + if [[ -e "/usr/share/keyrings/debian-archive-keyring.gpg" ]]; then + __Keyring="--keyring /usr/share/keyrings/debian-archive-keyring.gpg --include=debian-archive-keyring" fi ;; ppc64le) @@ -229,12 +228,19 @@ while :; do __UbuntuRepo="http://archive.ubuntu.com/ubuntu/" ;; lldb*) - version="${lowerI/lldb/}" - parts=(${version//./ }) + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + majorVersion="${version%%.*}" + + [ -z "${version##*.*}" ] && minorVersion="${version#*.}" + if [ -z "$minorVersion" ]; then + minorVersion=0 + fi # for versions > 6.0, lldb has dropped the minor version - if [[ "${parts[0]}" -gt 6 ]]; then - version="${parts[0]}" + if [ "$majorVersion" -le 6 ]; then + version="$majorVersion.$minorVersion" + else + version="$majorVersion" fi __LLDB_Package="liblldb-${version}-dev" @@ -243,15 +249,19 @@ while :; do unset __LLDB_Package ;; llvm*) - version="${lowerI/llvm/}" - parts=(${version//./ }) - __LLVM_MajorVersion="${parts[0]}" - __LLVM_MinorVersion="${parts[1]}" - - # for versions > 6.0, llvm has dropped the minor version - if [[ -z "$__LLVM_MinorVersion" && "$__LLVM_MajorVersion" -le 6 ]]; then - __LLVM_MinorVersion=0; + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + __LLVM_MajorVersion="${version%%.*}" + + [ -z "${version##*.*}" ] && __LLVM_MinorVersion="${version#*.}" + if [ -z "$__LLVM_MinorVersion" ]; then + __LLVM_MinorVersion=0 + fi + + # for versions > 6.0, lldb has dropped the minor version + if [ "$__LLVM_MajorVersion" -gt 6 ]; then + __LLVM_MinorVersion= fi + ;; xenial) # Ubuntu 16.04 if [[ "$__CodeName" != "jessie" ]]; then @@ -323,25 +333,24 @@ while :; do alpine*) __CodeName=alpine __UbuntuRepo= - version="${lowerI/alpine/}" - if [[ "$version" == "edge" ]]; then + if [[ "$lowerI" == "alpineedge" ]]; then __AlpineVersion=edge else - parts=(${version//./ }) - __AlpineMajorVersion="${parts[0]}" - __AlpineMinoVersion="${parts[1]}" - __AlpineVersion="$__AlpineMajorVersion.$__AlpineMinoVersion" + version="$(echo "$lowerI" | tr -d '[:alpha:]-=')" + __AlpineMajorVersion="${version%%.*}" + __AlpineMinorVersion="${version#*.}" + __AlpineVersion="$__AlpineMajorVersion.$__AlpineMinorVersion" fi ;; - freebsd12) + freebsd13) __CodeName=freebsd __SkipUnmount=1 ;; - freebsd13) + freebsd14) __CodeName=freebsd - __FreeBSDBase="13.2-RELEASE" - __FreeBSDABI="13" + __FreeBSDBase="14.0-RELEASE" + __FreeBSDABI="14" __SkipUnmount=1 ;; illumos) @@ -442,13 +451,39 @@ fi mkdir -p "$__RootfsDir" __RootfsDir="$( cd "$__RootfsDir" && pwd )" +__hasWget= +ensureDownloadTool() +{ + if command -v wget &> /dev/null; then + __hasWget=1 + elif command -v curl &> /dev/null; then + __hasWget=0 + else + >&2 echo "ERROR: either wget or curl is required by this script." + exit 1 + fi +} + if [[ "$__CodeName" == "alpine" ]]; then __ApkToolsVersion=2.12.11 - __ApkToolsSHA512SUM=53e57b49230da07ef44ee0765b9592580308c407a8d4da7125550957bb72cb59638e04f8892a18b584451c8d841d1c7cb0f0ab680cc323a3015776affaa3be33 __ApkToolsDir="$(mktemp -d)" __ApkKeysDir="$(mktemp -d)" + arch="$(uname -m)" - wget "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic//v$__ApkToolsVersion/x86_64/apk.static" -P "$__ApkToolsDir" + ensureDownloadTool + + if [[ "$__hasWget" == 1 ]]; then + wget -P "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static" + else + curl -SLO --create-dirs --output-dir "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static" + fi + if [[ "$arch" == "x86_64" ]]; then + __ApkToolsSHA512SUM="53e57b49230da07ef44ee0765b9592580308c407a8d4da7125550957bb72cb59638e04f8892a18b584451c8d841d1c7cb0f0ab680cc323a3015776affaa3be33" + elif [[ "$arch" == "aarch64" ]]; then + __ApkToolsSHA512SUM="9e2b37ecb2b56c05dad23d379be84fd494c14bd730b620d0d576bda760588e1f2f59a7fcb2f2080577e0085f23a0ca8eadd993b4e61c2ab29549fdb71969afd0" + else + echo "WARNING: add missing hash for your host architecture. To find the value, use: 'find /tmp -name apk.static -exec sha512sum {} \;'" + fi echo "$__ApkToolsSHA512SUM $__ApkToolsDir/apk.static" | sha512sum -c chmod +x "$__ApkToolsDir/apk.static" @@ -477,20 +512,23 @@ if [[ "$__CodeName" == "alpine" ]]; then fi # initialize DB + # shellcheck disable=SC2086 "$__ApkToolsDir/apk.static" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" --initdb add if [[ "$__AlpineLlvmLibsLookup" == 1 ]]; then + # shellcheck disable=SC2086 __AlpinePackages+=" $("$__ApkToolsDir/apk.static" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ -U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" \ - search 'llvm*-libs' | sort | tail -1 | sed 's/-[^-]*//2g')" + search 'llvm*-libs' | grep -E '^llvm' | sort | tail -1 | sed 's/-[^-]*//2g')" fi # install all packages in one go + # shellcheck disable=SC2086 "$__ApkToolsDir/apk.static" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \ -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \ @@ -501,12 +539,23 @@ if [[ "$__CodeName" == "alpine" ]]; then elif [[ "$__CodeName" == "freebsd" ]]; then mkdir -p "$__RootfsDir"/usr/local/etc JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} - wget -O - "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + + ensureDownloadTool + + if [[ "$__hasWget" == 1 ]]; then + wget -O- "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + else + curl -SL "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version + fi echo "ABI = \"FreeBSD:${__FreeBSDABI}:${__FreeBSDMachineArch}\"; FINGERPRINTS = \"${__RootfsDir}/usr/share/keys\"; REPOS_DIR = [\"${__RootfsDir}/etc/pkg\"]; REPO_AUTOUPDATE = NO; RUN_SCRIPTS = NO;" > "${__RootfsDir}"/usr/local/etc/pkg.conf echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"${__RootfsDir}/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf mkdir -p "$__RootfsDir"/tmp # get and build package manager - wget -O - "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + else + curl -SL "https://github.com/freebsd/pkg/archive/${__FreeBSDPkg}.tar.gz" | tar -C "$__RootfsDir"/tmp -zxf - + fi cd "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}" # needed for install to succeed mkdir -p "$__RootfsDir"/host/etc @@ -514,20 +563,36 @@ elif [[ "$__CodeName" == "freebsd" ]]; then rm -rf "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}" # install packages we need. INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf update + # shellcheck disable=SC2086 INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf install --yes $__FreeBSDPackages elif [[ "$__CodeName" == "illumos" ]]; then mkdir "$__RootfsDir/tmp" pushd "$__RootfsDir/tmp" JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} + + ensureDownloadTool + echo "Downloading sysroot." - wget -O - https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + else + curl -SL https://github.com/illumos/sysroot/releases/download/20181213-de6af22ae73b-v1/illumos-sysroot-i386-20181213-de6af22ae73b-v1.tar.gz | tar -C "$__RootfsDir" -xzf - + fi echo "Building binutils. Please wait.." - wget -O - https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + else + curl -SL https://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2 | tar -xjf - + fi mkdir build-binutils && cd build-binutils ../binutils-2.33.1/configure --prefix="$__RootfsDir" --target="${__illumosArch}-sun-solaris2.10" --program-prefix="${__illumosArch}-illumos-" --with-sysroot="$__RootfsDir" make -j "$JOBS" && make install && cd .. echo "Building gcc. Please wait.." - wget -O - https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + if [[ "$__hasWget" == 1 ]]; then + wget -O- https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + else + curl -SL https://ftp.gnu.org/gnu/gcc/gcc-8.4.0/gcc-8.4.0.tar.xz | tar -xJf - + fi CFLAGS="-fPIC" CXXFLAGS="-fPIC" CXXFLAGS_FOR_TARGET="-fPIC" @@ -544,7 +609,11 @@ elif [[ "$__CodeName" == "illumos" ]]; then fi BaseUrl="$BaseUrl/packages/SmartOS/trunk/${__illumosArch}/All" echo "Downloading manifest" - wget "$BaseUrl" + if [[ "$__hasWget" == 1 ]]; then + wget "$BaseUrl" + else + curl -SLO "$BaseUrl" + fi echo "Downloading dependencies." read -ra array <<<"$__IllumosPackages" for package in "${array[@]}"; do @@ -552,7 +621,11 @@ elif [[ "$__CodeName" == "illumos" ]]; then # find last occurrence of package in listing and extract its name package="$(sed -En '/.*href="('"$package"'-[0-9].*).tgz".*/h;$!d;g;s//\1/p' All)" echo "Resolved name '$package'" - wget "$BaseUrl"/"$package".tgz + if [[ "$__hasWget" == 1 ]]; then + wget "$BaseUrl"/"$package".tgz + else + curl -SLO "$BaseUrl"/"$package".tgz + fi ar -x "$package".tgz tar --skip-old-files -xzf "$package".tmp.tg* -C "$__RootfsDir" 2>/dev/null done @@ -561,10 +634,17 @@ elif [[ "$__CodeName" == "illumos" ]]; then rm -rf "$__RootfsDir"/{tmp,+*} mkdir -p "$__RootfsDir"/usr/include/net mkdir -p "$__RootfsDir"/usr/include/netpacket - wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h - wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h - wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h - wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + if [[ "$__hasWget" == 1 ]]; then + wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h + wget -P "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h + wget -P "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h + wget -P "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + else + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/bpf.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/net https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/io/bpf/net/dlt.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/netpacket https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/inet/sockmods/netpacket/packet.h + curl -SLO --create-dirs --output-dir "$__RootfsDir"/usr/include/sys https://raw.githubusercontent.com/illumos/illumos-gate/master/usr/src/uts/common/sys/sdt.h + fi elif [[ "$__CodeName" == "haiku" ]]; then JOBS=${MAXJOBS:="$(getconf _NPROCESSORS_ONLN)"} @@ -574,9 +654,16 @@ elif [[ "$__CodeName" == "haiku" ]]; then mkdir "$__RootfsDir/tmp/download" + ensureDownloadTool + echo "Downloading Haiku package tool" - git clone https://github.com/haiku/haiku-toolchains-ubuntu --depth 1 $__RootfsDir/tmp/script - wget -O "$__RootfsDir/tmp/download/hosttools.zip" $($__RootfsDir/tmp/script/fetch.sh --hosttools) + git clone https://github.com/haiku/haiku-toolchains-ubuntu --depth 1 "$__RootfsDir/tmp/script" + if [[ "$__hasWget" == 1 ]]; then + wget -O "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)" + else + curl -SLo "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)" + fi + unzip -o "$__RootfsDir/tmp/download/hosttools.zip" -d "$__RootfsDir/tmp/bin" DepotBaseUrl="https://depot.haiku-os.org/__api/v2/pkg/get-pkg" @@ -589,14 +676,25 @@ elif [[ "$__CodeName" == "haiku" ]]; then echo "Downloading $package..." # API documented here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L60 # The schema here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L598 - hpkgDownloadUrl="$(wget -qO- --post-data='{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ - --header='Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" - wget -P "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + if [[ "$__hasWget" == 1 ]]; then + hpkgDownloadUrl="$(wget -qO- --post-data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + wget -P "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + else + hpkgDownloadUrl="$(curl -sSL -XPOST --data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \ + --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')" + curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$hpkgDownloadUrl" + fi done for package in haiku haiku_devel; do echo "Downloading $package..." - hpkgVersion="$(wget -qO- $HpkgBaseUrl | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" - wget -P "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + if [[ "$__hasWget" == 1 ]]; then + hpkgVersion="$(wget -qO- "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" + wget -P "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + else + hpkgVersion="$(curl -sSL "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')" + curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg" + fi done # Set up the sysroot @@ -609,7 +707,11 @@ elif [[ "$__CodeName" == "haiku" ]]; then # Download buildtools echo "Downloading Haiku buildtools" - wget -O "$__RootfsDir/tmp/download/buildtools.zip" $($__RootfsDir/tmp/script/fetch.sh --buildtools --arch=$__HaikuArch) + if [[ "$__hasWget" == 1 ]]; then + wget -O "$__RootfsDir/tmp/download/buildtools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --buildtools --arch=$__HaikuArch)" + else + curl -SLo "$__RootfsDir/tmp/download/buildtools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --buildtools --arch=$__HaikuArch)" + fi unzip -o "$__RootfsDir/tmp/download/buildtools.zip" -d "$__RootfsDir" # Cleaning up temporary files @@ -622,10 +724,12 @@ elif [[ -n "$__CodeName" ]]; then __Keyring="$__Keyring --force-check-gpg" fi + # shellcheck disable=SC2086 debootstrap "--variant=minbase" $__Keyring --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo" cp "$__CrossDir/$__BuildArch/sources.list.$__CodeName" "$__RootfsDir/etc/apt/sources.list" chroot "$__RootfsDir" apt-get update chroot "$__RootfsDir" apt-get -f -y install + # shellcheck disable=SC2086 chroot "$__RootfsDir" apt-get -y install $__UbuntuPackages chroot "$__RootfsDir" symlinks -cr /usr chroot "$__RootfsDir" apt-get clean @@ -643,6 +747,5 @@ elif [[ "$__Tizen" == "tizen" ]]; then ROOTFS_DIR="$__RootfsDir" "$__CrossDir/tizen-build-rootfs.sh" "$__BuildArch" else echo "Unsupported target platform." - usage; - exit 1 + usage fi diff --git a/eng/common/cross/riscv64/sources.list.sid b/eng/common/cross/riscv64/sources.list.sid index 65f730d224caa..b5f7a7e6e1eb5 100644 --- a/eng/common/cross/riscv64/sources.list.sid +++ b/eng/common/cross/riscv64/sources.list.sid @@ -1 +1 @@ -deb http://deb.debian.org/debian-ports sid main +deb http://deb.debian.org/debian sid main diff --git a/eng/common/cross/riscv64/tizen/tizen.patch b/eng/common/cross/riscv64/tizen/tizen.patch new file mode 100644 index 0000000000000..eb6d1c07470bf --- /dev/null +++ b/eng/common/cross/riscv64/tizen/tizen.patch @@ -0,0 +1,9 @@ +diff -u -r a/usr/lib/libc.so b/usr/lib/libc.so +--- a/usr/lib64/libc.so 2016-12-30 23:00:08.284951863 +0900 ++++ b/usr/lib64/libc.so 2016-12-30 23:00:32.140951815 +0900 +@@ -2,4 +2,4 @@ + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ + OUTPUT_FORMAT(elf64-littleriscv) +-GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a AS_NEEDED ( /lib64/ld-linux-riscv64-lp64d.so.1 ) ) ++GROUP ( libc.so.6 libc_nonshared.a AS_NEEDED ( ld-linux-riscv64-lp64d.so.1 ) ) diff --git a/eng/common/cross/tizen-build-rootfs.sh b/eng/common/cross/tizen-build-rootfs.sh index ac84173d44fc3..ba31c93285f63 100644 --- a/eng/common/cross/tizen-build-rootfs.sh +++ b/eng/common/cross/tizen-build-rootfs.sh @@ -22,6 +22,10 @@ case "$ARCH" in TIZEN_ARCH="x86_64" LINK_ARCH="x86" ;; + riscv64) + TIZEN_ARCH="riscv64" + LINK_ARCH="riscv" + ;; *) echo "Unsupported architecture for tizen: $ARCH" exit 1 @@ -58,4 +62,21 @@ rm -rf $TIZEN_TMP_DIR echo ">>Start configuring Tizen rootfs" ln -sfn asm-${LINK_ARCH} ./usr/include/asm patch -p1 < $__TIZEN_CROSSDIR/tizen.patch +if [[ "$TIZEN_ARCH" == "riscv64" ]]; then + echo "Fixing broken symlinks in $PWD" + rm ./usr/lib64/libresolv.so + ln -s ../../lib64/libresolv.so.2 ./usr/lib64/libresolv.so + rm ./usr/lib64/libpthread.so + ln -s ../../lib64/libpthread.so.0 ./usr/lib64/libpthread.so + rm ./usr/lib64/libdl.so + ln -s ../../lib64/libdl.so.2 ./usr/lib64/libdl.so + rm ./usr/lib64/libutil.so + ln -s ../../lib64/libutil.so.1 ./usr/lib64/libutil.so + rm ./usr/lib64/libm.so + ln -s ../../lib64/libm.so.6 ./usr/lib64/libm.so + rm ./usr/lib64/librt.so + ln -s ../../lib64/librt.so.1 ./usr/lib64/librt.so + rm ./lib/ld-linux-riscv64-lp64d.so.1 + ln -s ../lib64/ld-linux-riscv64-lp64d.so.1 ./lib/ld-linux-riscv64-lp64d.so.1 +fi echo "<:--stdlib=${CLR_CMAKE_CXX_STANDARD_LIBRARY}>) + add_link_options($<$:--stdlib=${CLR_CMAKE_CXX_STANDARD_LIBRARY}>) +endif() + +option(CLR_CMAKE_CXX_STANDARD_LIBRARY_STATIC "Statically link against the C++ standard library" OFF) +if(CLR_CMAKE_CXX_STANDARD_LIBRARY_STATIC) + add_link_options($<$:-static-libstdc++>) +endif() + +set(CLR_CMAKE_CXX_ABI_LIBRARY "" CACHE STRING "C++ ABI implementation library to link against. Only supported with the Clang compiler.") +if (CLR_CMAKE_CXX_ABI_LIBRARY) + # The user may specify the ABI library with the 'lib' prefix, like 'libstdc++'. Strip the prefix here so the linker finds the right library. + string(REGEX REPLACE "^lib(.+)" "\\1" CLR_CMAKE_CXX_ABI_LIBRARY ${CLR_CMAKE_CXX_ABI_LIBRARY}) + # We need to specify this as a linker-backend option as Clang will filter this option out when linking to libc++. + add_link_options("LINKER:-l${CLR_CMAKE_CXX_ABI_LIBRARY}") +endif() + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/eng/common/helixpublish.proj b/eng/common/helixpublish.proj index d7f185856e791..c1323bf412105 100644 --- a/eng/common/helixpublish.proj +++ b/eng/common/helixpublish.proj @@ -1,3 +1,4 @@ + diff --git a/eng/common/internal/Directory.Build.props b/eng/common/internal/Directory.Build.props index dbf99d82a5c2e..a735fe9a133ca 100644 --- a/eng/common/internal/Directory.Build.props +++ b/eng/common/internal/Directory.Build.props @@ -1,4 +1,6 @@ + + diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj index 7f5ce6d608133..8fa77e5b181f8 100644 --- a/eng/common/internal/Tools.csproj +++ b/eng/common/internal/Tools.csproj @@ -1,5 +1,6 @@ + net472 false @@ -27,4 +28,5 @@ + diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh index 2d5660642b8d4..ccd3a17268e24 100644 --- a/eng/common/native/init-compiler.sh +++ b/eng/common/native/init-compiler.sh @@ -64,7 +64,7 @@ if [ -z "$CLR_CC" ]; then if [ -z "$majorVersion" ]; then # note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero. if [ "$compiler" = "clang" ]; then versions="18 17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5" - elif [ "$compiler" = "gcc" ]; then versions="13 12 11 10 9 8 7 6 5 4.9"; fi + elif [ "$compiler" = "gcc" ]; then versions="14 13 12 11 10 9 8 7 6 5 4.9"; fi for version in $versions; do _major="${version%%.*}" @@ -125,8 +125,8 @@ if [ -z "$CC" ]; then exit 1 fi -# Only lld version >= 9 can be considered stable. lld doesn't support s390x. -if [ "$compiler" = "clang" ] && [ -n "$majorVersion" ] && [ "$majorVersion" -ge 9 ] && [ "$build_arch" != "s390x" ]; then +# Only lld version >= 9 can be considered stable. lld supports s390x starting from 18.0. +if [ "$compiler" = "clang" ] && [ -n "$majorVersion" ] && [ "$majorVersion" -ge 9 ] && ([ "$build_arch" != "s390x" ] || [ "$majorVersion" -ge 18 ]); then if "$CC" -fuse-ld=lld -Wl,--version >/dev/null 2>&1; then LDFLAGS="-fuse-ld=lld" fi diff --git a/eng/common/native/init-distro-rid.sh b/eng/common/native/init-distro-rid.sh index de1687b2ccbe7..83ea7aab0e081 100644 --- a/eng/common/native/init-distro-rid.sh +++ b/eng/common/native/init-distro-rid.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # getNonPortableDistroRid # @@ -11,21 +11,16 @@ # non-portable rid getNonPortableDistroRid() { - local targetOs="$1" - local targetArch="$2" - local rootfsDir="$3" - local nonPortableRid="" + targetOs="$1" + targetArch="$2" + rootfsDir="$3" + nonPortableRid="" if [ "$targetOs" = "linux" ]; then + # shellcheck disable=SC1091 if [ -e "${rootfsDir}/etc/os-release" ]; then - source "${rootfsDir}/etc/os-release" - - if [[ "${ID}" == "rhel" || "${ID}" == "rocky" || "${ID}" == "alpine" ]]; then - # remove the last version digit - VERSION_ID="${VERSION_ID%.*}" - fi - - if [[ "${VERSION_ID:-}" =~ ^([[:digit:]]|\.)+$ ]]; then + . "${rootfsDir}/etc/os-release" + if echo "${VERSION_ID:-}" | grep -qE '^([[:digit:]]|\.)+$'; then nonPortableRid="${ID}.${VERSION_ID}-${targetArch}" else # Rolling release distros either do not set VERSION_ID, set it as blank or @@ -33,45 +28,33 @@ getNonPortableDistroRid() # so omit it here to be consistent with everything else. nonPortableRid="${ID}-${targetArch}" fi - elif [ -e "${rootfsDir}/android_platform" ]; then - source "$rootfsDir"/android_platform + # shellcheck disable=SC1091 + . "${rootfsDir}/android_platform" nonPortableRid="$RID" fi fi if [ "$targetOs" = "freebsd" ]; then - # $rootfsDir can be empty. freebsd-version is shell script and it should always work. - __freebsd_major_version=$($rootfsDir/bin/freebsd-version | { read v; echo "${v%%.*}"; }) + # $rootfsDir can be empty. freebsd-version is a shell script and should always work. + __freebsd_major_version=$("$rootfsDir"/bin/freebsd-version | cut -d'.' -f1) nonPortableRid="freebsd.$__freebsd_major_version-${targetArch}" - elif command -v getprop && getprop ro.product.system.model 2>&1 | grep -qi android; then + elif command -v getprop >/dev/null && getprop ro.product.system.model | grep -qi android; then __android_sdk_version=$(getprop ro.build.version.sdk) nonPortableRid="android.$__android_sdk_version-${targetArch}" elif [ "$targetOs" = "illumos" ]; then __uname_version=$(uname -v) - case "$__uname_version" in - omnios-*) - __omnios_major_version=$(echo "${__uname_version:8:2}") - nonPortableRid=omnios."$__omnios_major_version"-"$targetArch" - ;; - joyent_*) - __smartos_major_version=$(echo "${__uname_version:7:4}") - nonPortableRid=smartos."$__smartos_major_version"-"$targetArch" - ;; - illumos_*) - nonPortableRid=openindiana-"$targetArch" - ;; - esac + nonPortableRid="illumos-${targetArch}" elif [ "$targetOs" = "solaris" ]; then __uname_version=$(uname -v) - __solaris_major_version=$(echo "${__uname_version%.*}") - nonPortableRid=solaris."$__solaris_major_version"-"$targetArch" + __solaris_major_version=$(echo "$__uname_version" | cut -d'.' -f1) + nonPortableRid="solaris.$__solaris_major_version-${targetArch}" elif [ "$targetOs" = "haiku" ]; then - __uname_release=$(uname -r) + __uname_release="$(uname -r)" nonPortableRid=haiku.r"$__uname_release"-"$targetArch" fi - echo "$(echo $nonPortableRid | tr '[:upper:]' '[:lower:]')" + echo "$nonPortableRid" | tr '[:upper:]' '[:lower:]' } # initDistroRidGlobal @@ -85,26 +68,23 @@ getNonPortableDistroRid() # None # # Notes: -# -# It is important to note that the function does not return anything, but it -# exports the following variables on success: -# -# __DistroRid : Non-portable rid of the target platform. -# __PortableTargetOS : OS-part of the portable rid that corresponds to the target platform. -# +# It is important to note that the function does not return anything, but it +# exports the following variables on success: +# __DistroRid : Non-portable rid of the target platform. +# __PortableTargetOS : OS-part of the portable rid that corresponds to the target platform. initDistroRidGlobal() { - local targetOs="$1" - local targetArch="$2" - local rootfsDir="" - if [ "$#" -ge 3 ]; then + targetOs="$1" + targetArch="$2" + rootfsDir="" + if [ $# -ge 3 ]; then rootfsDir="$3" fi if [ -n "${rootfsDir}" ]; then # We may have a cross build. Check for the existence of the rootfsDir if [ ! -e "${rootfsDir}" ]; then - echo "Error rootfsDir has been passed, but the location is not valid." + echo "Error: rootfsDir has been passed, but the location is not valid." exit 1 fi fi @@ -119,7 +99,7 @@ initDistroRidGlobal() STRINGS="$(command -v llvm-strings || true)" fi - # Check for musl-based distros (e.g Alpine Linux, Void Linux). + # Check for musl-based distros (e.g. Alpine Linux, Void Linux). if "${rootfsDir}/usr/bin/ldd" --version 2>&1 | grep -q musl || ( [ -n "$STRINGS" ] && "$STRINGS" "${rootfsDir}/usr/bin/ldd" 2>&1 | grep -q musl ); then __PortableTargetOS="linux-musl" diff --git a/eng/common/native/init-os-and-arch.sh b/eng/common/native/init-os-and-arch.sh index e693617a6c2b6..38921d4338f74 100644 --- a/eng/common/native/init-os-and-arch.sh +++ b/eng/common/native/init-os-and-arch.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # Use uname to determine what the OS is. OSName=$(uname -s | tr '[:upper:]' '[:lower:]') @@ -35,6 +35,10 @@ fi case "$CPUName" in arm64|aarch64) arch=arm64 + if [ "$(getconf LONG_BIT)" -lt 64 ]; then + # This is 32-bit OS running on 64-bit CPU (for example Raspberry Pi OS) + arch=arm + fi ;; loongarch64) @@ -50,6 +54,7 @@ case "$CPUName" in ;; armv7l|armv8l) + # shellcheck disable=SC1091 if (NAME=""; . /etc/os-release; test "$NAME" = "Tizen"); then arch=armel else diff --git a/eng/common/post-build/check-channel-consistency.ps1 b/eng/common/post-build/check-channel-consistency.ps1 index 63f3464c986a7..1728f035a93ed 100644 --- a/eng/common/post-build/check-channel-consistency.ps1 +++ b/eng/common/post-build/check-channel-consistency.ps1 @@ -7,7 +7,7 @@ try { . $PSScriptRoot\post-build-utils.ps1 if ($PromoteToChannels -eq "") { - Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." + Write-PipelineTaskError -Type 'warning' -Message "This build won't publish assets as it's not configured to any Maestro channel. If that wasn't intended use Darc to configure a default channel using add-default-channel for this branch or to promote it to a channel using add-build-to-channel. See https://github.com/dotnet/arcade/blob/main/Documentation/Darc.md#assigning-an-individual-build-to-a-channel for more info." ExitWithExitCode 0 } diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1 new file mode 100644 index 0000000000000..82d91f6fd0226 --- /dev/null +++ b/eng/common/post-build/redact-logs.ps1 @@ -0,0 +1,81 @@ +[CmdletBinding(PositionalBinding=$False)] +param( + [Parameter(Mandatory=$true, Position=0)][string] $InputPath, + [Parameter(Mandatory=$true)][string] $BinlogToolVersion, + [Parameter(Mandatory=$false)][string] $DotnetPath, + [Parameter(Mandatory=$false)][string] $PackageFeed = 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json', + # File with strings to redact - separated by newlines. + # For comments start the line with '# ' - such lines are ignored + [Parameter(Mandatory=$false)][string] $TokensFilePath, + [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact +) + +try { + . $PSScriptRoot\post-build-utils.ps1 + + $packageName = 'binlogtool' + + $dotnet = $DotnetPath + + if (!$dotnet) { + $dotnetRoot = InitializeDotNetCli -install:$true + $dotnet = "$dotnetRoot\dotnet.exe" + } + + $toolList = & "$dotnet" tool list -g + + if ($toolList -like "*$packageName*") { + & "$dotnet" tool uninstall $packageName -g + } + + $toolPath = "$PSScriptRoot\..\..\..\.tools" + $verbosity = 'minimal' + + New-Item -ItemType Directory -Force -Path $toolPath + + Push-Location -Path $toolPath + + try { + Write-Host "Installing Binlog redactor CLI..." + Write-Host "'$dotnet' new tool-manifest" + & "$dotnet" new tool-manifest + Write-Host "'$dotnet' tool install $packageName --local --add-source '$PackageFeed' -v $verbosity --version $BinlogToolVersion" + & "$dotnet" tool install $packageName --local --add-source "$PackageFeed" -v $verbosity --version $BinlogToolVersion + + if (Test-Path $TokensFilePath) { + Write-Host "Adding additional sensitive data for redaction from file: " $TokensFilePath + $TokensToRedact += Get-Content -Path $TokensFilePath | Foreach {$_.Trim()} | Where { $_ -notmatch "^# " } + } + + $optionalParams = [System.Collections.ArrayList]::new() + + Foreach ($p in $TokensToRedact) + { + if($p -match '^\$\(.*\)$') + { + Write-Host ("Ignoring token {0} as it is probably unexpanded AzDO variable" -f $p) + } + elseif($p) + { + $optionalParams.Add("-p:" + $p) | Out-Null + } + } + + & $dotnet binlogtool redact --input:$InputPath --recurse --in-place ` + @optionalParams + + if ($LastExitCode -ne 0) { + Write-PipelineTelemetryError -Category 'Redactor' -Type 'warning' -Message "Problems using Redactor tool (exit code: $LastExitCode). But ignoring them now." + } + } + finally { + Pop-Location + } + + Write-Host 'done.' +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Category 'Redactor' -Message "There was an error while trying to redact logs. Error: $_" + ExitWithExitCode 1 +} diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index 73828dd30d317..aab40de3fd9ac 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.8.1-2" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "17.10.0-pre.4.0" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true diff --git a/eng/common/sdl/trim-assets-version.ps1 b/eng/common/sdl/trim-assets-version.ps1 index a2e0048770452..0daa2a9e94628 100644 --- a/eng/common/sdl/trim-assets-version.ps1 +++ b/eng/common/sdl/trim-assets-version.ps1 @@ -72,4 +72,4 @@ catch { Write-Host $_ Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ ExitWithExitCode 1 -} \ No newline at end of file +} diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md new file mode 100644 index 0000000000000..c114bc28dcb95 --- /dev/null +++ b/eng/common/template-guidance.md @@ -0,0 +1,137 @@ +# Overview + +Arcade provides templates for public (`/templates`) and 1ES pipeline templates (`/templates-official`) scenarios. Pipelines which are required to be managed by 1ES pipeline templates should reference `/templates-offical`, all other pipelines may reference `/templates`. + +## How to use + +Basic guidance is: + +- 1ES Pipeline Template or 1ES Microbuild template runs should reference `eng/common/templates-official`. Any internal production-graded pipeline should use these templates. + +- All other runs should reference `eng/common/templates`. + +See [azure-pipelines.yml](../../azure-pipelines.yml) (templates-official example) or [azure-pipelines-pr.yml](../../azure-pipelines-pr.yml) (templates example) for examples. + +#### The `templateIs1ESManaged` parameter + +The `templateIs1ESManaged` is available on most templates and affects which of the variants is used for nested templates. See [Development Notes](#development-notes) below for more information on the `templateIs1ESManaged1 parameter. + +- For templates under `job/`, `jobs/`, `steps`, or `post-build/`, this parameter must be explicitly set. + +## Multiple outputs + +1ES pipeline templates impose a policy where every publish artifact execution results in additional security scans being injected into your pipeline. When using `templates-official/jobs/jobs.yml`, Arcade reduces the number of additional security injections by gathering all publishing outputs into the [Build.ArtifactStagingDirectory](https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services), and utilizing the [outputParentDirectory](https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-pipeline-templates/features/outputs#multiple-outputs) feature of 1ES pipeline templates. When implementing your pipeline, if you ensure publish artifacts are located in the `$(Build.ArtifactStagingDirectory)`, and utilize the 1ES provided template context, then you can reduce the number of security scans for your pipeline. + +Example: +``` yaml +# azure-pipelines.yml +extends: + template: azure-pipelines/MicroBuild.1ES.Official.yml@MicroBuildTemplate + parameters: + stages: + - stage: build + jobs: + - template: /eng/common/templates-official/jobs/jobs.yml@self + parameters: + # 1ES makes use of outputs to reduce security task injection overhead + templateContext: + outputs: + - output: pipelineArtifact + displayName: 'Publish logs from source' + continueOnError: true + condition: always() + targetPath: $(Build.ArtifactStagingDirectory)/artifacts/log + artifactName: Logs + jobs: + - job: Windows + steps: + - script: echo "friendly neighborhood" > artifacts/marvel/spiderman.txt + # copy build outputs to artifact staging directory for publishing + - task: CopyFiles@2 + displayName: Gather build output + inputs: + SourceFolder: '$(Build.SourcesDirectory)/artifacts/marvel' + Contents: '**' + TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/marvel' +``` + +Note: Multiple outputs are ONLY applicable to 1ES PT publishing (only usable when referencing `templates-official`). + +# Development notes + +**Folder / file structure** + +``` text +eng\common\ + [templates || templates-official]\ + job\ + job.yml (shim + artifact publishing logic) + onelocbuild.yml (shim) + publish-build-assets.yml (shim) + source-build.yml (shim) + source-index-stage1.yml (shim) + jobs\ + codeql-build.yml (shim) + jobs.yml (shim) + source-build.yml (shim) + post-build\ + post-build.yml (shim) + trigger-subscription.yml (shim) + common-variabls.yml (shim) + setup-maestro-vars.yml (shim) + steps\ + publish-build-artifacts.yml (logic) + publish-pipeline-artifacts.yml (logic) + add-build-channel.yml (shim) + component-governance.yml (shim) + generate-sbom.yml (shim) + publish-logs.yml (shim) + retain-build.yml (shim) + send-to-helix.yml (shim) + source-build.yml (shim) + variables\ + pool-providers.yml (logic + redirect) # templates/variables/pool-providers.yml will redirect to templates-official/variables/pool-providers.yml if you are running in the internal project + sdl-variables.yml (logic) + core-templates\ + job\ + job.yml (logic) + onelocbuild.yml (logic) + publish-build-assets.yml (logic) + source-build.yml (logic) + source-index-stage1.yml (logic) + jobs\ + codeql-build.yml (logic) + jobs.yml (logic) + source-build.yml (logic) + post-build\ + common-variabls.yml (logic) + post-build.yml (logic) + setup-maestro-vars.yml (logic) + trigger-subscription.yml (logic) + steps\ + add-build-to-channel.yml (logic) + component-governance.yml (logic) + generate-sbom.yml (logic) + publish-build-artifacts.yml (redirect) + publish-logs.yml (logic) + publish-pipeline-artifacts.yml (redirect) + retain-build.yml (logic) + send-to-helix.yml (logic) + source-build.yml (logic) + variables\ + pool-providers.yml (redirect) +``` + +In the table above, a file is designated as "shim", "logic", or "redirect". + +- shim - represents a yaml file which is an intermediate step between pipeline logic and .Net Core Engineering's templates (`core-templates`) and defines the `is1ESPipeline` parameter value. + +- logic - represents actual base template logic. + +- redirect- represents a file in `core-templates` which redirects to the "logic" file in either `templates` or `templates-official`. + +Logic for Arcade's templates live **primarily** in the `core-templates` folder. The exceptions to the location of the logic files are around artifact publishing, which is handled differently between 1es pipeline templates and standard templates. `templates` and `templates-official` provide shim entry points which redirect to `core-templates` while also defining the `is1ESPipeline` parameter. If a shim is referenced in `templates`, then `is1ESPipeline` is set to `false`. If a shim is referenced in `templates-official`, then `is1ESPipeline` is set to `true`. + +Within `templates` and `templates-official`, the templates at the "stages", and "jobs" / "job" level have been replaced with shims. Templates at the "steps" and "variables" level are typically too granular to be replaced with shims and instead persist logic which is directly applicable to either scenario. + +Within `core-templates`, there are a handful of places where logic is dependent on which shim entry point was used. In those places, we redirect back to the respective logic file in `templates` or `templates-official`. diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index 1f035fee73f4a..4724e9aaa8091 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -1,264 +1,62 @@ -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - -parameters: -# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - cancelTimeoutInMinutes: '' - condition: '' - container: '' - continueOnError: false - dependsOn: '' - displayName: '' - pool: '' - steps: [] - strategy: '' - timeoutInMinutes: '' - variables: [] - workspace: '' - templateContext: '' - -# Job base template specific parameters - # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md - artifacts: '' - enableMicrobuild: false - enablePublishBuildArtifacts: false - enablePublishBuildAssets: false - enablePublishTestResults: false - enablePublishUsingPipelines: false - enableBuildRetry: false - disableComponentGovernance: '' - componentGovernanceIgnoreDirectories: '' - mergeTestResults: false - testRunTitle: '' - testResultsFormat: '' - name: '' - preSteps: [] - runAsPublic: false -# Sbom related params - enableSbom: true - PackageVersion: 7.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - jobs: -- job: ${{ parameters.name }} - - ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: - cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} - - ${{ if ne(parameters.condition, '') }}: - condition: ${{ parameters.condition }} - - ${{ if ne(parameters.container, '') }}: - container: ${{ parameters.container }} - - ${{ if ne(parameters.continueOnError, '') }}: - continueOnError: ${{ parameters.continueOnError }} - - ${{ if ne(parameters.dependsOn, '') }}: - dependsOn: ${{ parameters.dependsOn }} - - ${{ if ne(parameters.displayName, '') }}: - displayName: ${{ parameters.displayName }} - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - - ${{ if ne(parameters.strategy, '') }}: - strategy: ${{ parameters.strategy }} - - ${{ if ne(parameters.timeoutInMinutes, '') }}: - timeoutInMinutes: ${{ parameters.timeoutInMinutes }} - - ${{ if ne(parameters.templateContext, '') }}: - templateContext: ${{ parameters.templateContext }} - - variables: - - ${{ if ne(parameters.enableTelemetry, 'false') }}: - - name: DOTNET_CLI_TELEMETRY_PROFILE - value: '$(Build.Repository.Uri)' - - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: - - name: EnableRichCodeNavigation - value: 'true' - # Retry signature validation up to three times, waiting 2 seconds between attempts. - # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures - - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY - value: 3,2000 - - ${{ each variable in parameters.variables }}: - # handle name-value variable syntax - # example: - # - name: [key] - # value: [value] - - ${{ if ne(variable.name, '') }}: - - name: ${{ variable.name }} - value: ${{ variable.value }} - - # handle variable groups - - ${{ if ne(variable.group, '') }}: - - group: ${{ variable.group }} - - # handle template variable syntax - # example: - # - template: path/to/template.yml - # parameters: - # [key]: [value] - - ${{ if ne(variable.template, '') }}: - - template: ${{ variable.template }} - ${{ if ne(variable.parameters, '') }}: - parameters: ${{ variable.parameters }} - - # handle key-value variable syntax. - # example: - # - [key]: [value] - - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: - - ${{ each pair in variable }}: - - name: ${{ pair.key }} - value: ${{ pair.value }} - - # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds - - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: DotNet-HelixApi-Access - - ${{ if ne(parameters.workspace, '') }}: - workspace: ${{ parameters.workspace }} - - steps: - - ${{ if ne(parameters.preSteps, '') }}: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - env: - TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)' - continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - - - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - - task: NuGetAuthenticate@1 - - - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - - task: DownloadPipelineArtifact@2 - inputs: - buildType: current - artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} - targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} - itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} - - - ${{ each step in parameters.steps }}: - - ${{ step }} - - - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: - - task: RichCodeNavIndexer@0 - displayName: RichCodeNav Upload - inputs: - languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} - environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'production') }} - richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin - uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} - continueOnError: true - - - template: /eng/common/templates-official/steps/component-governance.yml - parameters: - ${{ if eq(parameters.disableComponentGovernance, '') }}: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: - disableComponentGovernance: false - ${{ else }}: - disableComponentGovernance: true - ${{ else }}: - disableComponentGovernance: ${{ parameters.disableComponentGovernance }} - componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: MicroBuildCleanup@1 - displayName: Execute Microbuild cleanup tasks - condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - env: - TeamName: $(_TeamName) - - - ${{ if ne(parameters.artifacts.publish, '') }}: - - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - - task: CopyFiles@2 - displayName: Gather binaries for publish to artifacts - inputs: - SourceFolder: 'artifacts/bin' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' - - task: CopyFiles@2 - displayName: Gather packages for publish to artifacts - inputs: - SourceFolder: 'artifacts/packages' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish pipeline artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} - continueOnError: true - condition: always() - - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: 'artifacts/log' - artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} - displayName: 'Publish logs' - continueOnError: true - condition: always() - - - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} - continueOnError: true - condition: always() - - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}: - - task: PublishTestResults@2 - displayName: Publish XUnit Test Results - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}: - - task: PublishTestResults@2 - displayName: Publish TRX Test Results - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - - template: /eng/common/templates-official/steps/generate-sbom.yml - parameters: - PackageVersion: ${{ parameters.packageVersion}} - BuildDropPath: ${{ parameters.buildDropPath }} - IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' - artifactName: 'BuildConfiguration' - displayName: 'Publish build retry configuration' - continueOnError: true \ No newline at end of file +- template: /eng/common/core-templates/job/job.yml + parameters: + is1ESPipeline: true + + # publish artifacts + # for 1ES managed templates, use the templateContext.output to handle multiple outputs. + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - output: buildArtifacts + displayName: Publish pipeline artifacts + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + condition: always() + continueOnError: true + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log' + artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)_Attempt$(System.JobAttempt)') }} + displayName: 'Publish logs' + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enablePublishBuildArtifacts, true) }}: + - output: buildArtifacts + displayName: Publish Logs + PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + publishLocation: Container + ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - output: pipelineArtifact + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/eng/common/BuildConfiguration' + artifactName: 'BuildConfiguration' + displayName: 'Publish build retry configuration' + continueOnError: true + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: + - output: pipelineArtifact + displayName: Publish SBOM manifest + continueOnError: true + targetPath: $(Build.ArtifactStagingDirectory)/sbom + artifactName: $(ARTIFACT_NAME) + + # add any outputs provided via root yaml + - ${{ if ne(parameters.templateContext.outputs, '') }}: + - ${{ each output in parameters.templateContext.outputs }}: + - ${{ output }} + + # add any remaining templateContext properties + ${{ each context in parameters.templateContext }}: + ${{ if and(ne(context.key, 'outputParentDirectory'), ne(context.key, 'outputs')) }}: + ${{ context.key }}: ${{ context.value }} + + ${{ each parameter in parameters }}: + ${{ if and(ne(parameter.key, 'templateContext'), ne(parameter.key, 'is1ESPipeline')) }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/onelocbuild.yml b/eng/common/templates-official/job/onelocbuild.yml index 52b4d05d3f8dd..0f0c514b912df 100644 --- a/eng/common/templates-official/job/onelocbuild.yml +++ b/eng/common/templates-official/job/onelocbuild.yml @@ -1,112 +1,7 @@ -parameters: - # Optional: dependencies of the job - dependsOn: '' - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: '' - - CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex - GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - - SourcesDirectory: $(Build.SourcesDirectory) - CreatePr: true - AutoCompletePr: false - ReusePr: true - UseLfLineEndings: true - UseCheckedInLocProjectJson: false - SkipLocProjectJsonGeneration: false - LanguageSet: VS_Main_Languages - LclSource: lclFilesInRepo - LclPackageId: '' - RepoType: gitHub - GitHubOrg: dotnet - MirrorRepo: '' - MirrorBranch: main - condition: '' - JobNameSuffix: '' - jobs: -- job: OneLocBuild${{ parameters.JobNameSuffix }} - - dependsOn: ${{ parameters.dependsOn }} - - displayName: OneLocBuild${{ parameters.JobNameSuffix }} - - variables: - - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat - - name: _GenerateLocProjectArguments - value: -SourcesDirectory ${{ parameters.SourcesDirectory }} - -LanguageSet "${{ parameters.LanguageSet }}" - -CreateNeutralXlfs - - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: - - name: _GenerateLocProjectArguments - value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson - - template: /eng/common/templates-official/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - - steps: - - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - - task: Powershell@2 - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 - arguments: $(_GenerateLocProjectArguments) - displayName: Generate LocProject.json - condition: ${{ parameters.condition }} - - - task: OneLocBuild@2 - displayName: OneLocBuild - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - locProj: eng/Localize/LocProject.json - outDir: $(Build.ArtifactStagingDirectory) - lclSource: ${{ parameters.LclSource }} - lclPackageId: ${{ parameters.LclPackageId }} - isCreatePrSelected: ${{ parameters.CreatePr }} - isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} - ${{ if eq(parameters.CreatePr, true) }}: - isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - isShouldReusePrSelected: ${{ parameters.ReusePr }} - packageSourceAuth: patAuth - patVariable: ${{ parameters.CeapexPat }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - repoType: ${{ parameters.RepoType }} - gitHubPatVariable: "${{ parameters.GithubPat }}" - ${{ if ne(parameters.MirrorRepo, '') }}: - isMirrorRepoSelected: true - gitHubOrganization: ${{ parameters.GitHubOrg }} - mirrorRepo: ${{ parameters.MirrorRepo }} - mirrorBranch: ${{ parameters.MirrorBranch }} - condition: ${{ parameters.condition }} - - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish Localization Files - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/loc' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} +- template: /eng/common/core-templates/job/onelocbuild.yml + parameters: + is1ESPipeline: true - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish LocProject.json - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/Localize/' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/publish-build-assets.yml b/eng/common/templates-official/job/publish-build-assets.yml index 589ac80a18b74..d667a70e8de74 100644 --- a/eng/common/templates-official/job/publish-build-assets.yml +++ b/eng/common/templates-official/job/publish-build-assets.yml @@ -1,155 +1,7 @@ -parameters: - configuration: 'Debug' - - # Optional: condition for the job to run - condition: '' - - # Optional: 'true' if future jobs should run even if this job fails - continueOnError: false - - # Optional: dependencies of the job - dependsOn: '' - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: {} - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishUsingPipelines: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishAssetsImmediately: false - - artifactsPublishingAdditionalParameters: '' - - signingValidationAdditionalParameters: '' - jobs: -- job: Asset_Registry_Publish - - dependsOn: ${{ parameters.dependsOn }} - timeoutInMinutes: 150 - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - displayName: Publish Assets - ${{ else }}: - displayName: Publish to Build Asset Registry - - variables: - - template: /eng/common/templates-official/variables/pool-providers.yml - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: Publish-Build-Assets - - group: AzureDevOps-Artifact-Feeds-Pats - - name: runCodesignValidationInjection - value: false - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates-official/post-build/common-variables.yml - - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 - os: windows - steps: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download artifact - inputs: - artifactName: AssetManifests - downloadPath: '$(Build.StagingDirectory)/Download' - checkDownloadedFiles: true - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: NuGetAuthenticate@1 - - - task: PowerShell@2 - displayName: Publish Build Assets - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet - /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com - /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} - /p:OfficialBuildId=$(Build.BuildNumber) - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: powershell@2 - displayName: Create ReleaseConfigs Artifact - inputs: - targetType: inline - script: | - New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force - $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt" - Add-Content -Path $filePath -Value $(BARBuildId) - Add-Content -Path $filePath -Value "$(DefaultChannels)" - Add-Content -Path $filePath -Value $(IsStableBuild) - - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish ReleaseConfigs Artifact - inputs: - PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - task: powershell@2 - displayName: Check if SymbolPublishingExclusionsFile.txt exists - inputs: - targetType: inline - script: | - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" - if(Test-Path -Path $symbolExclusionfile) - { - Write-Host "SymbolExclusionFile exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" - } - else{ - Write-Host "Symbols Exclusion file does not exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" - } - - - task: 1ES.PublishBuildArtifacts@1 - displayName: Publish SymbolPublishingExclusionsFile Artifact - condition: eq(variables['SymbolExclusionFile'], 'true') - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates-official/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion 3 - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' +- template: /eng/common/core-templates/job/publish-build-assets.yml + parameters: + is1ESPipeline: true - - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - - template: /eng/common/templates-official/steps/publish-logs.yml - parameters: - JobLabel: 'Publish_Artifacts_Logs' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/source-build.yml b/eng/common/templates-official/job/source-build.yml index f193dfbe23668..1a480034b678e 100644 --- a/eng/common/templates-official/job/source-build.yml +++ b/eng/common/templates-official/job/source-build.yml @@ -1,67 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. The template produces a server job with a - # default ID 'Source_Build_Complete' to put in a dependency list if necessary. - - # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. - jobNamePrefix: 'Source_Build' - - # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for - # managed-only repositories. This is an object with these properties: - # - # name: '' - # The name of the job. This is included in the job ID. - # targetRID: '' - # The name of the target RID to use, instead of the one auto-detected by Arcade. - # nonPortable: false - # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than - # linux-x64), and compiling against distro-provided packages rather than portable ones. - # skipPublishValidation: false - # Disables publishing validation. By default, a check is performed to ensure no packages are - # published by source-build. - # container: '' - # A container to use. Runs in docker. - # pool: {} - # A pool to use. Runs directly on an agent. - # buildScript: '' - # Specifies the build script to invoke to perform the build in the repo. The default - # './build.sh' should work for typical Arcade repositories, but this is customizable for - # difficult situations. - # jobProperties: {} - # A list of job properties to inject at the top level, for potential extensibility beyond - # container and pool. - platform: {} - jobs: -- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} - displayName: Source-Build (${{ parameters.platform.name }}) - - ${{ each property in parameters.platform.jobProperties }}: - ${{ property.key }}: ${{ property.value }} - - ${{ if ne(parameters.platform.container, '') }}: - container: ${{ parameters.platform.container }} - - ${{ if eq(parameters.platform.pool, '') }}: - # The default VM host AzDO pool. This should be capable of running Docker containers: almost all - # source-build builds run in Docker, including the default managed platform. - # /eng/common/templates-official/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - image: 1es-mariner-2 - os: linux - - ${{ if ne(parameters.platform.pool, '') }}: - pool: ${{ parameters.platform.pool }} - - workspace: - clean: all +- template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: true - steps: - - template: /eng/common/templates-official/steps/source-build.yml - parameters: - platform: ${{ parameters.platform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/job/source-index-stage1.yml b/eng/common/templates-official/job/source-index-stage1.yml index f0513aee5b0da..6d5ead316f92b 100644 --- a/eng/common/templates-official/job/source-index-stage1.yml +++ b/eng/common/templates-official/job/source-index-stage1.yml @@ -1,68 +1,7 @@ -parameters: - runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 - sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json - sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" - preSteps: [] - binlogPath: artifacts/log/Debug/Build.binlog - condition: '' - dependsOn: '' - pool: '' - jobs: -- job: SourceIndexStage1 - dependsOn: ${{ parameters.dependsOn }} - condition: ${{ parameters.condition }} - variables: - - name: SourceIndexPackageVersion - value: ${{ parameters.sourceIndexPackageVersion }} - - name: SourceIndexPackageSource - value: ${{ parameters.sourceIndexPackageSource }} - - name: BinlogPath - value: ${{ parameters.binlogPath }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: source-dot-net stage1 variables - - template: /eng/common/templates-official/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $(DncEngPublicBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64.open - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $(DncEngInternalBuildPool) - image: windows.vs2022.amd64 - os: windows - - steps: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 - inputs: - packageType: sdk - version: 6.0.x - installationPath: $(Agent.TempDirectory)/dotnet - workingDirectory: $(Agent.TempDirectory) - - - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - displayName: Download Tools - # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. - workingDirectory: $(Agent.TempDirectory) - - - script: ${{ parameters.sourceIndexBuildCommand }} - displayName: Build Repository - - - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output - displayName: Process Binlog into indexable sln +- template: /eng/common/core-templates/job/source-index-stage1.yml + parameters: + is1ESPipeline: true - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) - displayName: Upload stage1 artifacts to source index - env: - BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/codeql-build.yml b/eng/common/templates-official/jobs/codeql-build.yml index b68d3c2f31990..a726322ecfe01 100644 --- a/eng/common/templates-official/jobs/codeql-build.yml +++ b/eng/common/templates-official/jobs/codeql-build.yml @@ -1,31 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - jobs: -- template: /eng/common/templates-official/jobs/jobs.yml +- template: /eng/common/core-templates/jobs/codeql-build.yml parameters: - enableMicrobuild: false - enablePublishBuildArtifacts: false - enablePublishTestResults: false - enablePublishBuildAssets: false - enablePublishUsingPipelines: false - enableTelemetry: true + is1ESPipeline: true - variables: - - group: Publish-Build-Assets - # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in - # sync with the packages.config file. - - name: DefaultGuardianVersion - value: 0.109.0 - - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config - - name: GuardianVersion - value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - jobs: ${{ parameters.jobs }} - + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/jobs.yml b/eng/common/templates-official/jobs/jobs.yml index 857a0f8ba43e8..007deddaea0f5 100644 --- a/eng/common/templates-official/jobs/jobs.yml +++ b/eng/common/templates-official/jobs/jobs.yml @@ -1,97 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: Enable publishing using release pipelines - enablePublishUsingPipelines: false - - # Optional: Enable running the source-build jobs to build repo from source - enableSourceBuild: false - - # Optional: Parameters for source-build template. - # See /eng/common/templates-official/jobs/source-build.yml for options - sourceBuildParameters: [] - - graphFileGeneration: - # Optional: Enable generating the graph files at the end of the build - enabled: false - # Optional: Include toolset dependencies in the generated graph files - includeToolset: false - - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - - # Optional: Override automatically derived dependsOn value for "publish build assets" job - publishBuildAssetsDependsOn: '' - - # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. - publishAssetsImmediately: false - - # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) - artifactsPublishingAdditionalParameters: '' - signingValidationAdditionalParameters: '' - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - enableSourceIndex: false - sourceIndexParams: {} - -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - jobs: -- ${{ each job in parameters.jobs }}: - - template: ../job/job.yml - parameters: - # pass along parameters - ${{ each parameter in parameters }}: - ${{ if ne(parameter.key, 'jobs') }}: - ${{ parameter.key }}: ${{ parameter.value }} - - # pass along job properties - ${{ each property in job }}: - ${{ if ne(property.key, 'job') }}: - ${{ property.key }}: ${{ property.value }} - - name: ${{ job.job }} - -- ${{ if eq(parameters.enableSourceBuild, true) }}: - - template: /eng/common/templates-official/jobs/source-build.yml - parameters: - allCompletedJobId: Source_Build_Complete - ${{ each parameter in parameters.sourceBuildParameters }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if eq(parameters.enableSourceIndex, 'true') }}: - - template: ../job/source-index-stage1.yml - parameters: - runAsPublic: ${{ parameters.runAsPublic }} - ${{ each parameter in parameters.sourceIndexParams }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: - - template: ../job/publish-build-assets.yml - parameters: - continueOnError: ${{ parameters.continueOnError }} - dependsOn: - - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.publishBuildAssetsDependsOn }}: - - ${{ job.job }} - - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.jobs }}: - - ${{ job.job }} - - ${{ if eq(parameters.enableSourceBuild, true) }}: - - Source_Build_Complete +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: true - runAsPublic: ${{ parameters.runAsPublic }} - publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} - publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} - enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/jobs/source-build.yml b/eng/common/templates-official/jobs/source-build.yml index 08e5db9bb1161..483e7b611f346 100644 --- a/eng/common/templates-official/jobs/source-build.yml +++ b/eng/common/templates-official/jobs/source-build.yml @@ -1,46 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. A job is created for each platform, as - # well as an optional server job that completes when all platform jobs complete. - - # The name of the "join" job for all source-build platforms. If set to empty string, the job is - # not included. Existing repo pipelines can use this job depend on all source-build jobs - # completing without maintaining a separate list of every single job ID: just depend on this one - # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. - allCompletedJobId: '' - - # See /eng/common/templates-official/job/source-build.yml - jobNamePrefix: 'Source_Build' - - # This is the default platform provided by Arcade, intended for use by a managed-only repo. - defaultManagedPlatform: - name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8' - - # Defines the platforms on which to run build jobs. One job is created for each platform, and the - # object in this array is sent to the job template as 'platform'. If no platforms are specified, - # one job runs on 'defaultManagedPlatform'. - platforms: [] - jobs: +- template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: true -- ${{ if ne(parameters.allCompletedJobId, '') }}: - - job: ${{ parameters.allCompletedJobId }} - displayName: Source-Build Complete - pool: server - dependsOn: - - ${{ each platform in parameters.platforms }}: - - ${{ parameters.jobNamePrefix }}_${{ platform.name }} - - ${{ if eq(length(parameters.platforms), 0) }}: - - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} - -- ${{ each platform in parameters.platforms }}: - - template: /eng/common/templates-official/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ platform }} - -- ${{ if eq(length(parameters.platforms), 0) }}: - - template: /eng/common/templates-official/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ parameters.defaultManagedPlatform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/common-variables.yml b/eng/common/templates-official/post-build/common-variables.yml index c24193acfc981..c32fc49233f8f 100644 --- a/eng/common/templates-official/post-build/common-variables.yml +++ b/eng/common/templates-official/post-build/common-variables.yml @@ -1,22 +1,8 @@ variables: - - group: Publish-Build-Assets +- template: /eng/common/core-templates/post-build/common-variables.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true - # Whether the build is internal or not - - name: IsInternalBuild - value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} - - # Default Maestro++ API Endpoint and API Version - - name: MaestroApiEndPoint - value: "https://maestro-prod.westus2.cloudapp.azure.com" - - name: MaestroApiAccessToken - value: $(MaestroAccessToken) - - name: MaestroApiVersion - value: "2020-02-20" - - - name: SourceLinkCLIVersion - value: 3.0.0 - - name: SymbolToolVersion - value: 1.0.1 - - - name: runCodesignValidationInjection - value: false + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/post-build/post-build.yml b/eng/common/templates-official/post-build/post-build.yml index da1f40958b450..2364c0fd4a527 100644 --- a/eng/common/templates-official/post-build/post-build.yml +++ b/eng/common/templates-official/post-build/post-build.yml @@ -1,285 +1,8 @@ -parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - stages: -- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - - stage: Validate - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Validate Build Assets - variables: - - template: common-variables.yml - - template: /eng/common/templates-official/variables/pool-providers.yml - jobs: - - job: - displayName: NuGet Validation - condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ - - - job: - displayName: Signing Validation - condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - itemPattern: | - ** - !**/Microsoft.SourceBuild.Intermediate.*.nupkg - - # This is necessary whenever we want to publish/restore to an AzDO private feed - # Since sdk-task.ps1 tries to restore packages we need to do this authentication here - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: ../steps/publish-logs.yml - parameters: - StageLabel: 'Validation' - JobLabel: 'Signing' - BinlogToolVersion: $(BinlogToolVersion) - - - job: - displayName: SourceLink Validation - condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - image: 1es-windows-2022 - os: windows - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) - -GHCommit $(Build.SourceVersion) - -SourcelinkCliVersion $(SourceLinkCLIVersion) - continueOnError: true - -- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - - stage: publish_using_darc - ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - dependsOn: ${{ parameters.publishDependsOn }} - ${{ else }}: - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Publish using Darc - variables: - - template: common-variables.yml - - template: /eng/common/templates-official/variables/pool-providers.yml - jobs: - - job: - displayName: Publish Using Darc - timeoutInMinutes: 120 - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2022 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - name: NetCore1ESPool-Publishing-Internal - image: windows.vs2019.amd64 - os: windows - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: NuGetAuthenticate@1 +- template: /eng/common/core-templates/post-build/post-build.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/post-build/setup-maestro-vars.yml b/eng/common/templates-official/post-build/setup-maestro-vars.yml index 0c87f149a4ad7..024397d878645 100644 --- a/eng/common/templates-official/post-build/setup-maestro-vars.yml +++ b/eng/common/templates-official/post-build/setup-maestro-vars.yml @@ -1,70 +1,8 @@ -parameters: - BARBuildId: '' - PromoteToChannelIds: '' - steps: - - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Release Configs - inputs: - buildType: current - artifactName: ReleaseConfigs - checkDownloadedFiles: true - - - task: PowerShell@2 - name: setReleaseVars - displayName: Set Release Configs Vars - inputs: - targetType: inline - pwsh: true - script: | - try { - if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { - $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt - - $BarId = $Content | Select -Index 0 - $Channels = $Content | Select -Index 1 - $IsStableBuild = $Content | Select -Index 2 - - $AzureDevOpsProject = $Env:System_TeamProject - $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId - $AzureDevOpsBuildId = $Env:Build_BuildId - } - else { - $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" - - $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' - $apiHeaders.Add('Accept', 'application/json') - $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") - - $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } - - $BarId = $Env:BARBuildId - $Channels = $Env:PromoteToMaestroChannels -split "," - $Channels = $Channels -join "][" - $Channels = "[$Channels]" - - $IsStableBuild = $buildInfo.stable - $AzureDevOpsProject = $buildInfo.azureDevOpsProject - $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId - $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId - } - - Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" - Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" - Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" +- template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: true - Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" - Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" - Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" - } - catch { - Write-Host $_ - Write-Host $_.Exception - Write-Host $_.ScriptStackTrace - exit 1 - } - env: - MAESTRO_API_TOKEN: $(MaestroApiAccessToken) - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/add-build-to-channel.yml b/eng/common/templates-official/steps/add-build-to-channel.yml index f67a210d62f3e..543dea8c6969a 100644 --- a/eng/common/templates-official/steps/add-build-to-channel.yml +++ b/eng/common/templates-official/steps/add-build-to-channel.yml @@ -1,13 +1,7 @@ -parameters: - ChannelId: 0 - steps: -- task: PowerShell@2 - displayName: Add Build to Channel - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 - arguments: -BuildId $(BARBuildId) - -ChannelId ${{ parameters.ChannelId }} - -MaestroApiAccessToken $(MaestroApiAccessToken) - -MaestroApiEndPoint $(MaestroApiEndPoint) - -MaestroApiVersion $(MaestroApiVersion) +- template: /eng/common/core-templates/steps/add-build-to-channel.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/build-reason.yml b/eng/common/templates-official/steps/build-reason.yml deleted file mode 100644 index eba58109b52c9..0000000000000 --- a/eng/common/templates-official/steps/build-reason.yml +++ /dev/null @@ -1,12 +0,0 @@ -# build-reason.yml -# Description: runs steps if build.reason condition is valid. conditions is a string of valid build reasons -# to include steps (',' separated). -parameters: - conditions: '' - steps: [] - -steps: - - ${{ if and( not(startsWith(parameters.conditions, 'not')), contains(parameters.conditions, variables['build.reason'])) }}: - - ${{ parameters.steps }} - - ${{ if and( startsWith(parameters.conditions, 'not'), not(contains(parameters.conditions, variables['build.reason']))) }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates-official/steps/component-governance.yml b/eng/common/templates-official/steps/component-governance.yml index cbba0596709da..30bb3985ca2bf 100644 --- a/eng/common/templates-official/steps/component-governance.yml +++ b/eng/common/templates-official/steps/component-governance.yml @@ -1,13 +1,7 @@ -parameters: - disableComponentGovernance: false - componentGovernanceIgnoreDirectories: '' - steps: -- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: - - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" - displayName: Set skipComponentGovernanceDetection variable -- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: - - task: ComponentGovernanceComponentDetection@0 - continueOnError: true - inputs: - ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file +- template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/execute-codeql.yml b/eng/common/templates-official/steps/execute-codeql.yml deleted file mode 100644 index 9b4a5ffa30a78..0000000000000 --- a/eng/common/templates-official/steps/execute-codeql.yml +++ /dev/null @@ -1,32 +0,0 @@ -parameters: - # Language that should be analyzed. Defaults to csharp - language: csharp - # Build Commands - buildCommands: '' - overrideParameters: '' # Optional: to override values for parameters. - additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth - # diagnosis of problems with specific tool configurations. - publishGuardianDirectoryToPipeline: false - # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL - # parameters rather than relying on YAML. It may be better to use a local script, because you can - # reproduce results locally without piecing together a command based on the YAML. - executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' - # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named - # 'continueOnError', the parameter value is not correctly picked up. - # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter - # optional: determines whether to continue the build if the step errors; - sdlContinueOnError: false - -steps: -- template: /eng/common/templates-official/steps/execute-sdl.yml - parameters: - overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} - executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} - overrideParameters: ${{ parameters.overrideParameters }} - additionalParameters: '${{ parameters.additionalParameters }} - -CodeQLAdditionalRunConfigParams @("BuildCommands < ${{ parameters.buildCommands }}", "Language < ${{ parameters.language }}")' - publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} - sdlContinueOnError: ${{ parameters.sdlContinueOnError }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/execute-sdl.yml b/eng/common/templates-official/steps/execute-sdl.yml deleted file mode 100644 index 07426fde05d82..0000000000000 --- a/eng/common/templates-official/steps/execute-sdl.yml +++ /dev/null @@ -1,88 +0,0 @@ -parameters: - overrideGuardianVersion: '' - executeAllSdlToolsScript: '' - overrideParameters: '' - additionalParameters: '' - publishGuardianDirectoryToPipeline: false - sdlContinueOnError: false - condition: '' - -steps: -- task: NuGetAuthenticate@1 - inputs: - nuGetServiceConnections: GuardianConnect - -- task: NuGetToolInstaller@1 - displayName: 'Install NuGet.exe' - -- ${{ if ne(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts -Version ${{ parameters.overrideGuardianVersion }} - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian (Overridden) - -- ${{ if eq(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian - -- ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} - displayName: Execute SDL (Overridden) - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} - -GuardianCliLocation $(GuardianCliLocation) - -NugetPackageDirectory $(Build.SourcesDirectory)\.packages - -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) - ${{ parameters.additionalParameters }} - displayName: Execute SDL - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: - # We want to publish the Guardian results and configuration for easy diagnosis. However, the - # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default - # tooling files. Some of these files are large and aren't useful during an investigation, so - # exclude them by simply deleting them before publishing. (As of writing, there is no documented - # way to selectively exclude a dir from the pipeline artifact publish task.) - - task: DeleteFiles@1 - displayName: Delete Guardian dependencies to avoid uploading - inputs: - SourceFolder: $(Agent.BuildDirectory)/.gdn - Contents: | - c - i - condition: succeededOrFailed() - - - publish: $(Agent.BuildDirectory)/.gdn - artifact: GuardianConfiguration - displayName: Publish GuardianConfiguration - condition: succeededOrFailed() - - # Publish the SARIF files in a container named CodeAnalysisLogs to enable integration - # with the "SARIF SAST Scans Tab" Azure DevOps extension - - task: CopyFiles@2 - displayName: Copy SARIF files - inputs: - flattenFolders: true - sourceFolder: $(Agent.BuildDirectory)/.gdn/rc/ - contents: '**/*.sarif' - targetFolder: $(Build.SourcesDirectory)/CodeAnalysisLogs - condition: succeededOrFailed() - - # Use PublishBuildArtifacts because the SARIF extension only checks this case - # see microsoft/sarif-azuredevops-extension#4 - - task: PublishBuildArtifacts@1 - displayName: Publish SARIF files to CodeAnalysisLogs container - inputs: - pathToPublish: $(Build.SourcesDirectory)/CodeAnalysisLogs - artifactName: CodeAnalysisLogs - condition: succeededOrFailed() \ No newline at end of file diff --git a/eng/common/templates-official/steps/generate-sbom.yml b/eng/common/templates-official/steps/generate-sbom.yml index 1bf43bf807af3..9a89a4706d94e 100644 --- a/eng/common/templates-official/steps/generate-sbom.yml +++ b/eng/common/templates-official/steps/generate-sbom.yml @@ -1,48 +1,7 @@ -# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. -# PackageName - The name of the package this SBOM represents. -# PackageVersion - The version of the package this SBOM represents. -# ManifestDirPath - The path of the directory where the generated manifest files will be placed -# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. - -parameters: - PackageVersion: 8.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - PackageName: '.NET' - ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom - IgnoreDirectories: '' - sbomContinueOnError: true - steps: -- task: PowerShell@2 - displayName: Prep for SBOM generation in (Non-linux) - condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) - inputs: - filePath: ./eng/common/generate-sbom-prep.ps1 - arguments: ${{parameters.manifestDirPath}} - -# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 -- script: | - chmod +x ./eng/common/generate-sbom-prep.sh - ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} - displayName: Prep for SBOM generation in (Linux) - condition: eq(variables['Agent.Os'], 'Linux') - continueOnError: ${{ parameters.sbomContinueOnError }} - -- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: 'Generate SBOM manifest' - continueOnError: ${{ parameters.sbomContinueOnError }} - inputs: - PackageName: ${{ parameters.packageName }} - BuildDropPath: ${{ parameters.buildDropPath }} - PackageVersion: ${{ parameters.packageVersion }} - ManifestDirPath: ${{ parameters.manifestDirPath }} - ${{ if ne(parameters.IgnoreDirectories, '') }}: - AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' - -- task: 1ES.PublishPipelineArtifact@1 - displayName: Publish SBOM manifest - continueOnError: ${{parameters.sbomContinueOnError}} - inputs: - targetPath: '${{parameters.manifestDirPath}}' - artifactName: $(ARTIFACT_NAME) +- template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: true + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-build-artifacts.yml b/eng/common/templates-official/steps/publish-build-artifacts.yml new file mode 100644 index 0000000000000..100a3fc98493c --- /dev/null +++ b/eng/common/templates-official/steps/publish-build-artifacts.yml @@ -0,0 +1,41 @@ +parameters: +- name: displayName + type: string + default: 'Publish to Build Artifact' + +- name: condition + type: string + default: succeeded() + +- name: artifactName + type: string + +- name: pathToPublish + type: string + +- name: continueOnError + type: boolean + default: false + +- name: publishLocation + type: string + default: 'Container' + +- name: is1ESPipeline + type: boolean + default: true + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates-official cannot be referenced from a non-1ES managed template': error +- task: 1ES.PublishBuildArtifacts@1 + displayName: ${{ parameters.displayName }} + condition: ${{ parameters.condition }} + ${{ if parameters.continueOnError }}: + continueOnError: ${{ parameters.continueOnError }} + inputs: + PublishLocation: ${{ parameters.publishLocation }} + PathtoPublish: ${{ parameters.pathToPublish }} + ${{ if parameters.artifactName }}: + ArtifactName: ${{ parameters.artifactName }} + diff --git a/eng/common/templates-official/steps/publish-logs.yml b/eng/common/templates-official/steps/publish-logs.yml index 04012fed182a1..579fd531e94c3 100644 --- a/eng/common/templates-official/steps/publish-logs.yml +++ b/eng/common/templates-official/steps/publish-logs.yml @@ -1,23 +1,7 @@ -parameters: - StageLabel: '' - JobLabel: '' - steps: -- task: Powershell@2 - displayName: Prepare Binlogs to Upload - inputs: - targetType: inline - script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - continueOnError: true - condition: always() +- template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: true -- task: 1ES.PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/PostBuildLogs' - PublishLocation: Container - ArtifactName: PostBuildLogs - continueOnError: true - condition: always() + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/publish-pipeline-artifacts.yml b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml new file mode 100644 index 0000000000000..d71eb0c743986 --- /dev/null +++ b/eng/common/templates-official/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,26 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: true + +- name: args + type: object + default: {} + +steps: +- ${{ if ne(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates-official cannot be referenced from a non-1ES managed template': error +- task: 1ES.PublishPipelineArtifact@1 + displayName: ${{ coalesce(parameters.args.displayName, 'Publish to Build Artifact') }} + ${{ if parameters.args.condition }}: + condition: ${{ parameters.args.condition }} + ${{ else }}: + condition: succeeded() + ${{ if parameters.args.continueOnError }}: + continueOnError: ${{ parameters.args.continueOnError }} + inputs: + targetPath: ${{ parameters.args.targetPath }} + ${{ if parameters.args.artifactName }}: + artifactName: ${{ parameters.args.artifactName }} + ${{ if parameters.args.properties }}: + properties: ${{ parameters.args.properties }} \ No newline at end of file diff --git a/eng/common/templates-official/steps/retain-build.yml b/eng/common/templates-official/steps/retain-build.yml index 83d97a26a01ff..5594551508a3c 100644 --- a/eng/common/templates-official/steps/retain-build.yml +++ b/eng/common/templates-official/steps/retain-build.yml @@ -1,28 +1,7 @@ -parameters: - # Optional azure devops PAT with build execute permissions for the build's organization, - # only needed if the build that should be retained ran on a different organization than - # the pipeline where this template is executing from - Token: '' - # Optional BuildId to retain, defaults to the current running build - BuildId: '' - # Azure devops Organization URI for the build in the https://dev.azure.com/ format. - # Defaults to the organization the current pipeline is running on - AzdoOrgUri: '$(System.CollectionUri)' - # Azure devops project for the build. Defaults to the project the current pipeline is running on - AzdoProject: '$(System.TeamProject)' - steps: - - task: powershell@2 - inputs: - targetType: 'filePath' - filePath: eng/common/retain-build.ps1 - pwsh: true - arguments: > - -AzdoOrgUri: ${{parameters.AzdoOrgUri}} - -AzdoProject ${{parameters.AzdoProject}} - -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} - -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} - displayName: Enable permanent build retention - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - BUILD_ID: $(Build.BuildId) \ No newline at end of file +- template: /eng/common/core-templates/steps/retain-build.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/send-to-helix.yml b/eng/common/templates-official/steps/send-to-helix.yml index 3eb7e2d5f840c..6500f21bf845c 100644 --- a/eng/common/templates-official/steps/send-to-helix.yml +++ b/eng/common/templates-official/steps/send-to-helix.yml @@ -1,91 +1,7 @@ -# Please remember to update the documentation if you make changes to these parameters! -parameters: - HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' - HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number - HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues - HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group - HelixConfiguration: '' # optional -- additional property attached to a job - HelixPreCommands: '' # optional -- commands to run before Helix work item execution - HelixPostCommands: '' # optional -- commands to run after Helix work item execution - WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects - WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects - WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects - CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload - XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true - XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects - XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects - XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner - XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects - IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion - DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." - IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set - HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) - Creator: '' # optional -- if the build is external, use this to specify who is sending the job - DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO - condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() - continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false - steps: - - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' - displayName: ${{ parameters.DisplayNamePrefix }} (Windows) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog - displayName: ${{ parameters.DisplayNamePrefix }} (Unix) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} +- template: /eng/common/core-templates/steps/send-to-helix.yml + parameters: + is1ESPipeline: true + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates-official/steps/source-build.yml b/eng/common/templates-official/steps/source-build.yml index 829f17c34d118..8f92c49e7b06f 100644 --- a/eng/common/templates-official/steps/source-build.yml +++ b/eng/common/templates-official/steps/source-build.yml @@ -1,129 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. - - # This is a 'steps' template, and is intended for advanced scenarios where the existing build - # infra has a careful build methodology that must be followed. For example, a repo - # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline - # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to - # GitHub. Using this steps template leaves room for that infra to be included. - - # Defines the platform on which to run the steps. See 'eng/common/templates-official/job/source-build.yml' - # for details. The entire object is described in the 'job' template for simplicity, even though - # the usage of the properties on this object is split between the 'job' and 'steps' templates. - platform: {} - steps: -# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) -- script: | - set -x - df -h - - # If building on the internal project, the artifact feeds variable may be available (usually only if needed) - # In that case, call the feed setup script to add internal feeds corresponding to public ones. - # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. - # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those - # changes. - internalRestoreArgs= - if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then - # Temporarily work around https://github.com/dotnet/arcade/issues/7709 - chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh - $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) - internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' - - # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. - # This only works if there is a username/email configured, which won't be the case in most CI runs. - git config --get user.email - if [ $? -ne 0 ]; then - git config user.email dn-bot@microsoft.com - git config user.name dn-bot - fi - fi - - # If building on the internal project, the internal storage variable may be available (usually only if needed) - # In that case, add variables to allow the download of internal runtimes if the specified versions are not found - # in the default public locations. - internalRuntimeDownloadArgs= - if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' - fi - - buildConfig=Release - # Check if AzDO substitutes in a build config from a variable, and use it if so. - if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then - buildConfig='$(_BuildConfig)' - fi - - officialBuildArgs= - if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then - officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' - fi - - targetRidArgs= - if [ '${{ parameters.platform.targetRID }}' != '' ]; then - targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' - fi - - runtimeOsArgs= - if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then - runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' - fi - - baseOsArgs= - if [ '${{ parameters.platform.baseOS }}' != '' ]; then - baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' - fi - - publishArgs= - if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then - publishArgs='--publish' - fi - - assetManifestFileName=SourceBuild_RidSpecific.xml - if [ '${{ parameters.platform.name }}' != '' ]; then - assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml - fi - - ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ - --configuration $buildConfig \ - --restore --build --pack $publishArgs -bl \ - $officialBuildArgs \ - $internalRuntimeDownloadArgs \ - $internalRestoreArgs \ - $targetRidArgs \ - $runtimeOsArgs \ - $baseOsArgs \ - /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ - /p:ArcadeBuildFromSource=true \ - /p:AssetManifestFileName=$assetManifestFileName - displayName: Build - -# Upload build logs for diagnosis. -- task: CopyFiles@2 - displayName: Prepare BuildLogs staging directory - inputs: - SourceFolder: '$(Build.SourcesDirectory)' - Contents: | - **/*.log - **/*.binlog - artifacts/source-build/self/prebuilt-report/** - TargetFolder: '$(Build.StagingDirectory)/BuildLogs' - CleanTargetFolder: true - continueOnError: true - condition: succeededOrFailed() - -- task: 1ES.PublishPipelineArtifact@1 - displayName: Publish BuildLogs - inputs: - targetPath: '$(Build.StagingDirectory)/BuildLogs' - artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) - continueOnError: true - condition: succeededOrFailed() +- template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: true -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- task: ComponentGovernanceComponentDetection@0 - displayName: Component Detection (Exclude upstream cache) - inputs: - ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml deleted file mode 100644 index 7870f93bc1765..0000000000000 --- a/eng/common/templates/job/execute-sdl.yml +++ /dev/null @@ -1,139 +0,0 @@ -parameters: - enable: 'false' # Whether the SDL validation job should execute or not - overrideParameters: '' # Optional: to override values for parameters. - additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth - # diagnosis of problems with specific tool configurations. - publishGuardianDirectoryToPipeline: false - # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL - # parameters rather than relying on YAML. It may be better to use a local script, because you can - # reproduce results locally without piecing together a command based on the YAML. - executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' - # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named - # 'continueOnError', the parameter value is not correctly picked up. - # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter - sdlContinueOnError: false # optional: determines whether to continue the build if the step errors; - # optional: determines if build artifacts should be downloaded. - downloadArtifacts: true - # optional: determines if this job should search the directory of downloaded artifacts for - # 'tar.gz' and 'zip' archive files and extract them before running SDL validation tasks. - extractArchiveArtifacts: false - dependsOn: '' # Optional: dependencies of the job - artifactNames: '' # Optional: patterns supplied to DownloadBuildArtifacts - # Usage: - # artifactNames: - # - 'BlobArtifacts' - # - 'Artifacts_Windows_NT_Release' - # Optional: download a list of pipeline artifacts. 'downloadArtifacts' controls build artifacts, - # not pipeline artifacts, so doesn't affect the use of this parameter. - pipelineArtifactNames: [] - -jobs: -- job: Run_SDL - dependsOn: ${{ parameters.dependsOn }} - displayName: Run SDL tool - condition: and(succeededOrFailed(), eq( ${{ parameters.enable }}, 'true')) - variables: - - group: DotNet-VSTS-Bot - - name: AzDOProjectName - value: ${{ parameters.AzDOProjectName }} - - name: AzDOPipelineId - value: ${{ parameters.AzDOPipelineId }} - - name: AzDOBuildId - value: ${{ parameters.AzDOBuildId }} - - template: /eng/common/templates/variables/sdl-variables.yml - - name: GuardianVersion - value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - template: /eng/common/templates/variables/pool-providers.yml - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - checkout: self - clean: true - - # If the template caller didn't provide an AzDO parameter, set them all up as Maestro vars. - - ${{ if not(and(parameters.AzDOProjectName, parameters.AzDOPipelineId, parameters.AzDOBuildId)) }}: - - template: /eng/common/templates/post-build/setup-maestro-vars.yml - - - ${{ if ne(parameters.downloadArtifacts, 'false')}}: - - ${{ if ne(parameters.artifactNames, '') }}: - - ${{ each artifactName in parameters.artifactNames }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Build Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: ${{ artifactName }} - downloadPath: $(Build.ArtifactStagingDirectory)\artifacts - checkDownloadedFiles: true - - ${{ if eq(parameters.artifactNames, '') }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Build Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - downloadType: specific files - itemPattern: "**" - downloadPath: $(Build.ArtifactStagingDirectory)\artifacts - checkDownloadedFiles: true - - - ${{ each artifactName in parameters.pipelineArtifactNames }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Pipeline Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: ${{ artifactName }} - downloadPath: $(Build.ArtifactStagingDirectory)\artifacts - checkDownloadedFiles: true - - - powershell: eng/common/sdl/trim-assets-version.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts - displayName: Trim the version from the NuGet packages - continueOnError: ${{ parameters.sdlContinueOnError }} - - - powershell: eng/common/sdl/extract-artifact-packages.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts - -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts - displayName: Extract Blob Artifacts - continueOnError: ${{ parameters.sdlContinueOnError }} - - - powershell: eng/common/sdl/extract-artifact-packages.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts - -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts - displayName: Extract Package Artifacts - continueOnError: ${{ parameters.sdlContinueOnError }} - - - ${{ if ne(parameters.extractArchiveArtifacts, 'false') }}: - - powershell: eng/common/sdl/extract-artifact-archives.ps1 - -InputPath $(Build.ArtifactStagingDirectory)\artifacts - -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts - displayName: Extract Archive Artifacts - continueOnError: ${{ parameters.sdlContinueOnError }} - - - template: /eng/common/templates/steps/execute-sdl.yml - parameters: - overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} - executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} - overrideParameters: ${{ parameters.overrideParameters }} - additionalParameters: ${{ parameters.additionalParameters }} - publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} - sdlContinueOnError: ${{ parameters.sdlContinueOnError }} diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 8ec5c4f2d9f91..1cf9a6d48127b 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -1,259 +1,61 @@ -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - -parameters: -# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - cancelTimeoutInMinutes: '' - condition: '' - container: '' - continueOnError: false - dependsOn: '' - displayName: '' - pool: '' - steps: [] - strategy: '' - timeoutInMinutes: '' - variables: [] - workspace: '' - templateContext: '' - -# Job base template specific parameters - # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md - artifacts: '' - enableMicrobuild: false +parameters: enablePublishBuildArtifacts: false - enablePublishBuildAssets: false - enablePublishTestResults: false - enablePublishUsingPipelines: false - enableBuildRetry: false - disableComponentGovernance: '' - componentGovernanceIgnoreDirectories: '' - mergeTestResults: false - testRunTitle: '' - testResultsFormat: '' - name: '' - preSteps: [] - runAsPublic: false -# Sbom related params - enableSbom: true - PackageVersion: 7.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' jobs: -- job: ${{ parameters.name }} - - ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}: - cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }} - - ${{ if ne(parameters.condition, '') }}: - condition: ${{ parameters.condition }} - - ${{ if ne(parameters.container, '') }}: - container: ${{ parameters.container }} - - ${{ if ne(parameters.continueOnError, '') }}: - continueOnError: ${{ parameters.continueOnError }} - - ${{ if ne(parameters.dependsOn, '') }}: - dependsOn: ${{ parameters.dependsOn }} - - ${{ if ne(parameters.displayName, '') }}: - displayName: ${{ parameters.displayName }} - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - - ${{ if ne(parameters.strategy, '') }}: - strategy: ${{ parameters.strategy }} - - ${{ if ne(parameters.timeoutInMinutes, '') }}: - timeoutInMinutes: ${{ parameters.timeoutInMinutes }} - - ${{ if ne(parameters.templateContext, '') }}: - templateContext: ${{ parameters.templateContext }} - - variables: - - ${{ if ne(parameters.enableTelemetry, 'false') }}: - - name: DOTNET_CLI_TELEMETRY_PROFILE - value: '$(Build.Repository.Uri)' - - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}: - - name: EnableRichCodeNavigation - value: 'true' - # Retry signature validation up to three times, waiting 2 seconds between attempts. - # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures - - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY - value: 3,2000 - - ${{ each variable in parameters.variables }}: - # handle name-value variable syntax - # example: - # - name: [key] - # value: [value] - - ${{ if ne(variable.name, '') }}: - - name: ${{ variable.name }} - value: ${{ variable.value }} - - # handle variable groups - - ${{ if ne(variable.group, '') }}: - - group: ${{ variable.group }} - - # handle template variable syntax - # example: - # - template: path/to/template.yml - # parameters: - # [key]: [value] - - ${{ if ne(variable.template, '') }}: - - template: ${{ variable.template }} - ${{ if ne(variable.parameters, '') }}: - parameters: ${{ variable.parameters }} - - # handle key-value variable syntax. - # example: - # - [key]: [value] - - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}: - - ${{ each pair in variable }}: - - name: ${{ pair.key }} - value: ${{ pair.value }} - - # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds - - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: DotNet-HelixApi-Access - - ${{ if ne(parameters.workspace, '') }}: - workspace: ${{ parameters.workspace }} - - steps: - - ${{ if ne(parameters.preSteps, '') }}: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - task: MicroBuildSigningPlugin@3 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - env: - TeamName: $(_TeamName) - continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - - - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - - task: NuGetAuthenticate@1 - - - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - - task: DownloadPipelineArtifact@2 - inputs: - buildType: current - artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }} - targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }} - itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }} - - - ${{ each step in parameters.steps }}: - - ${{ step }} - - - ${{ if eq(parameters.enableRichCodeNavigation, true) }}: - - task: RichCodeNavIndexer@0 - displayName: RichCodeNav Upload - inputs: - languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} - environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'production') }} - richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin - uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }} - continueOnError: true - - - template: /eng/common/templates/steps/component-governance.yml - parameters: - ${{ if eq(parameters.disableComponentGovernance, '') }}: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}: - disableComponentGovernance: false - ${{ else }}: - disableComponentGovernance: true - ${{ else }}: - disableComponentGovernance: ${{ parameters.disableComponentGovernance }} - componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: MicroBuildCleanup@1 - displayName: Execute Microbuild cleanup tasks - condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - env: - TeamName: $(_TeamName) - - - ${{ if ne(parameters.artifacts.publish, '') }}: - - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - - task: CopyFiles@2 - displayName: Gather binaries for publish to artifacts - inputs: - SourceFolder: 'artifacts/bin' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin' - - task: CopyFiles@2 - displayName: Gather packages for publish to artifacts - inputs: - SourceFolder: 'artifacts/packages' - Contents: '**' - TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages' - - task: PublishBuildArtifacts@1 - displayName: Publish pipeline artifacts - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/artifacts' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} - continueOnError: true - condition: always() - - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - - publish: artifacts/log - artifact: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} - displayName: Publish logs - continueOnError: true - condition: always() - - - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: - - task: PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' - PublishLocation: Container - ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} - continueOnError: true - condition: always() - - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}: - - task: PublishTestResults@2 - displayName: Publish XUnit Test Results - inputs: - testResultsFormat: 'xUnit' - testResultsFiles: '*.xml' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}: - - task: PublishTestResults@2 - displayName: Publish TRX Test Results - inputs: - testResultsFormat: 'VSTest' - testResultsFiles: '*.trx' - searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)' - testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx - mergeTestResults: ${{ parameters.mergeTestResults }} - continueOnError: true - condition: always() - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}: - - template: /eng/common/templates/steps/generate-sbom.yml - parameters: - PackageVersion: ${{ parameters.packageVersion}} - BuildDropPath: ${{ parameters.buildDropPath }} - IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} - - - ${{ if eq(parameters.enableBuildRetry, 'true') }}: - - publish: $(Build.SourcesDirectory)\eng\common\BuildConfiguration - artifact: BuildConfiguration - displayName: Publish build retry configuration - continueOnError: true +- template: /eng/common/core-templates/job/job.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ if and(ne(parameter.key, 'steps'), ne(parameter.key, 'is1ESPipeline')) }}: + ${{ parameter.key }}: ${{ parameter.value }} + + steps: + - ${{ each step in parameters.steps }}: + - ${{ step }} + + artifactPublishSteps: + - ${{ if ne(parameters.artifacts.publish, '') }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish pipeline artifacts + pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts' + publishLocation: Container + artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} + continueOnError: true + condition: always() + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log' + artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} + displayName: 'Publish logs' + continueOnError: true + condition: always() + + - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}: + - template: /eng/common/core-templates/steps/publish-build-artifacts.yml + parameters: + is1ESPipeline: false + args: + displayName: Publish Logs + pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)' + publishLocation: Container + artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }} + continueOnError: true + condition: always() + + - ${{ if eq(parameters.enableBuildRetry, 'true') }}: + - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml + parameters: + is1ESPipeline: false + args: + targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration' + artifactName: 'BuildConfiguration' + displayName: 'Publish build retry configuration' + continueOnError: true diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml index 60ab00c4de3ac..ff829dc4c700c 100644 --- a/eng/common/templates/job/onelocbuild.yml +++ b/eng/common/templates/job/onelocbuild.yml @@ -1,109 +1,7 @@ -parameters: - # Optional: dependencies of the job - dependsOn: '' - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: '' - - CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex - GithubPat: $(BotAccount-dotnet-bot-repo-PAT) - - SourcesDirectory: $(Build.SourcesDirectory) - CreatePr: true - AutoCompletePr: false - ReusePr: true - UseLfLineEndings: true - UseCheckedInLocProjectJson: false - SkipLocProjectJsonGeneration: false - LanguageSet: VS_Main_Languages - LclSource: lclFilesInRepo - LclPackageId: '' - RepoType: gitHub - GitHubOrg: dotnet - MirrorRepo: '' - MirrorBranch: main - condition: '' - JobNameSuffix: '' - jobs: -- job: OneLocBuild${{ parameters.JobNameSuffix }} - - dependsOn: ${{ parameters.dependsOn }} - - displayName: OneLocBuild${{ parameters.JobNameSuffix }} - - variables: - - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat - - name: _GenerateLocProjectArguments - value: -SourcesDirectory ${{ parameters.SourcesDirectory }} - -LanguageSet "${{ parameters.LanguageSet }}" - -CreateNeutralXlfs - - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}: - - name: _GenerateLocProjectArguments - value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson - - template: /eng/common/templates/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}: - - task: Powershell@2 - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1 - arguments: $(_GenerateLocProjectArguments) - displayName: Generate LocProject.json - condition: ${{ parameters.condition }} - - - task: OneLocBuild@2 - displayName: OneLocBuild - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - inputs: - locProj: eng/Localize/LocProject.json - outDir: $(Build.ArtifactStagingDirectory) - lclSource: ${{ parameters.LclSource }} - lclPackageId: ${{ parameters.LclPackageId }} - isCreatePrSelected: ${{ parameters.CreatePr }} - isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} - ${{ if eq(parameters.CreatePr, true) }}: - isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - isShouldReusePrSelected: ${{ parameters.ReusePr }} - packageSourceAuth: patAuth - patVariable: ${{ parameters.CeapexPat }} - ${{ if eq(parameters.RepoType, 'gitHub') }}: - repoType: ${{ parameters.RepoType }} - gitHubPatVariable: "${{ parameters.GithubPat }}" - ${{ if ne(parameters.MirrorRepo, '') }}: - isMirrorRepoSelected: true - gitHubOrganization: ${{ parameters.GitHubOrg }} - mirrorRepo: ${{ parameters.MirrorRepo }} - mirrorBranch: ${{ parameters.MirrorBranch }} - condition: ${{ parameters.condition }} - - - task: PublishBuildArtifacts@1 - displayName: Publish Localization Files - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/loc' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} +- template: /eng/common/core-templates/job/onelocbuild.yml + parameters: + is1ESPipeline: false - - task: PublishBuildArtifacts@1 - displayName: Publish LocProject.json - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/Localize/' - PublishLocation: Container - ArtifactName: Loc - condition: ${{ parameters.condition }} \ No newline at end of file + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index 8ec0151def21a..ab2edec2adb54 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -1,151 +1,7 @@ -parameters: - configuration: 'Debug' - - # Optional: condition for the job to run - condition: '' - - # Optional: 'true' if future jobs should run even if this job fails - continueOnError: false - - # Optional: dependencies of the job - dependsOn: '' - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool - pool: {} - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishUsingPipelines: false - - # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing - publishAssetsImmediately: false - - artifactsPublishingAdditionalParameters: '' - - signingValidationAdditionalParameters: '' - jobs: -- job: Asset_Registry_Publish - - dependsOn: ${{ parameters.dependsOn }} - timeoutInMinutes: 150 - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - displayName: Publish Assets - ${{ else }}: - displayName: Publish to Build Asset Registry - - variables: - - template: /eng/common/templates/variables/pool-providers.yml - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: Publish-Build-Assets - - group: AzureDevOps-Artifact-Feeds-Pats - - name: runCodesignValidationInjection - value: false - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates/post-build/common-variables.yml - - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}: - name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download artifact - inputs: - artifactName: AssetManifests - downloadPath: '$(Build.StagingDirectory)/Download' - checkDownloadedFiles: true - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: NuGetAuthenticate@1 - - - task: PowerShell@2 - displayName: Publish Build Assets - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet - /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro.dot.net - /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} - /p:OfficialBuildId=$(Build.BuildNumber) - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - - task: powershell@2 - displayName: Create ReleaseConfigs Artifact - inputs: - targetType: inline - script: | - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(BARBuildId) - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value "$(DefaultChannels)" - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsStableBuild) - - - task: PublishBuildArtifacts@1 - displayName: Publish ReleaseConfigs Artifact - inputs: - PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs.txt' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - task: powershell@2 - displayName: Check if SymbolPublishingExclusionsFile.txt exists - inputs: - targetType: inline - script: | - $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" - if(Test-Path -Path $symbolExclusionfile) - { - Write-Host "SymbolExclusionFile exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" - } - else{ - Write-Host "Symbols Exclusion file does not exists" - Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" - } - - - task: PublishBuildArtifacts@1 - displayName: Publish SymbolPublishingExclusionsFile Artifact - condition: eq(variables['SymbolExclusionFile'], 'true') - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' - PublishLocation: Container - ArtifactName: ReleaseConfigs - - - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}: - - template: /eng/common/templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion 3 - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' +- template: /eng/common/core-templates/job/publish-build-assets.yml + parameters: + is1ESPipeline: false - - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - - template: /eng/common/templates/steps/publish-logs.yml - parameters: - JobLabel: 'Publish_Artifacts_Logs' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml index 8a3deef2b7274..e44d47b1d760c 100644 --- a/eng/common/templates/job/source-build.yml +++ b/eng/common/templates/job/source-build.yml @@ -1,66 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. The template produces a server job with a - # default ID 'Source_Build_Complete' to put in a dependency list if necessary. - - # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed. - jobNamePrefix: 'Source_Build' - - # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for - # managed-only repositories. This is an object with these properties: - # - # name: '' - # The name of the job. This is included in the job ID. - # targetRID: '' - # The name of the target RID to use, instead of the one auto-detected by Arcade. - # nonPortable: false - # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than - # linux-x64), and compiling against distro-provided packages rather than portable ones. - # skipPublishValidation: false - # Disables publishing validation. By default, a check is performed to ensure no packages are - # published by source-build. - # container: '' - # A container to use. Runs in docker. - # pool: {} - # A pool to use. Runs directly on an agent. - # buildScript: '' - # Specifies the build script to invoke to perform the build in the repo. The default - # './build.sh' should work for typical Arcade repositories, but this is customizable for - # difficult situations. - # jobProperties: {} - # A list of job properties to inject at the top level, for potential extensibility beyond - # container and pool. - platform: {} - jobs: -- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} - displayName: Source-Build (${{ parameters.platform.name }}) - - ${{ each property in parameters.platform.jobProperties }}: - ${{ property.key }}: ${{ property.value }} - - ${{ if ne(parameters.platform.container, '') }}: - container: ${{ parameters.platform.container }} - - ${{ if eq(parameters.platform.pool, '') }}: - # The default VM host AzDO pool. This should be capable of running Docker containers: almost all - # source-build builds run in Docker, including the default managed platform. - # /eng/common/templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open - - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 - - ${{ if ne(parameters.platform.pool, '') }}: - pool: ${{ parameters.platform.pool }} - - workspace: - clean: all +- template: /eng/common/core-templates/job/source-build.yml + parameters: + is1ESPipeline: false - steps: - - template: /eng/common/templates/steps/source-build.yml - parameters: - platform: ${{ parameters.platform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml index b98202aa02d82..89f3291593cb7 100644 --- a/eng/common/templates/job/source-index-stage1.yml +++ b/eng/common/templates/job/source-index-stage1.yml @@ -1,67 +1,7 @@ -parameters: - runAsPublic: false - sourceIndexPackageVersion: 1.0.1-20230228.2 - sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json - sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" - preSteps: [] - binlogPath: artifacts/log/Debug/Build.binlog - condition: '' - dependsOn: '' - pool: '' - jobs: -- job: SourceIndexStage1 - dependsOn: ${{ parameters.dependsOn }} - condition: ${{ parameters.condition }} - variables: - - name: SourceIndexPackageVersion - value: ${{ parameters.sourceIndexPackageVersion }} - - name: SourceIndexPackageSource - value: ${{ parameters.sourceIndexPackageSource }} - - name: BinlogPath - value: ${{ parameters.binlogPath }} - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - group: source-dot-net stage1 variables - - template: /eng/common/templates/variables/pool-providers.yml - - ${{ if ne(parameters.pool, '') }}: - pool: ${{ parameters.pool }} - ${{ if eq(parameters.pool, '') }}: - pool: - ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: $(DncEngPublicBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64.open - ${{ if eq(variables['System.TeamProject'], 'internal') }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - ${{ each preStep in parameters.preSteps }}: - - ${{ preStep }} - - - task: UseDotNet@2 - displayName: Use .NET Core SDK 6 - inputs: - packageType: sdk - version: 6.0.x - installationPath: $(Agent.TempDirectory)/dotnet - workingDirectory: $(Agent.TempDirectory) - - - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools - displayName: Download Tools - # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. - workingDirectory: $(Agent.TempDirectory) - - - script: ${{ parameters.sourceIndexBuildCommand }} - displayName: Build Repository - - - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output - displayName: Process Binlog into indexable sln +- template: /eng/common/core-templates/job/source-index-stage1.yml + parameters: + is1ESPipeline: false - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) - displayName: Upload stage1 artifacts to source index - env: - BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/codeql-build.yml b/eng/common/templates/jobs/codeql-build.yml index f7dc5ea4aaa63..517f24d6a52ce 100644 --- a/eng/common/templates/jobs/codeql-build.yml +++ b/eng/common/templates/jobs/codeql-build.yml @@ -1,31 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - jobs: -- template: /eng/common/templates/jobs/jobs.yml +- template: /eng/common/core-templates/jobs/codeql-build.yml parameters: - enableMicrobuild: false - enablePublishBuildArtifacts: false - enablePublishTestResults: false - enablePublishBuildAssets: false - enablePublishUsingPipelines: false - enableTelemetry: true + is1ESPipeline: false - variables: - - group: Publish-Build-Assets - # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in - # sync with the packages.config file. - - name: DefaultGuardianVersion - value: 0.109.0 - - name: GuardianPackagesConfigFile - value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config - - name: GuardianVersion - value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} - - jobs: ${{ parameters.jobs }} - + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml index 289bb2396ce83..388e9037b3e60 100644 --- a/eng/common/templates/jobs/jobs.yml +++ b/eng/common/templates/jobs/jobs.yml @@ -1,97 +1,7 @@ -parameters: - # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md - continueOnError: false - - # Optional: Include PublishBuildArtifacts task - enablePublishBuildArtifacts: false - - # Optional: Enable publishing using release pipelines - enablePublishUsingPipelines: false - - # Optional: Enable running the source-build jobs to build repo from source - enableSourceBuild: false - - # Optional: Parameters for source-build template. - # See /eng/common/templates/jobs/source-build.yml for options - sourceBuildParameters: [] - - graphFileGeneration: - # Optional: Enable generating the graph files at the end of the build - enabled: false - # Optional: Include toolset dependencies in the generated graph files - includeToolset: false - - # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job - jobs: [] - - # Optional: Override automatically derived dependsOn value for "publish build assets" job - publishBuildAssetsDependsOn: '' - - # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage. - publishAssetsImmediately: false - - # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml) - artifactsPublishingAdditionalParameters: '' - signingValidationAdditionalParameters: '' - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - enableSourceIndex: false - sourceIndexParams: {} - -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - jobs: -- ${{ each job in parameters.jobs }}: - - template: ../job/job.yml - parameters: - # pass along parameters - ${{ each parameter in parameters }}: - ${{ if ne(parameter.key, 'jobs') }}: - ${{ parameter.key }}: ${{ parameter.value }} - - # pass along job properties - ${{ each property in job }}: - ${{ if ne(property.key, 'job') }}: - ${{ property.key }}: ${{ property.value }} - - name: ${{ job.job }} - -- ${{ if eq(parameters.enableSourceBuild, true) }}: - - template: /eng/common/templates/jobs/source-build.yml - parameters: - allCompletedJobId: Source_Build_Complete - ${{ each parameter in parameters.sourceBuildParameters }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if eq(parameters.enableSourceIndex, 'true') }}: - - template: ../job/source-index-stage1.yml - parameters: - runAsPublic: ${{ parameters.runAsPublic }} - ${{ each parameter in parameters.sourceIndexParams }}: - ${{ parameter.key }}: ${{ parameter.value }} - -- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: - - template: ../job/publish-build-assets.yml - parameters: - continueOnError: ${{ parameters.continueOnError }} - dependsOn: - - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.publishBuildAssetsDependsOn }}: - - ${{ job.job }} - - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: - - ${{ each job in parameters.jobs }}: - - ${{ job.job }} - - ${{ if eq(parameters.enableSourceBuild, true) }}: - - Source_Build_Complete +- template: /eng/common/core-templates/jobs/jobs.yml + parameters: + is1ESPipeline: false - runAsPublic: ${{ parameters.runAsPublic }} - publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }} - publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }} - enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/jobs/source-build.yml b/eng/common/templates/jobs/source-build.yml index a15b07eb51d9d..818d4c326dbbf 100644 --- a/eng/common/templates/jobs/source-build.yml +++ b/eng/common/templates/jobs/source-build.yml @@ -1,46 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. A job is created for each platform, as - # well as an optional server job that completes when all platform jobs complete. - - # The name of the "join" job for all source-build platforms. If set to empty string, the job is - # not included. Existing repo pipelines can use this job depend on all source-build jobs - # completing without maintaining a separate list of every single job ID: just depend on this one - # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'. - allCompletedJobId: '' - - # See /eng/common/templates/job/source-build.yml - jobNamePrefix: 'Source_Build' - - # This is the default platform provided by Arcade, intended for use by a managed-only repo. - defaultManagedPlatform: - name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8' - - # Defines the platforms on which to run build jobs. One job is created for each platform, and the - # object in this array is sent to the job template as 'platform'. If no platforms are specified, - # one job runs on 'defaultManagedPlatform'. - platforms: [] - jobs: +- template: /eng/common/core-templates/jobs/source-build.yml + parameters: + is1ESPipeline: false -- ${{ if ne(parameters.allCompletedJobId, '') }}: - - job: ${{ parameters.allCompletedJobId }} - displayName: Source-Build Complete - pool: server - dependsOn: - - ${{ each platform in parameters.platforms }}: - - ${{ parameters.jobNamePrefix }}_${{ platform.name }} - - ${{ if eq(length(parameters.platforms), 0) }}: - - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} - -- ${{ each platform in parameters.platforms }}: - - template: /eng/common/templates/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ platform }} - -- ${{ if eq(length(parameters.platforms), 0) }}: - - template: /eng/common/templates/job/source-build.yml - parameters: - jobNamePrefix: ${{ parameters.jobNamePrefix }} - platform: ${{ parameters.defaultManagedPlatform }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index 173914f2364a7..7fa105875592c 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -1,22 +1,8 @@ variables: - - group: Publish-Build-Assets +- template: /eng/common/core-templates/post-build/common-variables.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false - # Whether the build is internal or not - - name: IsInternalBuild - value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} - - # Default Maestro++ API Endpoint and API Version - - name: MaestroApiEndPoint - value: "https://maestro.dot.net" - - name: MaestroApiAccessToken - value: $(MaestroAccessToken) - - name: MaestroApiVersion - value: "2020-02-20" - - - name: SourceLinkCLIVersion - value: 3.0.0 - - name: SymbolToolVersion - value: 1.0.1 - - - name: runCodesignValidationInjection - value: false + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index aba44a25a3387..53ede714bdd20 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -1,281 +1,8 @@ -parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - stages: -- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - - stage: Validate - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Validate Build Assets - variables: - - template: common-variables.yml - - template: /eng/common/templates/variables/pool-providers.yml - jobs: - - job: - displayName: NuGet Validation - condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - -ToolDestinationPath $(Agent.BuildDirectory)/Extract/ - - - job: - displayName: Signing Validation - condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - itemPattern: | - ** - !**/Microsoft.SourceBuild.Intermediate.*.nupkg - - # This is necessary whenever we want to publish/restore to an AzDO private feed - # Since sdk-task.ps1 tries to restore packages we need to do this authentication here - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: ../steps/publish-logs.yml - parameters: - StageLabel: 'Validation' - JobLabel: 'Signing' - - - job: - displayName: SourceLink Validation - condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true - - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) - -GHCommit $(Build.SourceVersion) - -SourcelinkCliVersion $(SourceLinkCLIVersion) - continueOnError: true - - - template: /eng/common/templates/job/execute-sdl.yml - parameters: - enable: ${{ parameters.SDLValidationParameters.enable }} - publishGuardianDirectoryToPipeline: ${{ parameters.SDLValidationParameters.publishGdn }} - additionalParameters: ${{ parameters.SDLValidationParameters.params }} - continueOnError: ${{ parameters.SDLValidationParameters.continueOnError }} - artifactNames: ${{ parameters.SDLValidationParameters.artifactNames }} - downloadArtifacts: ${{ parameters.SDLValidationParameters.downloadArtifacts }} - -- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - - stage: publish_using_darc - ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - dependsOn: ${{ parameters.publishDependsOn }} - ${{ else }}: - dependsOn: ${{ parameters.validateDependsOn }} - displayName: Publish using Darc - variables: - - template: common-variables.yml - - template: /eng/common/templates/variables/pool-providers.yml - jobs: - - job: - displayName: Publish Using Darc - timeoutInMinutes: 120 - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: VSEngSS-MicroBuild2022-1ES - demands: Cmd - # If it's not devdiv, it's dnceng - ${{ else }}: - name: NetCore1ESPool-Publishing-Internal - demands: ImageOverride -equals windows.vs2019.amd64 - steps: - - template: setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - - - task: NuGetAuthenticate@1 +- template: /eng/common/core-templates/post-build/post-build.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false - - task: PowerShell@2 - displayName: Publish Using Darc - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: -BuildId $(BARBuildId) - -PublishingInfraVersion ${{ parameters.publishingInfraVersion }} - -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)' - -MaestroToken '$(MaestroApiAccessToken)' - -WaitPublishingFinish true - -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' - -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/post-build/setup-maestro-vars.yml b/eng/common/templates/post-build/setup-maestro-vars.yml index 0c87f149a4ad7..a79fab5b441e8 100644 --- a/eng/common/templates/post-build/setup-maestro-vars.yml +++ b/eng/common/templates/post-build/setup-maestro-vars.yml @@ -1,70 +1,8 @@ -parameters: - BARBuildId: '' - PromoteToChannelIds: '' - steps: - - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Release Configs - inputs: - buildType: current - artifactName: ReleaseConfigs - checkDownloadedFiles: true - - - task: PowerShell@2 - name: setReleaseVars - displayName: Set Release Configs Vars - inputs: - targetType: inline - pwsh: true - script: | - try { - if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') { - $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt - - $BarId = $Content | Select -Index 0 - $Channels = $Content | Select -Index 1 - $IsStableBuild = $Content | Select -Index 2 - - $AzureDevOpsProject = $Env:System_TeamProject - $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId - $AzureDevOpsBuildId = $Env:Build_BuildId - } - else { - $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}" - - $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]' - $apiHeaders.Add('Accept', 'application/json') - $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}") - - $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" } - - $BarId = $Env:BARBuildId - $Channels = $Env:PromoteToMaestroChannels -split "," - $Channels = $Channels -join "][" - $Channels = "[$Channels]" - - $IsStableBuild = $buildInfo.stable - $AzureDevOpsProject = $buildInfo.azureDevOpsProject - $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId - $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId - } - - Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId" - Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels" - Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild" +- template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + # Specifies whether to use 1ES + is1ESPipeline: false - Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject" - Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId" - Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId" - } - catch { - Write-Host $_ - Write-Host $_.Exception - Write-Host $_.ScriptStackTrace - exit 1 - } - env: - MAESTRO_API_TOKEN: $(MaestroApiAccessToken) - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }} + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} \ No newline at end of file diff --git a/eng/common/templates/steps/add-build-to-channel.yml b/eng/common/templates/steps/add-build-to-channel.yml index f67a210d62f3e..42bbba161b9b6 100644 --- a/eng/common/templates/steps/add-build-to-channel.yml +++ b/eng/common/templates/steps/add-build-to-channel.yml @@ -1,13 +1,7 @@ -parameters: - ChannelId: 0 - steps: -- task: PowerShell@2 - displayName: Add Build to Channel - inputs: - filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1 - arguments: -BuildId $(BARBuildId) - -ChannelId ${{ parameters.ChannelId }} - -MaestroApiAccessToken $(MaestroApiAccessToken) - -MaestroApiEndPoint $(MaestroApiEndPoint) - -MaestroApiVersion $(MaestroApiVersion) +- template: /eng/common/core-templates/steps/add-build-to-channel.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/build-reason.yml b/eng/common/templates/steps/build-reason.yml deleted file mode 100644 index eba58109b52c9..0000000000000 --- a/eng/common/templates/steps/build-reason.yml +++ /dev/null @@ -1,12 +0,0 @@ -# build-reason.yml -# Description: runs steps if build.reason condition is valid. conditions is a string of valid build reasons -# to include steps (',' separated). -parameters: - conditions: '' - steps: [] - -steps: - - ${{ if and( not(startsWith(parameters.conditions, 'not')), contains(parameters.conditions, variables['build.reason'])) }}: - - ${{ parameters.steps }} - - ${{ if and( startsWith(parameters.conditions, 'not'), not(contains(parameters.conditions, variables['build.reason']))) }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml index cbba0596709da..c12a5f8d21d76 100644 --- a/eng/common/templates/steps/component-governance.yml +++ b/eng/common/templates/steps/component-governance.yml @@ -1,13 +1,7 @@ -parameters: - disableComponentGovernance: false - componentGovernanceIgnoreDirectories: '' - steps: -- ${{ if eq(parameters.disableComponentGovernance, 'true') }}: - - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true" - displayName: Set skipComponentGovernanceDetection variable -- ${{ if ne(parameters.disableComponentGovernance, 'true') }}: - - task: ComponentGovernanceComponentDetection@0 - continueOnError: true - inputs: - ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} \ No newline at end of file +- template: /eng/common/core-templates/steps/component-governance.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/execute-codeql.yml b/eng/common/templates/steps/execute-codeql.yml deleted file mode 100644 index 3930b1630214b..0000000000000 --- a/eng/common/templates/steps/execute-codeql.yml +++ /dev/null @@ -1,32 +0,0 @@ -parameters: - # Language that should be analyzed. Defaults to csharp - language: csharp - # Build Commands - buildCommands: '' - overrideParameters: '' # Optional: to override values for parameters. - additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' - # Optional: if specified, restore and use this version of Guardian instead of the default. - overrideGuardianVersion: '' - # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth - # diagnosis of problems with specific tool configurations. - publishGuardianDirectoryToPipeline: false - # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL - # parameters rather than relying on YAML. It may be better to use a local script, because you can - # reproduce results locally without piecing together a command based on the YAML. - executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' - # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named - # 'continueOnError', the parameter value is not correctly picked up. - # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter - # optional: determines whether to continue the build if the step errors; - sdlContinueOnError: false - -steps: -- template: /eng/common/templates/steps/execute-sdl.yml - parameters: - overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }} - executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }} - overrideParameters: ${{ parameters.overrideParameters }} - additionalParameters: '${{ parameters.additionalParameters }} - -CodeQLAdditionalRunConfigParams @("BuildCommands < ${{ parameters.buildCommands }}", "Language < ${{ parameters.language }}")' - publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }} - sdlContinueOnError: ${{ parameters.sdlContinueOnError }} \ No newline at end of file diff --git a/eng/common/templates/steps/execute-sdl.yml b/eng/common/templates/steps/execute-sdl.yml deleted file mode 100644 index 07426fde05d82..0000000000000 --- a/eng/common/templates/steps/execute-sdl.yml +++ /dev/null @@ -1,88 +0,0 @@ -parameters: - overrideGuardianVersion: '' - executeAllSdlToolsScript: '' - overrideParameters: '' - additionalParameters: '' - publishGuardianDirectoryToPipeline: false - sdlContinueOnError: false - condition: '' - -steps: -- task: NuGetAuthenticate@1 - inputs: - nuGetServiceConnections: GuardianConnect - -- task: NuGetToolInstaller@1 - displayName: 'Install NuGet.exe' - -- ${{ if ne(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts -Version ${{ parameters.overrideGuardianVersion }} - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian (Overridden) - -- ${{ if eq(parameters.overrideGuardianVersion, '') }}: - - pwsh: | - Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl - . .\sdl.ps1 - $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts - Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation" - displayName: Install Guardian - -- ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} - displayName: Execute SDL (Overridden) - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: ${{ parameters.executeAllSdlToolsScript }} - -GuardianCliLocation $(GuardianCliLocation) - -NugetPackageDirectory $(Build.SourcesDirectory)\.packages - -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) - ${{ parameters.additionalParameters }} - displayName: Execute SDL - continueOnError: ${{ parameters.sdlContinueOnError }} - condition: ${{ parameters.condition }} - -- ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: - # We want to publish the Guardian results and configuration for easy diagnosis. However, the - # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default - # tooling files. Some of these files are large and aren't useful during an investigation, so - # exclude them by simply deleting them before publishing. (As of writing, there is no documented - # way to selectively exclude a dir from the pipeline artifact publish task.) - - task: DeleteFiles@1 - displayName: Delete Guardian dependencies to avoid uploading - inputs: - SourceFolder: $(Agent.BuildDirectory)/.gdn - Contents: | - c - i - condition: succeededOrFailed() - - - publish: $(Agent.BuildDirectory)/.gdn - artifact: GuardianConfiguration - displayName: Publish GuardianConfiguration - condition: succeededOrFailed() - - # Publish the SARIF files in a container named CodeAnalysisLogs to enable integration - # with the "SARIF SAST Scans Tab" Azure DevOps extension - - task: CopyFiles@2 - displayName: Copy SARIF files - inputs: - flattenFolders: true - sourceFolder: $(Agent.BuildDirectory)/.gdn/rc/ - contents: '**/*.sarif' - targetFolder: $(Build.SourcesDirectory)/CodeAnalysisLogs - condition: succeededOrFailed() - - # Use PublishBuildArtifacts because the SARIF extension only checks this case - # see microsoft/sarif-azuredevops-extension#4 - - task: PublishBuildArtifacts@1 - displayName: Publish SARIF files to CodeAnalysisLogs container - inputs: - pathToPublish: $(Build.SourcesDirectory)/CodeAnalysisLogs - artifactName: CodeAnalysisLogs - condition: succeededOrFailed() \ No newline at end of file diff --git a/eng/common/templates/steps/generate-sbom.yml b/eng/common/templates/steps/generate-sbom.yml index 2b21eae427328..26dc00a2e0f31 100644 --- a/eng/common/templates/steps/generate-sbom.yml +++ b/eng/common/templates/steps/generate-sbom.yml @@ -1,48 +1,7 @@ -# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated. -# PackageName - The name of the package this SBOM represents. -# PackageVersion - The version of the package this SBOM represents. -# ManifestDirPath - The path of the directory where the generated manifest files will be placed -# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector. - -parameters: - PackageVersion: 8.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - PackageName: '.NET' - ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom - IgnoreDirectories: '' - sbomContinueOnError: true - steps: -- task: PowerShell@2 - displayName: Prep for SBOM generation in (Non-linux) - condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin')) - inputs: - filePath: ./eng/common/generate-sbom-prep.ps1 - arguments: ${{parameters.manifestDirPath}} - -# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461 -- script: | - chmod +x ./eng/common/generate-sbom-prep.sh - ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}} - displayName: Prep for SBOM generation in (Linux) - condition: eq(variables['Agent.Os'], 'Linux') - continueOnError: ${{ parameters.sbomContinueOnError }} - -- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 - displayName: 'Generate SBOM manifest' - continueOnError: ${{ parameters.sbomContinueOnError }} - inputs: - PackageName: ${{ parameters.packageName }} - BuildDropPath: ${{ parameters.buildDropPath }} - PackageVersion: ${{ parameters.packageVersion }} - ManifestDirPath: ${{ parameters.manifestDirPath }} - ${{ if ne(parameters.IgnoreDirectories, '') }}: - AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' - -- task: PublishPipelineArtifact@1 - displayName: Publish SBOM manifest - continueOnError: ${{parameters.sbomContinueOnError}} - inputs: - targetPath: '${{parameters.manifestDirPath}}' - artifactName: $(ARTIFACT_NAME) +- template: /eng/common/core-templates/steps/generate-sbom.yml + parameters: + is1ESPipeline: false + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/publish-build-artifacts.yml b/eng/common/templates/steps/publish-build-artifacts.yml new file mode 100644 index 0000000000000..6428a98dfef68 --- /dev/null +++ b/eng/common/templates/steps/publish-build-artifacts.yml @@ -0,0 +1,40 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: displayName + type: string + default: 'Publish to Build Artifact' + +- name: condition + type: string + default: succeeded() + +- name: artifactName + type: string + +- name: pathToPublish + type: string + +- name: continueOnError + type: boolean + default: false + +- name: publishLocation + type: string + default: 'Container' + +steps: +- ${{ if eq(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates cannot be referenced from a 1ES managed template': error +- task: PublishBuildArtifacts@1 + displayName: ${{ parameters.displayName }} + condition: ${{ parameters.condition }} + ${{ if parameters.continueOnError }}: + continueOnError: ${{ parameters.continueOnError }} + inputs: + PublishLocation: ${{ parameters.publishLocation }} + PathtoPublish: ${{ parameters.pathToPublish }} + ${{ if parameters.artifactName }}: + ArtifactName: ${{ parameters.artifactName }} \ No newline at end of file diff --git a/eng/common/templates/steps/publish-logs.yml b/eng/common/templates/steps/publish-logs.yml index 88f238f36bfd8..4ea86bd882355 100644 --- a/eng/common/templates/steps/publish-logs.yml +++ b/eng/common/templates/steps/publish-logs.yml @@ -1,23 +1,7 @@ -parameters: - StageLabel: '' - JobLabel: '' - steps: -- task: Powershell@2 - displayName: Prepare Binlogs to Upload - inputs: - targetType: inline - script: | - New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/ - continueOnError: true - condition: always() +- template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: false -- task: PublishBuildArtifacts@1 - displayName: Publish Logs - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/PostBuildLogs' - PublishLocation: Container - ArtifactName: PostBuildLogs - continueOnError: true - condition: always() + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/publish-pipeline-artifacts.yml b/eng/common/templates/steps/publish-pipeline-artifacts.yml new file mode 100644 index 0000000000000..5dd698b212fc6 --- /dev/null +++ b/eng/common/templates/steps/publish-pipeline-artifacts.yml @@ -0,0 +1,34 @@ +parameters: +- name: is1ESPipeline + type: boolean + default: false + +- name: args + type: object + default: {} + +steps: +- ${{ if eq(parameters.is1ESPipeline, true) }}: + - 'eng/common/templates cannot be referenced from a 1ES managed template': error +- task: PublishPipelineArtifact@1 + displayName: ${{ coalesce(parameters.args.displayName, 'Publish to Build Artifact') }} + ${{ if parameters.args.condition }}: + condition: ${{ parameters.args.condition }} + ${{ else }}: + condition: succeeded() + ${{ if parameters.args.continueOnError }}: + continueOnError: ${{ parameters.args.continueOnError }} + inputs: + targetPath: ${{ parameters.args.targetPath }} + ${{ if parameters.args.artifactName }}: + artifactName: ${{ parameters.args.artifactName }} + ${{ if parameters.args.publishLocation }}: + publishLocation: ${{ parameters.args.publishLocation }} + ${{ if parameters.args.fileSharePath }}: + fileSharePath: ${{ parameters.args.fileSharePath }} + ${{ if parameters.args.Parallel }}: + parallel: ${{ parameters.args.Parallel }} + ${{ if parameters.args.parallelCount }}: + parallelCount: ${{ parameters.args.parallelCount }} + ${{ if parameters.args.properties }}: + properties: ${{ parameters.args.properties }} \ No newline at end of file diff --git a/eng/common/templates/steps/retain-build.yml b/eng/common/templates/steps/retain-build.yml index 83d97a26a01ff..8e841ace3d293 100644 --- a/eng/common/templates/steps/retain-build.yml +++ b/eng/common/templates/steps/retain-build.yml @@ -1,28 +1,7 @@ -parameters: - # Optional azure devops PAT with build execute permissions for the build's organization, - # only needed if the build that should be retained ran on a different organization than - # the pipeline where this template is executing from - Token: '' - # Optional BuildId to retain, defaults to the current running build - BuildId: '' - # Azure devops Organization URI for the build in the https://dev.azure.com/ format. - # Defaults to the organization the current pipeline is running on - AzdoOrgUri: '$(System.CollectionUri)' - # Azure devops project for the build. Defaults to the project the current pipeline is running on - AzdoProject: '$(System.TeamProject)' - steps: - - task: powershell@2 - inputs: - targetType: 'filePath' - filePath: eng/common/retain-build.ps1 - pwsh: true - arguments: > - -AzdoOrgUri: ${{parameters.AzdoOrgUri}} - -AzdoProject ${{parameters.AzdoProject}} - -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }} - -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}} - displayName: Enable permanent build retention - env: - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - BUILD_ID: $(Build.BuildId) \ No newline at end of file +- template: /eng/common/core-templates/steps/retain-build.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/run-on-unix.yml b/eng/common/templates/steps/run-on-unix.yml deleted file mode 100644 index e1733814f65dc..0000000000000 --- a/eng/common/templates/steps/run-on-unix.yml +++ /dev/null @@ -1,7 +0,0 @@ -parameters: - agentOs: '' - steps: [] - -steps: -- ${{ if ne(parameters.agentOs, 'Windows_NT') }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates/steps/run-on-windows.yml b/eng/common/templates/steps/run-on-windows.yml deleted file mode 100644 index 73e7e9c275a1f..0000000000000 --- a/eng/common/templates/steps/run-on-windows.yml +++ /dev/null @@ -1,7 +0,0 @@ -parameters: - agentOs: '' - steps: [] - -steps: -- ${{ if eq(parameters.agentOs, 'Windows_NT') }}: - - ${{ parameters.steps }} diff --git a/eng/common/templates/steps/run-script-ifequalelse.yml b/eng/common/templates/steps/run-script-ifequalelse.yml deleted file mode 100644 index 3d1242f5587c8..0000000000000 --- a/eng/common/templates/steps/run-script-ifequalelse.yml +++ /dev/null @@ -1,33 +0,0 @@ -parameters: - # if parameter1 equals parameter 2, run 'ifScript' command, else run 'elsescript' command - parameter1: '' - parameter2: '' - ifScript: '' - elseScript: '' - - # name of script step - name: Script - - # display name of script step - displayName: If-Equal-Else Script - - # environment - env: {} - - # conditional expression for step execution - condition: '' - -steps: -- ${{ if and(ne(parameters.ifScript, ''), eq(parameters.parameter1, parameters.parameter2)) }}: - - script: ${{ parameters.ifScript }} - name: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - env: ${{ parameters.env }} - condition: ${{ parameters.condition }} - -- ${{ if and(ne(parameters.elseScript, ''), ne(parameters.parameter1, parameters.parameter2)) }}: - - script: ${{ parameters.elseScript }} - name: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - env: ${{ parameters.env }} - condition: ${{ parameters.condition }} \ No newline at end of file diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml index 3eb7e2d5f840c..39f99fc2762d0 100644 --- a/eng/common/templates/steps/send-to-helix.yml +++ b/eng/common/templates/steps/send-to-helix.yml @@ -1,91 +1,7 @@ -# Please remember to update the documentation if you make changes to these parameters! -parameters: - HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' - HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number - HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues - HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group - HelixConfiguration: '' # optional -- additional property attached to a job - HelixPreCommands: '' # optional -- commands to run before Helix work item execution - HelixPostCommands: '' # optional -- commands to run after Helix work item execution - WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects - WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects - WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects - CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload - XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true - XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects - XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects - XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner - XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects - IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion - DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json - WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." - IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set - HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net ) - Creator: '' # optional -- if the build is external, use this to specify who is sending the job - DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO - condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() - continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false - steps: - - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"' - displayName: ${{ parameters.DisplayNamePrefix }} (Windows) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog - displayName: ${{ parameters.DisplayNamePrefix }} (Unix) - env: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixConfiguration: ${{ parameters.HelixConfiguration }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - WorkItemCommand: ${{ parameters.WorkItemCommand }} - WorkItemTimeout: ${{ parameters.WorkItemTimeout }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - XUnitProjects: ${{ parameters.XUnitProjects }} - XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }} - XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }} - XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }} - XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - HelixBaseUri: ${{ parameters.HelixBaseUri }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} +- template: /eng/common/core-templates/steps/send-to-helix.yml + parameters: + is1ESPipeline: false + + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml index 41bbb915736a6..23c1d6f4e9f8d 100644 --- a/eng/common/templates/steps/source-build.yml +++ b/eng/common/templates/steps/source-build.yml @@ -1,129 +1,7 @@ -parameters: - # This template adds arcade-powered source-build to CI. - - # This is a 'steps' template, and is intended for advanced scenarios where the existing build - # infra has a careful build methodology that must be followed. For example, a repo - # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline - # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to - # GitHub. Using this steps template leaves room for that infra to be included. - - # Defines the platform on which to run the steps. See 'eng/common/templates/job/source-build.yml' - # for details. The entire object is described in the 'job' template for simplicity, even though - # the usage of the properties on this object is split between the 'job' and 'steps' templates. - platform: {} - steps: -# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.) -- script: | - set -x - df -h - - # If building on the internal project, the artifact feeds variable may be available (usually only if needed) - # In that case, call the feed setup script to add internal feeds corresponding to public ones. - # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. - # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those - # changes. - internalRestoreArgs= - if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then - # Temporarily work around https://github.com/dotnet/arcade/issues/7709 - chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh - $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) - internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' - - # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. - # This only works if there is a username/email configured, which won't be the case in most CI runs. - git config --get user.email - if [ $? -ne 0 ]; then - git config user.email dn-bot@microsoft.com - git config user.name dn-bot - fi - fi - - # If building on the internal project, the internal storage variable may be available (usually only if needed) - # In that case, add variables to allow the download of internal runtimes if the specified versions are not found - # in the default public locations. - internalRuntimeDownloadArgs= - if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' - fi - - buildConfig=Release - # Check if AzDO substitutes in a build config from a variable, and use it if so. - if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then - buildConfig='$(_BuildConfig)' - fi - - officialBuildArgs= - if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then - officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' - fi - - targetRidArgs= - if [ '${{ parameters.platform.targetRID }}' != '' ]; then - targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' - fi - - runtimeOsArgs= - if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then - runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}' - fi - - baseOsArgs= - if [ '${{ parameters.platform.baseOS }}' != '' ]; then - baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' - fi - - publishArgs= - if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then - publishArgs='--publish' - fi - - assetManifestFileName=SourceBuild_RidSpecific.xml - if [ '${{ parameters.platform.name }}' != '' ]; then - assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml - fi - - ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ - --configuration $buildConfig \ - --restore --build --pack $publishArgs -bl \ - $officialBuildArgs \ - $internalRuntimeDownloadArgs \ - $internalRestoreArgs \ - $targetRidArgs \ - $runtimeOsArgs \ - $baseOsArgs \ - /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ - /p:ArcadeBuildFromSource=true \ - /p:AssetManifestFileName=$assetManifestFileName - displayName: Build - -# Upload build logs for diagnosis. -- task: CopyFiles@2 - displayName: Prepare BuildLogs staging directory - inputs: - SourceFolder: '$(Build.SourcesDirectory)' - Contents: | - **/*.log - **/*.binlog - artifacts/source-build/self/prebuilt-report/** - TargetFolder: '$(Build.StagingDirectory)/BuildLogs' - CleanTargetFolder: true - continueOnError: true - condition: succeededOrFailed() - -- task: PublishPipelineArtifact@1 - displayName: Publish BuildLogs - inputs: - targetPath: '$(Build.StagingDirectory)/BuildLogs' - artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt) - continueOnError: true - condition: succeededOrFailed() +- template: /eng/common/core-templates/steps/source-build.yml + parameters: + is1ESPipeline: false -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- task: ComponentGovernanceComponentDetection@0 - displayName: Component Detection (Exclude upstream cache) - inputs: - ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache' + ${{ each parameter in parameters }}: + ${{ parameter.key }}: ${{ parameter.value }} diff --git a/eng/common/templates/steps/telemetry-end.yml b/eng/common/templates/steps/telemetry-end.yml deleted file mode 100644 index fadc04ca1b9a3..0000000000000 --- a/eng/common/templates/steps/telemetry-end.yml +++ /dev/null @@ -1,102 +0,0 @@ -parameters: - maxRetries: 5 - retryDelay: 10 # in seconds - -steps: -- bash: | - if [ "$AGENT_JOBSTATUS" = "Succeeded" ] || [ "$AGENT_JOBSTATUS" = "PartiallySucceeded" ]; then - errorCount=0 - else - errorCount=1 - fi - warningCount=0 - - curlStatus=1 - retryCount=0 - # retry loop to harden against spotty telemetry connections - # we don't retry successes and 4xx client errors - until [[ $curlStatus -eq 0 || ( $curlStatus -ge 400 && $curlStatus -le 499 ) || $retryCount -ge $MaxRetries ]] - do - if [ $retryCount -gt 0 ]; then - echo "Failed to send telemetry to Helix; waiting $RetryDelay seconds before retrying..." - sleep $RetryDelay - fi - - # create a temporary file for curl output - res=`mktemp` - - curlResult=` - curl --verbose --output $res --write-out "%{http_code}"\ - -H 'Content-Type: application/json' \ - -H "X-Helix-Job-Token: $Helix_JobToken" \ - -H 'Content-Length: 0' \ - -X POST -G "https://helix.dot.net/api/2018-03-14/telemetry/job/build/$Helix_WorkItemId/finish" \ - --data-urlencode "errorCount=$errorCount" \ - --data-urlencode "warningCount=$warningCount"` - curlStatus=$? - - if [ $curlStatus -eq 0 ]; then - if [ $curlResult -gt 299 ] || [ $curlResult -lt 200 ]; then - curlStatus=$curlResult - fi - fi - - let retryCount++ - done - - if [ $curlStatus -ne 0 ]; then - echo "Failed to Send Build Finish information after $retryCount retries" - vstsLogOutput="vso[task.logissue type=error;sourcepath=templates/steps/telemetry-end.yml;code=1;]Failed to Send Build Finish information: $curlStatus" - echo "##$vstsLogOutput" - exit 1 - fi - displayName: Send Unix Build End Telemetry - env: - # defined via VSTS variables in start-job.sh - Helix_JobToken: $(Helix_JobToken) - Helix_WorkItemId: $(Helix_WorkItemId) - MaxRetries: ${{ parameters.maxRetries }} - RetryDelay: ${{ parameters.retryDelay }} - condition: and(always(), ne(variables['Agent.Os'], 'Windows_NT')) -- powershell: | - if (($env:Agent_JobStatus -eq 'Succeeded') -or ($env:Agent_JobStatus -eq 'PartiallySucceeded')) { - $ErrorCount = 0 - } else { - $ErrorCount = 1 - } - $WarningCount = 0 - - # Basic retry loop to harden against server flakiness - $retryCount = 0 - while ($retryCount -lt $env:MaxRetries) { - try { - Invoke-RestMethod -Uri "https://helix.dot.net/api/2018-03-14/telemetry/job/build/$env:Helix_WorkItemId/finish?errorCount=$ErrorCount&warningCount=$WarningCount" -Method Post -ContentType "application/json" -Body "" ` - -Headers @{ 'X-Helix-Job-Token'=$env:Helix_JobToken } - break - } - catch { - $statusCode = $_.Exception.Response.StatusCode.value__ - if ($statusCode -ge 400 -and $statusCode -le 499) { - Write-Host "##vso[task.logissue]error Failed to send telemetry to Helix (status code $statusCode); not retrying (4xx client error)" - Write-Host "##vso[task.logissue]error ", $_.Exception.GetType().FullName, $_.Exception.Message - exit 1 - } - Write-Host "Failed to send telemetry to Helix (status code $statusCode); waiting $env:RetryDelay seconds before retrying..." - $retryCount++ - sleep $env:RetryDelay - continue - } - } - - if ($retryCount -ge $env:MaxRetries) { - Write-Host "##vso[task.logissue]error Failed to send telemetry to Helix after $retryCount retries." - exit 1 - } - displayName: Send Windows Build End Telemetry - env: - # defined via VSTS variables in start-job.ps1 - Helix_JobToken: $(Helix_JobToken) - Helix_WorkItemId: $(Helix_WorkItemId) - MaxRetries: ${{ parameters.maxRetries }} - RetryDelay: ${{ parameters.retryDelay }} - condition: and(always(),eq(variables['Agent.Os'], 'Windows_NT')) diff --git a/eng/common/templates/steps/telemetry-start.yml b/eng/common/templates/steps/telemetry-start.yml deleted file mode 100644 index 32c01ef0b553b..0000000000000 --- a/eng/common/templates/steps/telemetry-start.yml +++ /dev/null @@ -1,241 +0,0 @@ -parameters: - helixSource: 'undefined_defaulted_in_telemetry.yml' - helixType: 'undefined_defaulted_in_telemetry.yml' - buildConfig: '' - runAsPublic: false - maxRetries: 5 - retryDelay: 10 # in seconds - -steps: -- ${{ if and(eq(parameters.runAsPublic, 'false'), not(eq(variables['System.TeamProject'], 'public'))) }}: - - task: AzureKeyVault@1 - inputs: - azureSubscription: 'HelixProd_KeyVault' - KeyVaultName: HelixProdKV - SecretsFilter: 'HelixApiAccessToken' - condition: always() -- bash: | - # create a temporary file - jobInfo=`mktemp` - - # write job info content to temporary file - cat > $jobInfo < /dev/null; then echo "Curl failed; dumping some information about dotnet.microsoft.com for later investigation" - echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 + echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 || true fi echo "Will now retry the same URL with verbose logging." with_retries curl "$install_script_url" -sSL --verbose --retry 10 --create-dirs -o "$install_script" || { @@ -343,7 +341,7 @@ function InitializeBuildTool { _InitializeBuildToolCommand="msbuild" # use override if it exists - commonly set by source-build if [[ "${_OverrideArcadeInitializeBuildToolFramework:-x}" == "x" ]]; then - _InitializeBuildToolFramework="net8.0" + _InitializeBuildToolFramework="net9.0" else _InitializeBuildToolFramework="${_OverrideArcadeInitializeBuildToolFramework}" fi @@ -458,12 +456,10 @@ function MSBuild { local possiblePaths=() possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.ArcadeLogging.dll" ) possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.Arcade.Sdk.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.ArcadeLogging.dll" ) - possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.Arcade.Sdk.dll" ) possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.ArcadeLogging.dll" ) possiblePaths+=( "$toolset_dir/net7.0/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/net8.0/Microsoft.DotNet.Arcade.Sdk.dll" ) for path in "${possiblePaths[@]}"; do if [[ -f $path ]]; then selectedPath=$path @@ -510,7 +506,8 @@ function MSBuild-Core { echo "Build failed with exit code $exit_code. Check errors above." # When running on Azure Pipelines, override the returned exit code to avoid double logging. - if [[ "$ci" == "true" && -n ${SYSTEM_TEAMPROJECT:-} ]]; then + # Skip this when the build is a child of the VMR orchestrator build. + if [[ "$ci" == true && -n ${SYSTEM_TEAMPROJECT:-} && "$product_build" != true && "$properties" != *"DotNetBuildRepo=true"* ]]; then Write-PipelineSetResult -result "Failed" -message "msbuild execution failed." # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error diff --git a/global.json b/global.json index dd2c371a82896..1ad426b264b05 100644 --- a/global.json +++ b/global.json @@ -1,19 +1,19 @@ { "sdk": { - "version": "8.0.101", + "version": "9.0.100-preview.3.24204.13", "allowPrerelease": false, "rollForward": "patch" }, "tools": { - "dotnet": "8.0.101", + "dotnet": "9.0.100-preview.3.24204.13", "vs": { "version": "17.8.0" }, "xcopy-msbuild": "17.8.1-2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24225.1", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24225.1", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24265.1", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24265.1", "Microsoft.Build.Traversal": "3.4.0" } } From 502b6513bc8586a0f397cd48199ec033e0794582 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:19:02 -0700 Subject: [PATCH 385/423] rename --- .../Shared/Extensions/SourceTextExtensions.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 70e01a1eee82e..e010468e9e91c 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -19,18 +19,18 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class SourceTextExtensions { - // char segment: 4k characters. 4K * 256 * 2 (bytes per char) = 4MB - private const int CharSegmentLength = 4096; + // char array length: 4k characters. 4K * 256 * 2 (bytes per char) = 4MB + private const int CharArrayLength = 4096; // 16k characters. Equivalent to 32KB in memory. comes from SourceText char buffer size and less than large object size public const int SourceTextLengthThreshold = 32 * 1024 / sizeof(char); /// /// Note: there is a strong invariant that you only get arrays back from this that are exactly long. Putting arrays back into this of the wrong length will result in broken + /// cref="CharArrayLength"/> long. Putting arrays back into this of the wrong length will result in broken /// behavior. Do not expose this pool outside of this class. /// - private static readonly ObjectPool s_charArrayPool = new(() => new char[CharSegmentLength], 256); + private static readonly ObjectPool s_charArrayPool = new(() => new char[CharArrayLength], 256); public static void GetLineAndOffset(this SourceText text, int position, out int lineNumber, out int offset) { @@ -196,10 +196,10 @@ private static void WriteChunksTo(SourceText sourceText, ObjectWriter writer, in // chunk size using var pooledObject = s_charArrayPool.GetPooledObject(); var buffer = pooledObject.Object; - Contract.ThrowIfTrue(buffer.Length != CharSegmentLength); + Contract.ThrowIfTrue(buffer.Length != CharArrayLength); // We write out the chunk size for sanity purposes. - writer.WriteInt32(CharSegmentLength); + writer.WriteInt32(CharArrayLength); // number of chunks var numberOfChunks = 1 + (length / buffer.Length); @@ -258,7 +258,7 @@ public static SourceText CreateSourceText( static ImmutableArray CreateChunks(int totalLength) { - var numberOfChunks = 1 + (totalLength / CharSegmentLength); + var numberOfChunks = 1 + (totalLength / CharArrayLength); var buffer = new FixedSizeArrayBuilder(numberOfChunks); for (var i = 0; i < numberOfChunks; i++) buffer.Add(s_charArrayPool.Allocate()); @@ -267,8 +267,8 @@ static ImmutableArray CreateChunks(int totalLength) } } - private static int GetIndexFromPosition(int position) => position / CharSegmentLength; - private static int GetColumnFromPosition(int position) => position % CharSegmentLength; + private static int GetIndexFromPosition(int position) => position / CharArrayLength; + private static int GetColumnFromPosition(int position) => position % CharArrayLength; private sealed class CharArrayChunkTextWriter(int totalLength, ImmutableArray chunks, Encoding encoding) : TextWriter { @@ -286,12 +286,12 @@ public override void Write(string? value) while (valueSpan.Length > 0) { var chunk = _chunks[GetIndexFromPosition(Position)]; - Contract.ThrowIfTrue(chunk.Length != CharSegmentLength); + Contract.ThrowIfTrue(chunk.Length != CharArrayLength); var chunkIndex = GetColumnFromPosition(Position); - Contract.ThrowIfTrue(chunkIndex >= CharSegmentLength); + Contract.ThrowIfTrue(chunkIndex >= CharArrayLength); - var count = Math.Min(valueSpan.Length, CharSegmentLength - chunkIndex); + var count = Math.Min(valueSpan.Length, CharArrayLength - chunkIndex); valueSpan[..count].CopyTo(chunk.AsSpan().Slice(chunkIndex, count)); Position += count; @@ -314,7 +314,7 @@ public CharArrayChunkTextReader(ImmutableArray chunks, int length) { _chunks = chunks; _disposed = false; - Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, CharSegmentLength)); + Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, CharArrayLength)); } public static TextReader CreateFromObjectReader(ObjectReader reader) @@ -327,7 +327,7 @@ public static TextReader CreateFromObjectReader(ObjectReader reader) } var chunkSize = reader.ReadInt32(); - Contract.ThrowIfTrue(chunkSize != CharSegmentLength); + Contract.ThrowIfTrue(chunkSize != CharArrayLength); var numberOfChunks = reader.ReadInt32(); // read as chunks @@ -340,14 +340,14 @@ public static TextReader CreateFromObjectReader(ObjectReader reader) var (currentChunk, currentChunkLength) = reader.ReadCharArray( static length => { - Contract.ThrowIfTrue(length > CharSegmentLength); + Contract.ThrowIfTrue(length > CharArrayLength); return s_charArrayPool.Allocate(); }); - Contract.ThrowIfTrue(currentChunk.Length != CharSegmentLength); + Contract.ThrowIfTrue(currentChunk.Length != CharArrayLength); // All but the last chunk must be completely filled. - Contract.ThrowIfTrue(i < numberOfChunks - 1 && currentChunkLength != CharSegmentLength); + Contract.ThrowIfTrue(i < numberOfChunks - 1 && currentChunkLength != CharArrayLength); chunks.Add(currentChunk); offset += currentChunkLength; From b46e963990be9efdf95bd5ed9493279ffbf729b2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:20:03 -0700 Subject: [PATCH 386/423] revert --- .../Core/Portable/Shared/Extensions/SourceTextExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index e010468e9e91c..58d706f1332aa 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class SourceTextExtensions { // char array length: 4k characters. 4K * 256 * 2 (bytes per char) = 4MB - private const int CharArrayLength = 4096; + private const int CharArrayLength = 4 * 1024; // 16k characters. Equivalent to 32KB in memory. comes from SourceText char buffer size and less than large object size public const int SourceTextLengthThreshold = 32 * 1024 / sizeof(char); From 09db6ea6d63d126f7c44ab03168255b4fe3e9851 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:23:21 -0700 Subject: [PATCH 387/423] Simplify --- .../Shared/Extensions/SourceTextExtensions.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 58d706f1332aa..55e14542348c4 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -246,7 +246,7 @@ public static SourceText CreateSourceText( var chunks = CreateChunks(totalLength); - using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!); + using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); node.WriteTo(chunkWriter); Contract.ThrowIfTrue(totalLength != chunkWriter.Position); @@ -270,10 +270,16 @@ static ImmutableArray CreateChunks(int totalLength) private static int GetIndexFromPosition(int position) => position / CharArrayLength; private static int GetColumnFromPosition(int position) => position % CharArrayLength; - private sealed class CharArrayChunkTextWriter(int totalLength, ImmutableArray chunks, Encoding encoding) : TextWriter + private sealed class CharArrayChunkTextWriter( + int totalLength, ImmutableArray chunks, Encoding encoding, CancellationToken cancellationToken) : TextWriter { private readonly int _totalLength = totalLength; private readonly ImmutableArray _chunks = chunks; + private readonly CancellationToken _cancellationToken = cancellationToken; + + /// + /// Public so that caller can assert that writing out the text actually wrote out the full text of the node. + /// public int Position; public override Encoding Encoding { get; } = encoding; @@ -285,6 +291,8 @@ public override void Write(string? value) var valueSpan = value.AsSpan(); while (valueSpan.Length > 0) { + _cancellationToken.ThrowIfCancellationRequested(); + var chunk = _chunks[GetIndexFromPosition(Position)]; Contract.ThrowIfTrue(chunk.Length != CharArrayLength); @@ -307,6 +315,9 @@ private sealed class CharArrayChunkTextReader : TextReaderWithLength private readonly ImmutableArray _chunks; private bool _disposed; + /// + /// Public so that the caller can assert that the new SourceText read all the way to the end of this successfully. + /// public int Position; public CharArrayChunkTextReader(ImmutableArray chunks, int length) @@ -374,9 +385,7 @@ protected override void Dispose(bool disposing) public override int Peek() { if (Position >= Length) - { return -1; - } return Read(Position); } @@ -384,35 +393,33 @@ public override int Peek() public override int Read() { if (Position >= Length) - { return -1; - } return Read(Position++); } + private int Read(int position) + { + var chunkIndex = GetIndexFromPosition(position); + var chunkColumn = GetColumnFromPosition(position); + + return _chunks[chunkIndex][chunkColumn]; + } + public override int Read(char[] buffer, int index, int count) { if (buffer == null) - { throw new ArgumentNullException(nameof(buffer)); - } if (index < 0 || index >= buffer.Length) - { throw new ArgumentOutOfRangeException(nameof(index)); - } if (count < 0 || (index + count) > buffer.Length) - { throw new ArgumentOutOfRangeException(nameof(count)); - } // check quick bail out if (count == 0) - { return 0; - } // adjust to actual char to read var totalCharsToRead = Math.Min(count, Length - Position); @@ -440,15 +447,8 @@ public override int Read(char[] buffer, int index, int count) } Position += totalCharsToRead; + Contract.ThrowIfTrue(Position > Length); return totalCharsToRead; } - - private int Read(int position) - { - var chunkIndex = GetIndexFromPosition(position); - var chunkColumn = GetColumnFromPosition(position); - - return _chunks[chunkIndex][chunkColumn]; - } } } From c2c2c8ef5f106d63de52622554766d641bc5148c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:26:01 -0700 Subject: [PATCH 388/423] Fixup --- .../Shared/Extensions/SourceTextExtensions.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 55e14542348c4..69643b2dce055 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -244,17 +244,28 @@ public static SourceText CreateSourceText( if (totalLength <= SourceTextLengthThreshold) return SourceText.From(node.ToFullString(), encoding, checksumAlgorithm); + // Allocate the space to write the node into. Explicitly chunked so that nothing goes into the LOH. var chunks = CreateChunks(totalLength); + try + { + // Write the node into that temp space. + using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); + node.WriteTo(chunkWriter); + Contract.ThrowIfTrue(totalLength != chunkWriter.Position); - using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); - node.WriteTo(chunkWriter); - Contract.ThrowIfTrue(totalLength != chunkWriter.Position); - - using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); - var result = textService.CreateText(chunkReader, encoding, checksumAlgorithm, cancellationToken); - Contract.ThrowIfTrue(totalLength != chunkReader.Position); + // Call into the text service to make us a SourceText from the chunks. + using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); + var result = textService.CreateText(chunkReader, encoding, checksumAlgorithm, cancellationToken); + Contract.ThrowIfTrue(totalLength != chunkReader.Position); - return result; + return result; + } + finally + { + // Finally, free the chunks so they can be used by the next caller. + foreach (var chunk in chunks) + s_charArrayPool.Free(chunk); + } static ImmutableArray CreateChunks(int totalLength) { From 6c8d03a71fd432c1da0bdbf8d19ec39e30251ae2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:28:58 -0700 Subject: [PATCH 389/423] Double dispose --- .../Shared/Extensions/SourceTextExtensions.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 69643b2dce055..47370e8c41ea7 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -246,26 +246,19 @@ public static SourceText CreateSourceText( // Allocate the space to write the node into. Explicitly chunked so that nothing goes into the LOH. var chunks = CreateChunks(totalLength); - try - { - // Write the node into that temp space. - using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); - node.WriteTo(chunkWriter); - Contract.ThrowIfTrue(totalLength != chunkWriter.Position); - // Call into the text service to make us a SourceText from the chunks. - using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); - var result = textService.CreateText(chunkReader, encoding, checksumAlgorithm, cancellationToken); - Contract.ThrowIfTrue(totalLength != chunkReader.Position); + // Write the node into that temp space. + using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); + node.WriteTo(chunkWriter); + Contract.ThrowIfTrue(totalLength != chunkWriter.Position); - return result; - } - finally - { - // Finally, free the chunks so they can be used by the next caller. - foreach (var chunk in chunks) - s_charArrayPool.Free(chunk); - } + // Call into the text service to make us a SourceText from the chunks. Disposal of this reader will free all + // the intermediary chunks we allocated. + using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); + var result = textService.CreateText(chunkReader, encoding, checksumAlgorithm, cancellationToken); + Contract.ThrowIfTrue(totalLength != chunkReader.Position); + + return result; static ImmutableArray CreateChunks(int totalLength) { From e873caada8d9e25e44a2161f62d5482bcda2d39e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:49:32 -0700 Subject: [PATCH 390/423] Switch ot LargeText --- .../Core/Portable/Shared/Extensions/SourceTextExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 47370e8c41ea7..916d026520778 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -255,7 +255,7 @@ public static SourceText CreateSourceText( // Call into the text service to make us a SourceText from the chunks. Disposal of this reader will free all // the intermediary chunks we allocated. using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); - var result = textService.CreateText(chunkReader, encoding, checksumAlgorithm, cancellationToken); + var result = SourceText.From(chunkReader, totalLength, encoding, checksumAlgorithm); Contract.ThrowIfTrue(totalLength != chunkReader.Position); return result; From 1fd8b148217ba54153f5b661cbf1402933572b31 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:51:52 -0700 Subject: [PATCH 391/423] REvert --- ...harpSyntaxTreeFactoryService.ParsedSyntaxTree.cs | 13 ++++--------- .../Shared/Extensions/SourceTextExtensions.cs | 2 +- ...asicSyntaxTreeFactoryService.ParsedSyntaxTree.vb | 13 ++++--------- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs index 93cbc12009ec6..f910dad739ce9 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -20,7 +20,6 @@ private sealed class ParsedSyntaxTree : CSharpSyntaxTree { private readonly CSharpSyntaxNode _root; private readonly SourceHashAlgorithm _checksumAlgorithm; - private readonly ITextFactoryService _textFactoryService; public override Encoding? Encoding { get; } public override CSharpParseOptions Options { get; } @@ -34,13 +33,11 @@ public ParsedSyntaxTree( CSharpParseOptions options, string filePath, Encoding? encoding, - SourceHashAlgorithm checksumAlgorithm, - ITextFactoryService textFactoryService) + SourceHashAlgorithm checksumAlgorithm) { _lazyText = lazyText; _root = CloneNodeAsRoot(root); _checksumAlgorithm = checksumAlgorithm; - _textFactoryService = textFactoryService; Encoding = encoding; Options = options; FilePath = filePath; @@ -50,8 +47,7 @@ public override SourceText GetText(CancellationToken cancellationToken) { if (_lazyText == null) { - var sourceText = SourceTextExtensions.CreateSourceText( - _textFactoryService, GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken); + var sourceText = SourceTextExtensions.CreateSourceText(GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken); Interlocked.CompareExchange(ref _lazyText, sourceText, null); } @@ -88,13 +84,12 @@ public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions opti (CSharpParseOptions)options, FilePath, Encoding, - _checksumAlgorithm, - _textFactoryService); + _checksumAlgorithm); public override SyntaxTree WithFilePath(string path) => path == FilePath ? this - : new ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm, _textFactoryService); + : new ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm); public override SyntaxReference GetReference(SyntaxNode node) => new NodeSyntaxReference(node); diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 916d026520778..659b204bbdecf 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -237,7 +237,7 @@ public static SourceText ReadFrom(ITextFactoryService textService, ObjectReader } public static SourceText CreateSourceText( - ITextFactoryService textService, SyntaxNode node, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) + SyntaxNode node, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) { // If this node is small enough to not go into the LOH, we can just fast path directly to creating a SourceText from it. var totalLength = node.FullWidth(); diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb index 7c0b81d673708..2fc2c60bd6ba7 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -5,7 +5,6 @@ Imports System.Runtime.InteropServices Imports System.Text Imports System.Threading -Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic @@ -18,7 +17,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Private ReadOnly _root As VisualBasicSyntaxNode Private ReadOnly _checksumAlgorithm As SourceHashAlgorithm - Private ReadOnly _textFactoryService As ITextFactoryService Public Overrides ReadOnly Property Encoding As Encoding Public Overrides ReadOnly Property Options As VisualBasicParseOptions @@ -32,12 +30,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic options As VisualBasicParseOptions, filePath As String, encoding As Encoding, - checksumAlgorithm As SourceHashAlgorithm, - textFactoryService As ITextFactoryService) + checksumAlgorithm As SourceHashAlgorithm) _lazyText = lazyText _root = CloneNodeAsRoot(root) _checksumAlgorithm = checksumAlgorithm - _textFactoryService = textFactoryService Me.Encoding = encoding Me.Options = options Me.FilePath = filePath @@ -46,7 +42,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText If _lazyText Is Nothing Then Dim text = SourceTextExtensions.CreateSourceText( - _textFactoryService, GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken) + GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken) Interlocked.CompareExchange(_lazyText, text, Nothing) End If @@ -88,14 +84,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic DirectCast(options, VisualBasicParseOptions), FilePath, Encoding, - _checksumAlgorithm, - _textFactoryService)) + _checksumAlgorithm)) End Function Public Overrides Function WithFilePath(path As String) As SyntaxTree Return If(path = FilePath, Me, - New ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm, _textFactoryService)) + New ParsedSyntaxTree(_lazyText, _root, Options, path, Encoding, _checksumAlgorithm)) End Function Public Overrides Function GetReference(node As SyntaxNode) As SyntaxReference From 40ba1594b85a69bde501296d2ddda2a034d593c5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:52:11 -0700 Subject: [PATCH 392/423] REvert --- .../VisualBasicSyntaxTreeFactoryService.vb | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb index 1b6bd34e9ca90..97b8341d7aae7 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb @@ -11,23 +11,15 @@ Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.VisualBasic + Partial Friend Class VisualBasicSyntaxTreeFactoryService Inherits AbstractSyntaxTreeFactoryService - - Private NotInheritable Class Factory - Implements ILanguageServiceFactory - - Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService - Return New VisualBasicSyntaxTreeFactoryService(languageServices.WorkspaceServices.GetRequiredService(Of ITextFactoryService)()) - End Function - End Class - Private Shared ReadOnly _parseOptionsWithLatestLanguageVersion As VisualBasicParseOptions = VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest) - Private ReadOnly _textFactoryService As ITextFactoryService - Private Sub New(textFactoryService As ITextFactoryService) - _textFactoryService = textFactoryService + + + Public Sub New() End Sub Public Overloads Overrides Function GetDefaultParseOptions() As ParseOptions @@ -83,14 +75,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic options = GetDefaultParseOptions() End If - Return New ParsedSyntaxTree( - lazyText:=Nothing, - DirectCast(root, VisualBasicSyntaxNode), - DirectCast(options, VisualBasicParseOptions), - filePath, - encoding, - checksumAlgorithm, - _textFactoryService) + Return New ParsedSyntaxTree(lazyText:=Nothing, DirectCast(root, VisualBasicSyntaxNode), DirectCast(options, VisualBasicParseOptions), filePath, encoding, checksumAlgorithm) End Function End Class End Namespace From 95023369113f79cded1e0d3be88edd38f853fd6f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:52:51 -0700 Subject: [PATCH 393/423] REvert --- .../CSharpSyntaxTreeFactoryService.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs index 674653fc312d4..f07ba78829056 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs @@ -7,28 +7,29 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Diagnostics; +using System.IO; using System.Text; using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp; -internal sealed partial class CSharpSyntaxTreeFactoryService(ITextFactoryService textFactoryService) : AbstractSyntaxTreeFactoryService +[ExportLanguageService(typeof(ISyntaxTreeFactoryService), LanguageNames.CSharp), Shared] +internal partial class CSharpSyntaxTreeFactoryService : AbstractSyntaxTreeFactoryService { - [ExportLanguageServiceFactory(typeof(ISyntaxTreeFactoryService), LanguageNames.CSharp), Shared] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - private sealed class Factory() : ILanguageServiceFactory - { - public ILanguageService CreateLanguageService(HostLanguageServices languageServices) - => new CSharpSyntaxTreeFactoryService(languageServices.WorkspaceServices.GetRequiredService()); - } - private static readonly CSharpParseOptions _parseOptionWithLatestLanguageVersion = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview); - private readonly ITextFactoryService _textFactoryService = textFactoryService; + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpSyntaxTreeFactoryService() + { + } public override ParseOptions GetDefaultParseOptions() => CSharpParseOptions.Default; @@ -62,8 +63,7 @@ public override bool OptionsDifferOnlyByPreprocessorDirectives(ParseOptions opti public override SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SourceHashAlgorithm checksumAlgorithm, SyntaxNode root) { options ??= GetDefaultParseOptions(); - return new ParsedSyntaxTree( - lazyText: null, (CSharpSyntaxNode)root, (CSharpParseOptions)options, filePath, encoding, checksumAlgorithm, _textFactoryService); + return new ParsedSyntaxTree(lazyText: null, (CSharpSyntaxNode)root, (CSharpParseOptions)options, filePath, encoding, checksumAlgorithm); } public override SyntaxTree ParseSyntaxTree(string filePath, ParseOptions options, SourceText text, CancellationToken cancellationToken) From 6ab93f21a17565969d1b98f5f394669e195cd85d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:56:30 -0700 Subject: [PATCH 394/423] Update src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs --- .../CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs index f910dad739ce9..8a64e2ff71573 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -38,6 +38,7 @@ public ParsedSyntaxTree( _lazyText = lazyText; _root = CloneNodeAsRoot(root); _checksumAlgorithm = checksumAlgorithm; + Encoding = encoding; Options = options; FilePath = filePath; From 75354effd921fd2f572b81006db142247e6bcae7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:56:48 -0700 Subject: [PATCH 395/423] Update src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb --- .../VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb index 2fc2c60bd6ba7..751738b8b856e 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -41,8 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText If _lazyText Is Nothing Then - Dim text = SourceTextExtensions.CreateSourceText( - GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken) + Dim text = SourceTextExtensions.CreateSourceText(GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken) Interlocked.CompareExchange(_lazyText, text, Nothing) End If From 45c2f71fc731e8893112608ab2c8d69ce485b742 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 14:58:35 -0700 Subject: [PATCH 396/423] Docs --- .../Core/Portable/Shared/Extensions/SourceTextExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 659b204bbdecf..0e2abf181652b 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -22,8 +22,8 @@ internal static partial class SourceTextExtensions // char array length: 4k characters. 4K * 256 * 2 (bytes per char) = 4MB private const int CharArrayLength = 4 * 1024; - // 16k characters. Equivalent to 32KB in memory. comes from SourceText char buffer size and less than large object size - public const int SourceTextLengthThreshold = 32 * 1024 / sizeof(char); + // 32k characters. Equivalent to 64KB in memory bytes. Will not be put into the LOH. + public const int SourceTextLengthThreshold = 32 * 1024; /// /// Note: there is a strong invariant that you only get arrays back from this that are exactly Date: Wed, 15 May 2024 15:03:40 -0700 Subject: [PATCH 397/423] Primary constructor --- .../Shared/Extensions/SourceTextExtensions.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 0e2abf181652b..b6939875d9b55 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -254,6 +254,9 @@ public static SourceText CreateSourceText( // Call into the text service to make us a SourceText from the chunks. Disposal of this reader will free all // the intermediary chunks we allocated. + + Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, CharArrayLength)); + using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); var result = SourceText.From(chunkReader, totalLength, encoding, checksumAlgorithm); Contract.ThrowIfTrue(totalLength != chunkReader.Position); @@ -314,24 +317,16 @@ public override void Write(string? value) } } - private sealed class CharArrayChunkTextReader : TextReaderWithLength + private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int length) : TextReaderWithLength(length) { - private readonly ImmutableArray _chunks; - private bool _disposed; + private readonly ImmutableArray _chunks = chunks; + private bool _disposed = false; /// /// Public so that the caller can assert that the new SourceText read all the way to the end of this successfully. /// public int Position; - public CharArrayChunkTextReader(ImmutableArray chunks, int length) - : base(length) - { - _chunks = chunks; - _disposed = false; - Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, CharArrayLength)); - } - public static TextReader CreateFromObjectReader(ObjectReader reader) { var length = reader.ReadInt32(); @@ -369,7 +364,11 @@ public static TextReader CreateFromObjectReader(ObjectReader reader) } Contract.ThrowIfFalse(offset == length); - return new CharArrayChunkTextReader(chunks.MoveToImmutable(), length); + + var chunksArray = chunks.MoveToImmutable(); + Contract.ThrowIfTrue(chunksArray.Any(static (c, s) => c.Length != s, CharArrayLength)); + + return new CharArrayChunkTextReader(chunksArray, length); } protected override void Dispose(bool disposing) From df2d662557718cfa73d00d9a24f2b540705c97af Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 15 May 2024 15:42:24 -0700 Subject: [PATCH 398/423] Working on the build --- eng/targets/Settings.props | 2 ++ eng/targets/TargetFrameworks.props | 2 ++ 2 files changed, 4 insertions(+) diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props index 99698db200629..3598b4bdc7671 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -58,6 +58,8 @@ false true + + <_SkipUpgradeNetAnalyzersNuGetWarning>true diff --git a/eng/targets/TargetFrameworks.props b/eng/targets/TargetFrameworks.props index 20b3ce78a455f..4e0450edc7ce4 100644 --- a/eng/targets/TargetFrameworks.props +++ b/eng/targets/TargetFrameworks.props @@ -36,6 +36,8 @@ --> + + $(NetMinimum) $(NetPrevious) $(NetCurrent);$(NetPrevious) $(NetCurrent);$(NetPrevious) From 3b59475de44e3ea31f66578adfc2ebc8cf1ad435 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 15:55:49 -0700 Subject: [PATCH 399/423] Simplify git push --- .../FindSymbols/FindReferences/DependentProjectsFinder.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 8feec7c6c56fc..03b7590699e6c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -156,9 +156,6 @@ private static async Task> GetDependentProjectsWorkerAsy // Try to add to cache, returning existing value if another thread already added it. using (await s_solutionToDependentProjectMapGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { - if (dictionary.TryGetValue(key, out dependentProjects)) - return dependentProjects; - return dictionary.GetOrAdd(key, dependentProjects); } From 73070333674875dad510ff58e13b16cd0efe9f4e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 16:08:00 -0700 Subject: [PATCH 400/423] Fix null initial name --- .../FindReferences/DependentProjectsFinder.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 03b7590699e6c..dc87229e40fca 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -375,6 +375,15 @@ private static async Task HasReferenceToAssemblyAsync(Project project, str using (await s_metadataIdToAssemblyNameGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { + // Overwrite an existing null name with a non-null one. + if (s_metadataIdToAssemblyName.TryGetValue(metadataId, out var existingName) && + existingName == null && + name != null) + { + s_metadataIdToAssemblyName[metadataId] = name; + } + + // Return whatever is in the map, adding ourselves if something is not already there. name = s_metadataIdToAssemblyName.GetOrAdd(metadataId, name); } From e85832f2953964f0c00adc0c67d92ae707f3b8d7 Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Wed, 15 May 2024 16:21:36 -0700 Subject: [PATCH 401/423] Update attributes for STJ (#73495) --- .../Protocol/TextDocumentChangeRegistrationOptions.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.cs b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.cs index 39de99b3501a0..fa152aa3d8140 100644 --- a/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.cs +++ b/src/Features/LanguageServer/Protocol/Protocol/TextDocumentChangeRegistrationOptions.cs @@ -2,13 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; -using Roslyn.LanguageServer.Protocol; +using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol { @@ -17,14 +11,13 @@ namespace Roslyn.LanguageServer.Protocol /// /// See the Language Server Protocol specification for additional information. /// - [DataContract] internal class TextDocumentChangeRegistrationOptions : TextDocumentRegistrationOptions { /// /// How documents are synced to the server. See /// and . /// - [DataMember(Name = "syncKind")] + [JsonPropertyName("syncKind")] public TextDocumentSyncKind SyncKind { get; From a2eb5139e7b37632f6a1c54e06b6e4fe1f4af11b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 16:26:44 -0700 Subject: [PATCH 402/423] Make into auto props --- .../Core/Portable/Shared/Extensions/SourceTextExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index b6939875d9b55..50e83b304ac99 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -287,7 +287,7 @@ private sealed class CharArrayChunkTextWriter( /// /// Public so that caller can assert that writing out the text actually wrote out the full text of the node. /// - public int Position; + public int Position { get; private set; } public override Encoding Encoding { get; } = encoding; @@ -325,7 +325,7 @@ private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int /// /// Public so that the caller can assert that the new SourceText read all the way to the end of this successfully. /// - public int Position; + public int Position { get; private set; } public static TextReader CreateFromObjectReader(ObjectReader reader) { From 1f23973d16e70d7179135aefca7043ea22f70107 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 15 May 2024 16:28:05 -0700 Subject: [PATCH 403/423] Increase size and doc properly --- .../Core/Portable/Shared/Extensions/SourceTextExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 50e83b304ac99..5b8433ec1ccc8 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -19,7 +19,9 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class SourceTextExtensions { - // char array length: 4k characters. 4K * 256 * 2 (bytes per char) = 4MB + private const int ObjectPoolCount = 1024; + + // char array length: 4k characters. 4K * 1024 (object pool count) * 2 (bytes per char) = 8MB private const int CharArrayLength = 4 * 1024; // 32k characters. Equivalent to 64KB in memory bytes. Will not be put into the LOH. @@ -30,7 +32,7 @@ internal static partial class SourceTextExtensions /// cref="CharArrayLength"/> long. Putting arrays back into this of the wrong length will result in broken /// behavior. Do not expose this pool outside of this class. /// - private static readonly ObjectPool s_charArrayPool = new(() => new char[CharArrayLength], 256); + private static readonly ObjectPool s_charArrayPool = new(() => new char[CharArrayLength], ObjectPoolCount); public static void GetLineAndOffset(this SourceText text, int position, out int lineNumber, out int offset) { From a3481f4e7daf8e3826dad747e10207d2dadbb568 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:12:20 -0700 Subject: [PATCH 404/423] Switch to a sorted map to ensure that we produce the same value regardless of how it was constructed --- .../Portable/Workspace/Solution/ProjectId.cs | 15 ++++- .../SourceGeneratorExecutionVersion.cs | 11 ++-- .../Workspace/Workspace_SourceGeneration.cs | 3 +- ...SourceGeneratorExecutionVersionMapTests.cs | 63 +++++++++++++++++++ 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs index fa08ca56e7319..95937d1d88cde 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis; #pragma warning restore CA1200 // Avoid using cref tags with a prefix [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] [DataContract] -public sealed class ProjectId : IEquatable +public sealed class ProjectId : IEquatable, IComparer { /// /// Checksum of this ProjectId, built only from . @@ -37,8 +37,8 @@ public sealed class ProjectId : IEquatable /// /// An optional name to show only for debugger-display purposes. This must not be used for any other - /// purpose. Importantly, it must not be part of the equality/hashing contract of this type (including ). + /// purpose. Importantly, it must not be part of the equality/hashing/comparable contract of this type (including + /// ). /// [DataMember(Order = 1)] private readonly string? _debugName; @@ -114,4 +114,13 @@ internal Checksum Checksum writer.WriteString(nameof(ProjectId)); writer.WriteGuid(@this.Id); }), this); + + int IComparer.Compare(ProjectId? x, ProjectId? y) + => (x, y) switch + { + (null, null) => 0, + (null, _) => -1, + (_, null) => 1, + _ => x.Id.CompareTo(y.Id), + }; } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs index 765e3cae91379..8b87aa885a8c3 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorExecutionVersion.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Collections; @@ -48,14 +49,14 @@ public static SourceGeneratorExecutionVersion ReadFrom(ObjectReader reader) /// Helper construct to allow a mapping from s to . /// Limited to just the surface area the workspace needs. /// -internal sealed class SourceGeneratorExecutionVersionMap(ImmutableSegmentedDictionary map) +internal sealed class SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary map) { public static readonly SourceGeneratorExecutionVersionMap Empty = new(); - public ImmutableSegmentedDictionary Map { get; } = map; + public ImmutableSortedDictionary Map { get; } = map; public SourceGeneratorExecutionVersionMap() - : this(ImmutableSegmentedDictionary.Empty) + : this(ImmutableSortedDictionary.Empty) { } @@ -75,6 +76,8 @@ public override bool Equals([NotNullWhen(true)] object? obj) public void WriteTo(ObjectWriter writer) { + // Writing out the dictionary in order is fine. That's because it's a sorted dictionary, and ProjectIds are + // naturally comparable. writer.WriteInt32(Map.Count); foreach (var (projectId, version) in Map) { @@ -86,7 +89,7 @@ public void WriteTo(ObjectWriter writer) public static SourceGeneratorExecutionVersionMap Deserialize(ObjectReader reader) { var count = reader.ReadInt32(); - var builder = ImmutableSegmentedDictionary.CreateBuilder(); + var builder = ImmutableSortedDictionary.CreateBuilder(); for (var i = 0; i < count; i++) { var projectId = ProjectId.ReadFrom(reader); diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs index 2d7ea17c29427..a530c1eb25491 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Frozen; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -69,7 +70,7 @@ static SourceGeneratorExecutionVersionMap GetUpdatedSourceGeneratorVersions( // projects that transitively depend on that project, so that their generators will run as well when next // asked. var dependencyGraph = solution.GetProjectDependencyGraph(); - var result = ImmutableSegmentedDictionary.CreateBuilder(); + var result = ImmutableSortedDictionary.CreateBuilder(); // Determine if we want a major solution change, forcing regeneration of all projects. var solutionMajor = projectIds.Any(t => t.projectId is null && t.forceRegeneration); diff --git a/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs b/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs new file mode 100644 index 0000000000000..4c5a9ce25fd00 --- /dev/null +++ b/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public sealed class SourceGeneratorExecutionVersionMapTests +{ + [Fact] + public void TestOrderingDoesNotMatter() + { + var projectId1 = ProjectId.CreateNewId(); + var projectId2 = ProjectId.CreateNewId(); + + var project1Kvp = new KeyValuePair(projectId1, new(MajorVersion: 1, MinorVersion: 1)); + var project2Kvp = new KeyValuePair(projectId2, new(MajorVersion: 2, MinorVersion: 2)); + + var map1 = new SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary.CreateRange([project1Kvp, project2Kvp])); + var map2 = new SourceGeneratorExecutionVersionMap(ImmutableSortedDictionary.CreateRange([project2Kvp, project1Kvp])); + Assert.True(map1.Map.SequenceEqual(map2.Map)); + + using var memoryStream1 = SerializableBytes.CreateWritableStream(); + using var memoryStream2 = SerializableBytes.CreateWritableStream(); + { + using var writer1 = new ObjectWriter(memoryStream1, leaveOpen: true); + { + map1.WriteTo(writer1); + } + + using var writer2 = new ObjectWriter(memoryStream2, leaveOpen: true); + { + map2.WriteTo(writer2); + } + + memoryStream1.Position = 0; + memoryStream2.Position = 0; + + var array1 = memoryStream1.ToArray(); + var array2 = memoryStream2.ToArray(); + + Assert.Equal(array1.Length, array2.Length); + Assert.True(array1.Length > 0); + + Assert.True(array1.AsSpan().SequenceEqual(array2)); + + memoryStream1.Position = 0; + memoryStream2.Position = 0; + + var rehydrated1 = SourceGeneratorExecutionVersionMap.Deserialize(ObjectReader.GetReader(memoryStream1, leaveOpen: true)); + var rehydrated2 = SourceGeneratorExecutionVersionMap.Deserialize(ObjectReader.GetReader(memoryStream2, leaveOpen: true)); + + Assert.True(rehydrated1.Map.SequenceEqual(rehydrated2.Map)); + Assert.True(rehydrated1.Map.SequenceEqual(map1.Map)); + } + } +} From ee81fbffea1f19cae9001dede2beb25c87a84c3c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:13:27 -0700 Subject: [PATCH 405/423] IComparable --- .../Portable/Workspace/Solution/ProjectId.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs index 95937d1d88cde..4b1f14104ae9c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectId.cs @@ -22,7 +22,7 @@ namespace Microsoft.CodeAnalysis; #pragma warning restore CA1200 // Avoid using cref tags with a prefix [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] [DataContract] -public sealed class ProjectId : IEquatable, IComparer +public sealed class ProjectId : IEquatable, IComparable { /// /// Checksum of this ProjectId, built only from . @@ -115,12 +115,11 @@ internal Checksum Checksum writer.WriteGuid(@this.Id); }), this); - int IComparer.Compare(ProjectId? x, ProjectId? y) - => (x, y) switch - { - (null, null) => 0, - (null, _) => -1, - (_, null) => 1, - _ => x.Id.CompareTo(y.Id), - }; + int IComparable.CompareTo(ProjectId? other) + { + if (other is null) + return 1; + + return this.Id.CompareTo(other.Id); + } } From 501ad5217b8ccb4384695bd7bf2634e847b2d5c5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:14:33 -0700 Subject: [PATCH 406/423] Add test --- .../SolutionTests/SourceGeneratorExecutionVersionMapTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs b/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs index 4c5a9ce25fd00..00f06cf10e63f 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SourceGeneratorExecutionVersionMapTests.cs @@ -18,6 +18,7 @@ public void TestOrderingDoesNotMatter() { var projectId1 = ProjectId.CreateNewId(); var projectId2 = ProjectId.CreateNewId(); + Assert.NotEqual(projectId1, projectId2); var project1Kvp = new KeyValuePair(projectId1, new(MajorVersion: 1, MinorVersion: 1)); var project2Kvp = new KeyValuePair(projectId2, new(MajorVersion: 2, MinorVersion: 2)); From 9f693e9be28cedfa8402754c553f2f3caa505d49 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:15:35 -0700 Subject: [PATCH 407/423] Cleanup --- .../Core/Portable/Workspace/Workspace_SourceGeneration.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs index a530c1eb25491..9070ead22dcdb 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_SourceGeneration.cs @@ -2,14 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Frozen; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; From 9023312dda369c4fdbae4d46820f05fb9d581126 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:33:02 -0700 Subject: [PATCH 408/423] Make the impl of SyntaxNode.GetText more efficient --- src/Compilers/Core/Portable/Syntax/SyntaxNode.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index ba5ab7ed1b3dc..1b6c2dab513f7 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -325,9 +325,10 @@ public virtual void WriteTo(TextWriter writer) /// is not supported. public SourceText GetText(Encoding? encoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) { - var builder = new StringBuilder(this.Green.FullWidth); - this.WriteTo(new StringWriter(builder)); - return new StringBuilderText(builder, encoding, checksumAlgorithm); + var fullWidth = this.Green.FullWidth; + var writer = SourceTextWriter.Create(encoding, checksumAlgorithm, fullWidth); + this.WriteTo(writer); + return writer.ToSourceText(); } /// From fd68dc15dec2d27f44b9fc1a6d414f0611c2e5a8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:34:17 -0700 Subject: [PATCH 409/423] Remove IDE side --- .../Shared/Extensions/SourceTextExtensions.cs | 86 +------------------ 1 file changed, 3 insertions(+), 83 deletions(-) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 5b8433ec1ccc8..1eef6e5b28e97 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -4,9 +4,7 @@ using System; using System.Collections.Immutable; -using System.Diagnostics; using System.IO; -using System.Linq; using System.Text; using System.Threading; using Microsoft.CodeAnalysis.Host; @@ -238,87 +236,6 @@ public static SourceText ReadFrom(ITextFactoryService textService, ObjectReader return textService.CreateText(textReader, encoding, checksumAlgorithm, cancellationToken); } - public static SourceText CreateSourceText( - SyntaxNode node, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) - { - // If this node is small enough to not go into the LOH, we can just fast path directly to creating a SourceText from it. - var totalLength = node.FullWidth(); - if (totalLength <= SourceTextLengthThreshold) - return SourceText.From(node.ToFullString(), encoding, checksumAlgorithm); - - // Allocate the space to write the node into. Explicitly chunked so that nothing goes into the LOH. - var chunks = CreateChunks(totalLength); - - // Write the node into that temp space. - using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); - node.WriteTo(chunkWriter); - Contract.ThrowIfTrue(totalLength != chunkWriter.Position); - - // Call into the text service to make us a SourceText from the chunks. Disposal of this reader will free all - // the intermediary chunks we allocated. - - Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, CharArrayLength)); - - using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); - var result = SourceText.From(chunkReader, totalLength, encoding, checksumAlgorithm); - Contract.ThrowIfTrue(totalLength != chunkReader.Position); - - return result; - - static ImmutableArray CreateChunks(int totalLength) - { - var numberOfChunks = 1 + (totalLength / CharArrayLength); - var buffer = new FixedSizeArrayBuilder(numberOfChunks); - for (var i = 0; i < numberOfChunks; i++) - buffer.Add(s_charArrayPool.Allocate()); - - return buffer.MoveToImmutable(); - } - } - - private static int GetIndexFromPosition(int position) => position / CharArrayLength; - private static int GetColumnFromPosition(int position) => position % CharArrayLength; - - private sealed class CharArrayChunkTextWriter( - int totalLength, ImmutableArray chunks, Encoding encoding, CancellationToken cancellationToken) : TextWriter - { - private readonly int _totalLength = totalLength; - private readonly ImmutableArray _chunks = chunks; - private readonly CancellationToken _cancellationToken = cancellationToken; - - /// - /// Public so that caller can assert that writing out the text actually wrote out the full text of the node. - /// - public int Position { get; private set; } - - public override Encoding Encoding { get; } = encoding; - - public override void Write(string? value) - { - Contract.ThrowIfNull(value); - - var valueSpan = value.AsSpan(); - while (valueSpan.Length > 0) - { - _cancellationToken.ThrowIfCancellationRequested(); - - var chunk = _chunks[GetIndexFromPosition(Position)]; - Contract.ThrowIfTrue(chunk.Length != CharArrayLength); - - var chunkIndex = GetColumnFromPosition(Position); - Contract.ThrowIfTrue(chunkIndex >= CharArrayLength); - - var count = Math.Min(valueSpan.Length, CharArrayLength - chunkIndex); - valueSpan[..count].CopyTo(chunk.AsSpan().Slice(chunkIndex, count)); - - Position += count; - valueSpan = valueSpan[count..]; - } - - Contract.ThrowIfTrue(Position > _totalLength); - } - } - private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int length) : TextReaderWithLength(length) { private readonly ImmutableArray _chunks = chunks; @@ -329,6 +246,9 @@ private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int /// public int Position { get; private set; } + private static int GetIndexFromPosition(int position) => position / CharArrayLength; + private static int GetColumnFromPosition(int position) => position % CharArrayLength; + public static TextReader CreateFromObjectReader(ObjectReader reader) { var length = reader.ReadInt32(); From 178600c4995109c4a544348522e447605ecb708a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:38:32 -0700 Subject: [PATCH 410/423] Remove IDE side --- .../CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs | 3 +-- .../VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs index 8a64e2ff71573..814853036893e 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -48,8 +48,7 @@ public override SourceText GetText(CancellationToken cancellationToken) { if (_lazyText == null) { - var sourceText = SourceTextExtensions.CreateSourceText(GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken); - Interlocked.CompareExchange(ref _lazyText, sourceText, null); + Interlocked.CompareExchange(ref _lazyText, GetRoot(cancellationToken).GetText(Encoding, _checksumAlgorithm), null); } return _lazyText; diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb index 751738b8b856e..99b66db0d1bca 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -41,8 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText If _lazyText Is Nothing Then - Dim text = SourceTextExtensions.CreateSourceText(GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken) - Interlocked.CompareExchange(_lazyText, text, Nothing) + Interlocked.CompareExchange(_lazyText, GetRoot(cancellationToken).GetText(Encoding, _checksumAlgorithm), Nothing) End If Return _lazyText From e708e41bc8f34cf4692035cc2d45e65d81875eb6 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:51:41 -0700 Subject: [PATCH 411/423] Reduce lines of code by 25% --- src/Compilers/Core/Portable/Syntax/SyntaxNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index 1b6c2dab513f7..dc8bddd5e1dc0 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -325,8 +325,7 @@ public virtual void WriteTo(TextWriter writer) /// is not supported. public SourceText GetText(Encoding? encoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) { - var fullWidth = this.Green.FullWidth; - var writer = SourceTextWriter.Create(encoding, checksumAlgorithm, fullWidth); + var writer = SourceTextWriter.Create(encoding, checksumAlgorithm, this.Green.FullWidth); this.WriteTo(writer); return writer.ToSourceText(); } From ddca33ca6ffda60cc1d83effa609a0d053c55952 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:55:23 -0700 Subject: [PATCH 412/423] Switch to cleaner task yielding pattern --- .../Core/GoToDefinition/AbstractGoToCommandHandler`2.cs | 2 +- .../Utilities/BrokeredServiceProxy.cs | 9 +++++---- .../Core/Def/NavigateTo/RoslynSearchItemsSource.cs | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs index 8253bca56b7e1..a93e35b1372d2 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToCommandHandler`2.cs @@ -251,7 +251,7 @@ private async Task FindResultsAsync( IFindUsagesContext findContext, Document document, int position, CancellationToken cancellationToken) { // Ensure that we relinquish the thread so that the caller can proceed with their work. - await Task.Yield().ConfigureAwait(false); + await TaskScheduler.Default.SwitchTo(alwaysYield: true); using (Logger.LogBlock(FunctionId, KeyValueLogMessage.Create(LogType.UserAction), cancellationToken)) { diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs index de5f1376515eb..ae532d7b3ba06 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Utilities/BrokeredServiceProxy.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.VisualStudio.Threading; using Nerdbank.Streams; using Roslyn.Utilities; using StreamJsonRpc; @@ -31,8 +32,8 @@ public BrokeredServiceProxy(T service) async Task CreateServerAsync() { - // Ensure caller can proceed. - await Task.Yield().ConfigureAwait(false); + // Always yield to ensure caller can proceed. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); var serverMultiplexingStream = await MultiplexingStream.CreateAsync(serverStream); var serverChannel = await serverMultiplexingStream.AcceptChannelAsync(""); @@ -46,8 +47,8 @@ async Task CreateServerAsync() async Task CreateClientAsync() { - // Ensure caller can proceed. - await Task.Yield().ConfigureAwait(false); + // Always yield to ensure caller can proceed. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); var clientMultiplexingStream = await MultiplexingStream.CreateAsync(clientStream); var clientChannel = await clientMultiplexingStream.OfferChannelAsync(""); diff --git a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs index 3d32fc0b23cf3..6051c5d55f959 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchItemsSource.cs @@ -66,8 +66,8 @@ private async Task PerformSearchWorkerAsync( ISearchCallback searchCallback, CancellationToken cancellationToken) { - // Ensure we yield immedaitely so our caller can proceed with other work. - await Task.Yield().ConfigureAwait(false); + // Ensure we yield immediately so our caller can proceed with other work. + await TaskScheduler.Default.SwitchTo(alwaysYield: true); var searchValue = searchQuery.QueryString.Trim(); if (string.IsNullOrWhiteSpace(searchValue)) From 4a8894289db4a74284dca269039c21be596ca86e Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 13 May 2024 17:29:35 -0700 Subject: [PATCH 413/423] Add required references to the devkit component output directory --- ...deAnalysis.LanguageServer.UnitTests.csproj | 2 +- ...isualStudio.LanguageServices.DevKit.csproj | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj index d1514de4c67c8..1ee7dd6eccb05 100644 --- a/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj +++ b/src/Features/LanguageServer/Microsoft.CodeAnalysis.LanguageServer.UnitTests/Microsoft.CodeAnalysis.LanguageServer.UnitTests.csproj @@ -23,7 +23,7 @@ --> + Targets="GetPackInputs"> diff --git a/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj b/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj index 339b5d2a742e6..396ab0ff2e66d 100644 --- a/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj +++ b/src/VisualStudio/DevKit/Impl/Microsoft.VisualStudio.LanguageServices.DevKit.csproj @@ -9,8 +9,7 @@ .NET Compiler Platform ("Roslyn") Language Server Protocol internal. true - CollectPackInputs;$(BeforePack) - + $(NoWarn);NU5100 @@ -45,18 +44,20 @@ - - - + + + + + + + + + + + + - - - - - - + <_Content Include="@(Content)" Condition="'%(Content.Pack)'=='true'"/> From c26ac42829f34e27c6263cf019ced3c41dad3a25 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 16 May 2024 10:58:50 -0700 Subject: [PATCH 414/423] Two more places --- .../Protocol/Features/CodeFixes/CodeFixService.cs | 2 +- .../Services/DiagnosticAnalyzer/DiagnosticComputer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs index 3ea1ac7bbe425..61b85eb647bab 100644 --- a/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs +++ b/src/Features/LanguageServer/Protocol/Features/CodeFixes/CodeFixService.cs @@ -157,7 +157,7 @@ public async Task GetMostSevereFixAsync( CancellationToken cancellationToken) { // Ensure we yield here so the caller can continue on. - await AwaitExtensions.ConfigureAwait(Task.Yield(), false); + await TaskScheduler.Default.SwitchTo(alwaysYield: true); await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: false, diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index d73d1bc31c709..32f957d276dc4 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -307,7 +307,7 @@ static async Task WaitForHighPriorityTasksAsync(CancellationToken cancellationTo if (task.IsCompleted) { // Make sure to yield so continuations of 'task' can make progress. - await AwaitExtensions.ConfigureAwait(Task.Yield(), false); + await TaskScheduler.Default.SwitchTo(alwaysYield: true); } else { From 5d092536ca9363fa0f2f688bf9eeab1eee08f7bb Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 16 May 2024 12:57:10 +0000 Subject: [PATCH 415/423] Update dependencies from https://github.com/dotnet/arcade build 20240515.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24225.1 -> To Version 9.0.0-beta.24265.1 From da3e165fd7af4a0d3b1fedeed76792f606bc5530 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 16 May 2024 12:57:10 +0000 Subject: [PATCH 416/423] Update dependencies from https://github.com/dotnet/arcade build 20240515.1 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 8.0.0-beta.24225.1 -> To Version 9.0.0-beta.24265.1 From 95b33026473a7cfd5aeacabd4d2b86182f190315 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 15 May 2024 18:12:13 -0700 Subject: [PATCH 417/423] Cleaning up the .NET 9 SDK issues This change adjusts our repo to working with the .NET 9 SDK. --- .../contributing/Target Framework Strategy.md | 10 +- eng/SourceBuildPrebuiltBaseline.xml | 6 +- eng/targets/TargetFrameworks.props | 3 + .../EditAndContinue/EditAndContinueTests.cs | 220 +++++++++++++----- .../LocalStateTracingTests.cs | 2 +- ...CodeAnalysis.CSharp.Emit2.UnitTests.csproj | 2 +- .../Microsoft.CodeAnalysis.UnitTests.csproj | 2 +- .../Test/Core/Compilation/RuntimeUtilities.cs | 9 +- ...eAnalysis.CSharp.Features.UnitTests.csproj | 2 +- .../CSharpSemanticSearchServiceTests.cs | 2 +- .../CSharpTest/CommandLineRunnerTests.cs | 2 +- ...Analysis.CSharp.Scripting.UnitTests.csproj | 2 +- .../CSharpTest/ObjectFormatterTests.cs | 6 +- src/Tools/BuildBoss/ProjectCheckerUtil.cs | 1 + 14 files changed, 197 insertions(+), 72 deletions(-) diff --git a/docs/contributing/Target Framework Strategy.md b/docs/contributing/Target Framework Strategy.md index a6545072f1920..a0228703facd4 100644 --- a/docs/contributing/Target Framework Strategy.md +++ b/docs/contributing/Target Framework Strategy.md @@ -26,6 +26,7 @@ Projects in our repository should include the following values in `` setting. Instead our repo uses the above values and when inside source build or VMR our properties are initialized with arcade properties. @@ -58,7 +59,7 @@ This problem primarily comes from our use of polyfill APIs. To avoid this we emp This comes up in two forms: -### Pattern for types +### Pattern for types When creating a polyfill for a type use the `#if !NET...` to declare the type and in the `#else` use a `TypeForwardedTo` for the actual type. @@ -99,6 +100,13 @@ When creating a polyfill for an extension use the `#if NET...` to declare the ex #endif ``` +## Transitioning to new .NET SDK +As the .NET team approaches releasing a new .NET SDK the Roslyn team will begin using preview versions of that SDK in our build. This will often lead to test failures in our CI system due to underlying behavior changes in the runtime. These failures will often not show up when running in Visual Studio due to the way the runtime for the test runner is chosen. +To ensure we have a simple developer environment such project should be moved to to the `$(NetRoslynNext)` target framework. That ensures the new runtime is loaded when running tests locally. + +When the .NET SDK RTMs and Roslyn adopts it all occurrences of `$(NetRoslynNext)` will be moved to simply `$(NetRoslyn)`. + +**DO NOT** include both `$(NetRoslyn)` and `$(NetRoslynNext)` in the same project unless there is a very specific reason that both tests are adding value. The most common case is that the runtime has changed behavior and we simply need to update our baselines to match it. Adding extra TFMs for this just increases test time for very little gain. diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 592d3954059e8..8451ebe80c273 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -13,13 +13,15 @@ - + - + diff --git a/eng/targets/TargetFrameworks.props b/eng/targets/TargetFrameworks.props index 4e0450edc7ce4..8493c1046cb51 100644 --- a/eng/targets/TargetFrameworks.props +++ b/eng/targets/TargetFrameworks.props @@ -18,6 +18,7 @@ net8.0 net7.0;net8.0 net6.0 + net9.0