From 0874a505fa20ceb0b904f1b90be1908b72e8e8ad Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Fri, 14 May 2021 09:11:19 +0200 Subject: [PATCH 001/187] Rename 'Basic' to 'Visual Basic' in Visual Studio options page --- .../VisualBasic/Impl/LanguageService/VisualBasicPackage.vb | 2 ++ src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb index 740139e98436f..f2106d45d9726 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb @@ -30,6 +30,8 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic ' Advanced ' Code Style (category) ' General + ' Naming + ' IntelliSense diff --git a/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef b/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef index ef4f168cef62d..83f3b2e4c621e 100644 --- a/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef +++ b/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef @@ -65,6 +65,8 @@ // Code Style (category) // General // Naming +// IntelliSense + [$RootKey$\Languages\Language Services\Basic\EditorToolsOptions\IntelliSense] @="#112" @@ -167,7 +169,7 @@ [$RootKey$\Languages\Language Services\Basic] @="{e34acdc0-baae-11d0-88bf-00a0c9110049}" "Package"="{574fc912-f74f-4b4e-92c3-f695c208a2bb}" -"LangResID"=dword:00000000 +"LangResID"=dword:00000065 "DefaultToInsertSpaces"=dword:00000001 "EnableAdvancedMembersOption"=dword:00000001 From e2a77740fb1db14480b57d1db42b3bfb35e7f825 Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Sat, 15 May 2021 20:17:28 +0200 Subject: [PATCH 002/187] Update comments per feedback --- .../VisualBasic/Impl/LanguageService/VisualBasicPackage.vb | 2 +- src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb index f2106d45d9726..7a4b6f8167fdd 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb @@ -23,7 +23,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic ' The option page configuration is duplicated in PackageRegistration.pkgdef. ' ' VB option pages tree - ' Basic + ' Visual Basic ' General (from editor) ' Scroll Bars (from editor) ' Tabs (from editor) diff --git a/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef b/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef index 83f3b2e4c621e..bfb3a04c6856c 100644 --- a/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef +++ b/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef @@ -57,7 +57,7 @@ // The option page configuration is duplicated on VisualBasicPackage type definition. // // VB option pages tree -// Basic +// Visual Basic // General (from editor) // Scroll Bars (from editor) // Tabs (from editor) From 5fd73c502789746e1c20b12a5696f38748da7629 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Wed, 15 Dec 2021 20:54:06 -0800 Subject: [PATCH 003/187] Report error if 'record struct' constructor calls default parameterless constructor (#58339) --- .../Portable/Binder/Binder_Statements.cs | 11 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Semantic/Semantics/RecordStructTests.cs | 344 ++++++++++++++++++ 17 files changed, 423 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index bf9d9b659e10d..31869e224e126 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3420,7 +3420,7 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, bool thisInitializer = initializer?.IsKind(SyntaxKind.ThisConstructorInitializer) == true; if (!thisInitializer && - ContainingType.GetMembersUnordered().OfType().Any()) + hasAnyRecordConstructors()) { var constructorSymbol = (MethodSymbol)this.ContainingMember(); if (!constructorSymbol.IsStatic && @@ -3437,6 +3437,12 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, && thisInitializer && ContainingType.IsDefaultValueTypeConstructor(initializer); + if (skipInitializer && + hasAnyRecordConstructors()) + { + Error(diagnostics, ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, initializer.ThisOrBaseKeyword); + } + // Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel return new BoundConstructorMethodBody(constructor, bodyBinder.GetDeclaredLocalsForScope(constructor), @@ -3447,6 +3453,9 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, null : bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody, constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded)); + + bool hasAnyRecordConstructors() => + ContainingType.GetMembersUnordered().OfType().Any(); } internal virtual BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax initializer, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index d3cdb466a752d..c257fea3b7bd1 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6866,6 +6866,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A lambda expression with attributes cannot be converted to an expression tree + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + The operation may overflow '{0}' at runtime (use 'unchecked' syntax to override) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 21d55131f01bf..964b50453d0ca 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1999,6 +1999,7 @@ internal enum ErrorCode WRN_CompileTimeCheckedOverflow = 8973, WRN_MethGrpToNonDel = 8974, + ERR_RecordStructConstructorCallsDefaultConstructor = 8982, #endregion diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 0fc0af7751f66..d334071e2d152 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1022,6 +1022,11 @@ Primární konstruktor je v konfliktu se syntetizovaně zkopírovaným konstruktorem. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Přiřazení odkazu {1} k {0} nelze provést, protože {1} má užší řídicí obor než {0}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index fab99a6112ed4..358f243c0c509 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1022,6 +1022,11 @@ Der primäre Konstruktor verursacht einen Konflikt mit dem synthetisierten Kopierkonstruktor. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. ref-assign von "{1}" zu "{0}" ist nicht möglich, weil "{1}" einen geringeren Escapebereich als "{0}" aufweist. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index e61b82ee62bd9..c029628899a86 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1022,6 +1022,11 @@ El constructor principal está en conflicto con el constructor de copia sintetizado. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. No se puede asignar referencia "{1}" a "{0}" porque "{1}" tiene un ámbito de escape más limitado que "{0}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index d3e10858683d6..54f9dfccbeb5c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1022,6 +1022,11 @@ Le constructeur principal est en conflit avec le constructeur de copie synthétisée. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Impossible d'effectuer une assignation par référence de '{1}' vers '{0}', car '{1}' a une portée de sortie plus limitée que '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ae09f873ca7a5..9884be5d98434 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1022,6 +1022,11 @@ Il costruttore primario è in conflitto con il costruttore di copia sintetizzato. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Non è possibile assegnare '{1}' a '{0}' come ref perché l'ambito di escape di '{1}' è ridotto rispetto a quello di '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 78f8ef3c14a9d..832e8afce59f6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1022,6 +1022,11 @@ プライマリ コンストラクターが、合成されたコピー コンストラクターと競合しています。 + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}' を '{0}' に ref 割り当てすることはできません。'{1}' のエスケープ スコープが '{0}' より狭いためです。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index f44cf74c02950..0980e056007d9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1022,6 +1022,11 @@ 기본 생성자가 합성된 복사 생성자와 충돌합니다. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}'을(를) '{0}'에 참조 할당할 수 없습니다. '{1}'이(가) '{0}'보다 이스케이프 범위가 좁기 때문입니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 6d5b28c290a26..e790851286280 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1022,6 +1022,11 @@ Konstruktor podstawowy powoduje konflikt z konstruktorem syntetyzowanej kopii. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Nie można przypisać odwołania elementu „{1}” do elementu „{0}”, ponieważ element „{1}” ma węższy zakres wyjścia niż element „{0}”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 34597e7def371..781f858282a8c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1022,6 +1022,11 @@ O construtor primário entra em conflito com o construtor de cópia sintetizado. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Não é possível atribuir ref '{1}' a '{0}' porque '{1}' tem um escopo de escape mais limitado que '{0}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index ab7c1967c0b75..90dbd691b6a59 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1022,6 +1022,11 @@ Первичный конструктор конфликтует с синтезированным конструктором копий. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. Не удается присвоить по ссылке "{1}" для "{0}", так как escape-область у "{1}" уже, чем у "{0}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 5160e9e0f8812..c3bdceded066e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1022,6 +1022,11 @@ Birincil oluşturucu, sentezlenmiş kopya oluşturucusuyla çakışıyor. + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. '{1}', '{0}' öğesinden daha dar bir kaçış kapsamı içerdiğinden '{0}' öğesine '{1}' ref ataması yapılamıyor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 8dd99d7d68f16..13572a101628a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1022,6 +1022,11 @@ 主构造函数与合成的复制构造函数冲突。 + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. 无法将“{1}”重新赋值为“{0}”,因为“{1}”具有比“{0}”更窄的转义范围。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 15fb46f8f8213..06d23b0f04b18 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1022,6 +1022,11 @@ 主要建構函式與合成的複製建構函式相衝突。 + + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + + Cannot ref-assign '{1}' to '{0}' because '{1}' has a narrower escape scope than '{0}'. 不能將 '{1}' 參考指派至 '{0}',因為 '{1}' 的逸出範圍比 '{0}' 還要窄。 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index e5ae5daaf864a..fa11e107a789a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -10616,5 +10616,349 @@ public Value(Value original) : this(42) { } var comp = CreateCompilation(src); CompileAndVerify(comp, expectedOutput: "Value { I = 42 }"); } + + [Fact] + public void ExplicitConstructors_01() + { + var source = +@"using static System.Console; +record struct S1 +{ +} +record struct S2 +{ + public S2() { } +} +record struct S3 +{ + public S3(object o) { } +} +class Program +{ + static void Main() + { + WriteLine(new S1()); + WriteLine(new S2()); + WriteLine(new S3()); + WriteLine(new S3(null)); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: +@"S1 { } +S2 { } +S3 { } +S3 { } +"); + verifier.VerifyMissing("S1..ctor()"); + verifier.VerifyIL("S2..ctor()", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +}"); + verifier.VerifyMissing("S3..ctor()"); + verifier.VerifyIL("S3..ctor(object)", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +}"); + } + + [Fact] + public void ExplicitConstructors_02() + { + var source = +@"record struct S1 +{ + public S1(object o) { } +} +record struct S2() +{ + public S2(object o) { } +} +record struct S3(char A) +{ + public S3(object o) { } +} +record struct S4(char A, char B) +{ + public S4(object o) { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S2(object o) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S2").WithLocation(7, 12), + // (11,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S3(object o) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S3").WithLocation(11, 12), + // (15,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S4(object o) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S4").WithLocation(15, 12)); + } + + [Fact] + public void ExplicitConstructors_03() + { + var source = +@"using static System.Console; +record struct S1 +{ + public S1(object o) : this() { } +} +record struct S2() +{ + public S2(object o) : this() { } +} +class Program +{ + static void Main() + { + WriteLine(new S1()); + WriteLine(new S2()); + } +}"; + CompileAndVerify(source, expectedOutput: +@"S1 { } +S2 { } +"); + } + + [Fact] + public void ExplicitConstructors_04() + { + var source = +@"using static System.Console; +record struct S0 +{ + internal object F = 0; + public S0() { } +} +record struct S1 +{ + internal object F = 1; + public S1(object o) : this() { F = o; } +} +record struct S2() +{ + internal object F = 2; + public S2(object o) : this() { F = o; } +} +class Program +{ + static void Main() + { + WriteLine(new S0().F); + WriteLine(new S1().F); + WriteLine(new S1(-1).F); + WriteLine(new S2().F); + WriteLine(new S2(-2).F); + } +}"; + CompileAndVerify(source, expectedOutput: +@"0 + +-1 +2 +-2 +"); + } + + [Fact] + [WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")] + public void ExplicitConstructors_05() + { + var source = +@"record struct S3(char A) +{ + public S3(object o) : this() { } +} +record struct S4(char A, char B) +{ + public S4(object o) : this() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S3(object o) : this() { } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(3, 27), + // (7,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S4(object o) : this() { } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(7, 27)); + } + + [Fact] + [WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")] + public void ExplicitConstructors_06() + { + var source = +@"record struct S3(char A) +{ + internal object F = 3; + public S3(object o) : this() { F = o; } +} +record struct S4(char A, char B) +{ + internal object F = 4; + public S4(object o) : this() { F = o; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S3(object o) : this() { F = o; } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(4, 27), + // (9,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // public S4(object o) : this() { F = o; } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(9, 27)); + } + + [Fact] + public void ExplicitConstructors_07() + { + var source = +@"using static System.Console; +record struct S1 +{ + public S1(object o) : this() { } + public S1() { } +} +record struct S3(char A) +{ + public S3(object o) : this() { } + public S3() : this('a') { } +} +record struct S4(char A, char B) +{ + public S4(object o) : this() { } + public S4() : this('a', 'b') { } +} +class Program +{ + static void Main() + { + WriteLine(new S1()); + WriteLine(new S1(1)); + WriteLine(new S3()); + WriteLine(new S3(3)); + WriteLine(new S4()); + WriteLine(new S4(4)); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: +@"S1 { } +S1 { } +S3 { A = a } +S3 { A = a } +S4 { A = a, B = b } +S4 { A = a, B = b } +"); + verifier.VerifyIL("S1..ctor()", +@"{ + // Code size 1 (0x1) + .maxstack 0 + IL_0000: ret +}"); + verifier.VerifyIL("S1..ctor(object)", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""S1..ctor()"" + IL_0006: ret +}"); + verifier.VerifyIL("S3..ctor()", +@"{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s 97 + IL_0003: call ""S3..ctor(char)"" + IL_0008: ret +}"); + verifier.VerifyIL("S3..ctor(object)", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""S3..ctor()"" + IL_0006: ret +}"); + verifier.VerifyIL("S4..ctor()", +@"{ + // Code size 11 (0xb) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s 97 + IL_0003: ldc.i4.s 98 + IL_0005: call ""S4..ctor(char, char)"" + IL_000a: ret +}"); + verifier.VerifyIL("S4..ctor(object)", +@"{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""S4..ctor()"" + IL_0006: ret +}"); + } + + [Fact] + public void ExplicitConstructors_08() + { + var source = +@"record struct S2() +{ + public S2(object o) : this() { } + public S2() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,27): error CS2121: The call is ambiguous between the following methods or properties: 'S2.S2()' and 'S2.S2()' + // public S2(object o) : this() { } + Diagnostic(ErrorCode.ERR_AmbigCall, "this").WithArguments("S2.S2()", "S2.S2()").WithLocation(3, 27), + // (4,12): error CS2111: Type 'S2' already defines a member called 'S2' with the same parameter types + // public S2() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "S2").WithArguments("S2", "S2").WithLocation(4, 12), + // (4,12): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S2() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "S2").WithLocation(4, 12)); + } + + [Fact] + public void ExplicitConstructors_09() + { + var source = +@"record struct S1 +{ + public S1(object o) : base() { } +} +record struct S2() +{ + public S2(object o) : base() { } +} +record struct S3(char A) +{ + public S3(object o) : base() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (3,12): error CS0522: 'S1': structs cannot call base class constructors + // public S1(object o) : base() { } + Diagnostic(ErrorCode.ERR_StructWithBaseConstructorCall, "S1").WithArguments("S1").WithLocation(3, 12), + // (7,12): error CS0522: 'S2': structs cannot call base class constructors + // public S2(object o) : base() { } + Diagnostic(ErrorCode.ERR_StructWithBaseConstructorCall, "S2").WithArguments("S2").WithLocation(7, 12), + // (7,27): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S2(object o) : base() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "base").WithLocation(7, 27), + // (11,12): error CS0522: 'S3': structs cannot call base class constructors + // public S3(object o) : base() { } + Diagnostic(ErrorCode.ERR_StructWithBaseConstructorCall, "S3").WithArguments("S3").WithLocation(11, 12), + // (11,27): error CS8862: A constructor declared in a record with parameter list must have 'this' constructor initializer. + // public S3(object o) : base() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "base").WithLocation(11, 27)); + } } } From 588f8672fcd1e8b134818c32f656fb0bc0176d95 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 3 Jan 2022 09:37:22 -0800 Subject: [PATCH 004/187] Improve error reporting for 'this()' initializer from 'record struct' constructor --- .../Portable/Binder/Binder_Statements.cs | 33 +++++++++---- .../CSharp/Portable/CSharpResources.resx | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 4 +- .../Portable/xlf/CSharpResources.de.xlf | 4 +- .../Portable/xlf/CSharpResources.es.xlf | 4 +- .../Portable/xlf/CSharpResources.fr.xlf | 4 +- .../Portable/xlf/CSharpResources.it.xlf | 4 +- .../Portable/xlf/CSharpResources.ja.xlf | 4 +- .../Portable/xlf/CSharpResources.ko.xlf | 4 +- .../Portable/xlf/CSharpResources.pl.xlf | 4 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 4 +- .../Portable/xlf/CSharpResources.ru.xlf | 4 +- .../Portable/xlf/CSharpResources.tr.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 4 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 4 +- .../Semantic/Semantics/RecordStructTests.cs | 47 +++++++++++++++++-- 16 files changed, 93 insertions(+), 41 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 31869e224e126..5354029158f1a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3420,10 +3420,9 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, bool thisInitializer = initializer?.IsKind(SyntaxKind.ThisConstructorInitializer) == true; if (!thisInitializer && - hasAnyRecordConstructors()) + hasRecordPrimaryConstructor()) { - var constructorSymbol = (MethodSymbol)this.ContainingMember(); - if (!constructorSymbol.IsStatic && + if (isInstanceConstructor(out MethodSymbol constructorSymbol) && !SynthesizedRecordCopyCtor.IsCopyConstructor(constructorSymbol)) { // Note: we check the constructor initializer of copy constructors elsewhere @@ -3431,18 +3430,21 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, } } - // The `: this()` initializer is ignored when it is a default value type constructor - // and we need to include field initializers into the constructor. - bool skipInitializer = includesFieldInitializers - && thisInitializer + bool isDefaultValueTypeInitializer = thisInitializer && ContainingType.IsDefaultValueTypeConstructor(initializer); - if (skipInitializer && - hasAnyRecordConstructors()) + if (isDefaultValueTypeInitializer && + isInstanceConstructor(out _) && + hasRecordPrimaryConstructor()) { Error(diagnostics, ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, initializer.ThisOrBaseKeyword); } + // The `: this()` initializer is ignored when it is a default value type constructor + // and we need to include field initializers into the constructor. + bool skipInitializer = includesFieldInitializers + && isDefaultValueTypeInitializer; + // Using BindStatement to bind block to make sure we are reusing results of partial binding in SemanticModel return new BoundConstructorMethodBody(constructor, bodyBinder.GetDeclaredLocalsForScope(constructor), @@ -3454,8 +3456,19 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, bodyBinder.BindExpressionBodyAsBlock(constructor.ExpressionBody, constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded)); - bool hasAnyRecordConstructors() => + bool hasRecordPrimaryConstructor() => ContainingType.GetMembersUnordered().OfType().Any(); + + bool isInstanceConstructor(out MethodSymbol constructorSymbol) + { + if (this.ContainingMember() is MethodSymbol { IsStatic: false } method) + { + constructorSymbol = method; + return true; + } + constructorSymbol = null; + return false; + } } internal virtual BoundExpressionStatement BindConstructorInitializer(ConstructorInitializerSyntax initializer, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index c257fea3b7bd1..0eeff0bf52101 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6867,7 +6867,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A lambda expression with attributes cannot be converted to an expression tree - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. The operation may overflow '{0}' at runtime (use 'unchecked' syntax to override) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index d334071e2d152..7d7fd7db14c55 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 358f243c0c509..109a6b64113d2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index c029628899a86..c6db9fa9c1157 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 54f9dfccbeb5c..1796b2525a7c0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 9884be5d98434..2e11b9d9be67f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 832e8afce59f6..524f7a5924738 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 0980e056007d9..3401653261d80 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index e790851286280..fb76111aa5253 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 781f858282a8c..e1a3fff80b7ff 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 90dbd691b6a59..668c43448ae91 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index c3bdceded066e..71993a37a5ec9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 13572a101628a..581791125b476 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 06d23b0f04b18..fb39ce0ab12fa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1023,8 +1023,8 @@ - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. - A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index fa11e107a789a..6b887a5cad92b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -10780,10 +10780,10 @@ public S4(object o) : this() { } }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (3,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // (3,27): error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. // public S3(object o) : this() { } Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(3, 27), - // (7,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // (7,27): error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. // public S4(object o) : this() { } Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(7, 27)); } @@ -10805,10 +10805,10 @@ record struct S4(char A, char B) }"; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (4,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // (4,27): error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. // public S3(object o) : this() { F = o; } Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(4, 27), - // (9,27): error CS8982: A 'this' initializer for a 'record struct' constructor must call the primary constructor or an explicitly declared constructor. + // (9,27): error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. // public S4(object o) : this() { F = o; } Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(9, 27)); } @@ -10960,5 +10960,44 @@ public S3(object o) : base() { } // public S3(object o) : base() { } Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "base").WithLocation(11, 27)); } + + [Fact] + [WorkItem(58328, "https://github.com/dotnet/roslyn/issues/58328")] + public void ExplicitConstructors_10() + { + var source = +@"record struct S(object F) +{ + public object F; + public S(int i) : this() { F = i; } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (1,15): error CS0171: Field 'S.F' must be fully assigned before control is returned to the caller + // record struct S(object F) + Diagnostic(ErrorCode.ERR_UnassignedThis, "S").WithArguments("S.F").WithLocation(1, 15), + // (1,24): warning CS8907: Parameter 'F' is unread. Did you forget to use it to initialize the property with that name? + // record struct S(object F) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "F").WithArguments("F").WithLocation(1, 24), + // (4,23): error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this' initializer that calls the primary constructor or an explicitly declared constructor. + // public S(int i) : this() { F = i; } + Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(4, 23)); + } + + [Fact] + public void ExplicitConstructors_11() + { + var source = +@"record struct S(int X) +{ + static internal int F = 1; + static S() : this() { } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,18): error CS0514: 'S': static constructor cannot have an explicit 'this' or 'base' constructor call + // static S() : this() { } + Diagnostic(ErrorCode.ERR_StaticConstructorWithExplicitConstructorCall, "this").WithArguments("S").WithLocation(4, 18)); + } } } From 9d2819a5e53041b6df71aec7c251288509b505f6 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Mon, 29 Nov 2021 14:11:23 -0800 Subject: [PATCH 005/187] Require definite assignment of all fields if struct includes any field initializers (#57925) --- .../Portable/FlowAnalysis/FlowAnalysisPass.cs | 2 +- .../EditAndContinue/EditAndContinueTests.cs | 6 +- .../Semantics/StructConstructorTests.cs | 697 +++++++++++++----- 3 files changed, 529 insertions(+), 176 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs index cb6480c0e4ef7..cb74697895015 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs @@ -43,7 +43,7 @@ public static BoundBlock Rewrite( if (method.ReturnsVoid || method.IsIterator || method.IsAsyncEffectivelyReturningTask(compilation)) { // we don't analyze synthesized void methods. - if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer) || + if ((method.IsImplicitlyDeclared && !method.IsScriptInitializer && !(method.ContainingType.IsStructType() && method.IsParameterlessConstructor() && !method.IsDefaultValueTypeConstructor(requireZeroInit: true))) || Analyze(compilation, method, block, diagnostics)) { block = AppendImplicitReturn(block, method, originalBodyNested); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 8620daf21a198..a46b3a689be66 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -4932,7 +4932,7 @@ public void Struct_ImplementSynthesizedConstructor() struct S { int a = 1; - int b; + int b = 2; } "; var source1 = @@ -4940,11 +4940,11 @@ struct S struct S { int a = 1; - int b; + int b = 2; public S() { - b = 2; + b = 3; } } "; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs index 27977bcea55be..0b959997e1537 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StructConstructorTests.cs @@ -899,12 +899,6 @@ public void FieldInitializers_01() var source = @"#pragma warning disable 649 using System; -struct S1 -{ - object X = null; - object Y; - public override string ToString() => (X, Y).ToString(); -} struct S2 { object X = null; @@ -923,7 +917,6 @@ class Program { static void Main() { - Console.WriteLine(new S1()); Console.WriteLine(new S2()); Console.WriteLine(new S3()); } @@ -934,49 +927,33 @@ static void Main() // (5,12): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. // object X = null; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "X").WithArguments("struct field initializers", "10.0").WithLocation(5, 12), - // (11,12): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. - // object X = null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "X").WithArguments("struct field initializers", "10.0").WithLocation(11, 12), - // (13,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // (7,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. // public S2() { Y = 1; } - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S2").WithArguments("parameterless struct constructors", "10.0").WithLocation(13, 12), - // (19,12): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S2").WithArguments("parameterless struct constructors", "10.0").WithLocation(7, 12), + // (13,12): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. // object Y = null; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Y").WithArguments("struct field initializers", "10.0").WithLocation(19, 12)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Y").WithArguments("struct field initializers", "10.0").WithLocation(13, 12)); comp = CreateCompilation(source, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: -@"(, ) -(, 1) +@"(, 1) (, )"); verifier.VerifyIL("Program.Main", @"{ - // Code size 50 (0x32) + // Code size 35 (0x23) .maxstack 1 .locals init (S3 V_0) - IL_0000: newobj ""S1..ctor()"" - IL_0005: box ""S1"" + IL_0000: newobj ""S2..ctor()"" + IL_0005: box ""S2"" IL_000a: call ""void System.Console.WriteLine(object)"" - IL_000f: newobj ""S2..ctor()"" - IL_0014: box ""S2"" - IL_0019: call ""void System.Console.WriteLine(object)"" - IL_001e: ldloca.s V_0 - IL_0020: initobj ""S3"" - IL_0026: ldloc.0 - IL_0027: box ""S3"" - IL_002c: call ""void System.Console.WriteLine(object)"" - IL_0031: ret -}"); - verifier.VerifyIL("S1..ctor()", -@"{ - // Code size 8 (0x8) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldnull - IL_0002: stfld ""object S1.X"" - IL_0007: ret + IL_000f: ldloca.s V_0 + IL_0011: initobj ""S3"" + IL_0017: ldloc.0 + IL_0018: box ""S3"" + IL_001d: call ""void System.Console.WriteLine(object)"" + IL_0022: ret }"); verifier.VerifyIL("S2..ctor()", @"{ @@ -1012,12 +989,6 @@ public void FieldInitializers_02() var source = @"#pragma warning disable 649 using System; -struct S1 -{ - internal object X = 1; - internal object Y; - public override string ToString() => (X, Y).ToString(); -} struct S2 { internal object X = 2; @@ -1036,95 +1007,63 @@ class Program { static void Main() { - Console.WriteLine(new S1()); Console.WriteLine(new S2()); Console.WriteLine(new S3()); - Console.WriteLine(new S1 { }); Console.WriteLine(new S2 { }); Console.WriteLine(new S3 { }); - Console.WriteLine(new S1 { Y = 2 }); Console.WriteLine(new S2 { Y = 4 }); Console.WriteLine(new S3 { Y = 6 }); } }"; var verifier = CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: -@"(1, ) -(2, 2) +@"(2, 2) (, ) -(1, ) (2, 2) (, ) -(1, 2) (2, 4) (, 6)"); verifier.VerifyIL("Program.Main", @"{ - // Code size 193 (0xc1) + // Code size 132 (0x84) .maxstack 2 .locals init (S3 V_0, - S1 V_1, - S2 V_2) - IL_0000: newobj ""S1..ctor()"" - IL_0005: box ""S1"" + S2 V_1) + IL_0000: newobj ""S2..ctor()"" + IL_0005: box ""S2"" IL_000a: call ""void System.Console.WriteLine(object)"" - IL_000f: newobj ""S2..ctor()"" - IL_0014: box ""S2"" - IL_0019: call ""void System.Console.WriteLine(object)"" - IL_001e: ldloca.s V_0 - IL_0020: initobj ""S3"" - IL_0026: ldloc.0 - IL_0027: box ""S3"" + IL_000f: ldloca.s V_0 + IL_0011: initobj ""S3"" + IL_0017: ldloc.0 + IL_0018: box ""S3"" + IL_001d: call ""void System.Console.WriteLine(object)"" + IL_0022: newobj ""S2..ctor()"" + IL_0027: box ""S2"" IL_002c: call ""void System.Console.WriteLine(object)"" - IL_0031: newobj ""S1..ctor()"" - IL_0036: box ""S1"" - IL_003b: call ""void System.Console.WriteLine(object)"" - IL_0040: newobj ""S2..ctor()"" - IL_0045: box ""S2"" - IL_004a: call ""void System.Console.WriteLine(object)"" - IL_004f: ldloca.s V_0 - IL_0051: initobj ""S3"" - IL_0057: ldloc.0 - IL_0058: box ""S3"" - IL_005d: call ""void System.Console.WriteLine(object)"" - IL_0062: ldloca.s V_1 - IL_0064: call ""S1..ctor()"" - IL_0069: ldloca.s V_1 - IL_006b: ldc.i4.2 - IL_006c: box ""int"" - IL_0071: stfld ""object S1.Y"" - IL_0076: ldloc.1 - IL_0077: box ""S1"" - IL_007c: call ""void System.Console.WriteLine(object)"" - IL_0081: ldloca.s V_2 - IL_0083: call ""S2..ctor()"" - IL_0088: ldloca.s V_2 - IL_008a: ldc.i4.4 - IL_008b: box ""int"" - IL_0090: stfld ""object S2.Y"" - IL_0095: ldloc.2 - IL_0096: box ""S2"" - IL_009b: call ""void System.Console.WriteLine(object)"" - IL_00a0: ldloca.s V_0 - IL_00a2: initobj ""S3"" - IL_00a8: ldloca.s V_0 - IL_00aa: ldc.i4.6 - IL_00ab: box ""int"" - IL_00b0: stfld ""object S3.Y"" - IL_00b5: ldloc.0 - IL_00b6: box ""S3"" - IL_00bb: call ""void System.Console.WriteLine(object)"" - IL_00c0: ret -}"); - verifier.VerifyIL("S1..ctor()", -@"{ - // Code size 13 (0xd) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldc.i4.1 - IL_0002: box ""int"" - IL_0007: stfld ""object S1.X"" - IL_000c: ret + IL_0031: ldloca.s V_0 + IL_0033: initobj ""S3"" + IL_0039: ldloc.0 + IL_003a: box ""S3"" + IL_003f: call ""void System.Console.WriteLine(object)"" + IL_0044: ldloca.s V_1 + IL_0046: call ""S2..ctor()"" + IL_004b: ldloca.s V_1 + IL_004d: ldc.i4.4 + IL_004e: box ""int"" + IL_0053: stfld ""object S2.Y"" + IL_0058: ldloc.1 + IL_0059: box ""S2"" + IL_005e: call ""void System.Console.WriteLine(object)"" + IL_0063: ldloca.s V_0 + IL_0065: initobj ""S3"" + IL_006b: ldloca.s V_0 + IL_006d: ldc.i4.6 + IL_006e: box ""int"" + IL_0073: stfld ""object S3.Y"" + IL_0078: ldloc.0 + IL_0079: box ""S3"" + IL_007e: call ""void System.Console.WriteLine(object)"" + IL_0083: ret }"); verifier.VerifyIL("S2..ctor()", @"{ @@ -1164,12 +1103,6 @@ public void FieldInitializers_03() var source = @"#pragma warning disable 649 using System; -struct S1 -{ - internal object X { get; } = 1; - internal object Y { get; } - public override string ToString() => (X, Y).ToString(); -} struct S2 { internal object X { get; init; } = 2; @@ -1188,10 +1121,8 @@ class Program { static void Main() { - Console.WriteLine(new S1()); Console.WriteLine(new S2()); Console.WriteLine(new S3()); - Console.WriteLine(new S1 { }); Console.WriteLine(new S2 { }); Console.WriteLine(new S3 { }); Console.WriteLine(new S2 { Y = 4 }); @@ -1200,71 +1131,53 @@ static void Main() }"; var verifier = CompileAndVerify(new[] { source, IsExternalInitTypeDefinition }, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: -@"(1, ) -(2, 2) +@"(2, 2) (, ) -(1, ) (2, 2) (, ) (2, 4) (, 6)"); verifier.VerifyIL("Program.Main", @"{ - // Code size 162 (0xa2) + // Code size 132 (0x84) .maxstack 2 .locals init (S3 V_0, S2 V_1) - IL_0000: newobj ""S1..ctor()"" - IL_0005: box ""S1"" + IL_0000: newobj ""S2..ctor()"" + IL_0005: box ""S2"" IL_000a: call ""void System.Console.WriteLine(object)"" - IL_000f: newobj ""S2..ctor()"" - IL_0014: box ""S2"" - IL_0019: call ""void System.Console.WriteLine(object)"" - IL_001e: ldloca.s V_0 - IL_0020: initobj ""S3"" - IL_0026: ldloc.0 - IL_0027: box ""S3"" + IL_000f: ldloca.s V_0 + IL_0011: initobj ""S3"" + IL_0017: ldloc.0 + IL_0018: box ""S3"" + IL_001d: call ""void System.Console.WriteLine(object)"" + IL_0022: newobj ""S2..ctor()"" + IL_0027: box ""S2"" IL_002c: call ""void System.Console.WriteLine(object)"" - IL_0031: newobj ""S1..ctor()"" - IL_0036: box ""S1"" - IL_003b: call ""void System.Console.WriteLine(object)"" - IL_0040: newobj ""S2..ctor()"" - IL_0045: box ""S2"" - IL_004a: call ""void System.Console.WriteLine(object)"" - IL_004f: ldloca.s V_0 - IL_0051: initobj ""S3"" - IL_0057: ldloc.0 - IL_0058: box ""S3"" - IL_005d: call ""void System.Console.WriteLine(object)"" - IL_0062: ldloca.s V_1 - IL_0064: call ""S2..ctor()"" - IL_0069: ldloca.s V_1 - IL_006b: ldc.i4.4 - IL_006c: box ""int"" - IL_0071: call ""void S2.Y.init"" - IL_0076: ldloc.1 - IL_0077: box ""S2"" - IL_007c: call ""void System.Console.WriteLine(object)"" - IL_0081: ldloca.s V_0 - IL_0083: initobj ""S3"" - IL_0089: ldloca.s V_0 - IL_008b: ldc.i4.6 - IL_008c: box ""int"" - IL_0091: call ""void S3.Y.set"" - IL_0096: ldloc.0 - IL_0097: box ""S3"" - IL_009c: call ""void System.Console.WriteLine(object)"" - IL_00a1: ret -}"); - verifier.VerifyIL("S1..ctor()", -@"{ - // Code size 13 (0xd) - .maxstack 2 - IL_0000: ldarg.0 - IL_0001: ldc.i4.1 - IL_0002: box ""int"" - IL_0007: stfld ""object S1.k__BackingField"" - IL_000c: ret + IL_0031: ldloca.s V_0 + IL_0033: initobj ""S3"" + IL_0039: ldloc.0 + IL_003a: box ""S3"" + IL_003f: call ""void System.Console.WriteLine(object)"" + IL_0044: ldloca.s V_1 + IL_0046: call ""S2..ctor()"" + IL_004b: ldloca.s V_1 + IL_004d: ldc.i4.4 + IL_004e: box ""int"" + IL_0053: call ""void S2.Y.init"" + IL_0058: ldloc.1 + IL_0059: box ""S2"" + IL_005e: call ""void System.Console.WriteLine(object)"" + IL_0063: ldloca.s V_0 + IL_0065: initobj ""S3"" + IL_006b: ldloca.s V_0 + IL_006d: ldc.i4.6 + IL_006e: box ""int"" + IL_0073: call ""void S3.Y.set"" + IL_0078: ldloc.0 + IL_0079: box ""S3"" + IL_007e: call ""void System.Console.WriteLine(object)"" + IL_0083: ret }"); verifier.VerifyIL("S2..ctor()", @"{ @@ -1419,6 +1332,446 @@ .maxstack 2 }"); } + [WorkItem(57870, "https://github.com/dotnet/roslyn/issues/57870")] + [Fact] + public void FieldInitializers_06() + { + var source = +@"#pragma warning disable 649 +struct S1 +{ + internal object X = null; + internal object Y; +} +struct S2 +{ + internal object X = 2; + internal object Y; + public S2() { } +} +struct S3 +{ + internal object X; + internal object Y = 3; + public S3() { Y = 3; } +} +struct S4 +{ + internal object X; + internal object Y = 4; + public S4() { X = 4; } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (2,8): error CS0171: Field 'S1.Y' must be fully assigned before control is returned to the caller + // struct S1 + Diagnostic(ErrorCode.ERR_UnassignedThis, "S1").WithArguments("S1.Y").WithLocation(2, 8), + // (4,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object X = null; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "X").WithArguments("struct field initializers", "10.0").WithLocation(4, 21), + // (9,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object X = 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "X").WithArguments("struct field initializers", "10.0").WithLocation(9, 21), + // (11,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // public S2() { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S2").WithArguments("parameterless struct constructors", "10.0").WithLocation(11, 12), + // (11,12): error CS0171: Field 'S2.Y' must be fully assigned before control is returned to the caller + // public S2() { } + Diagnostic(ErrorCode.ERR_UnassignedThis, "S2").WithArguments("S2.Y").WithLocation(11, 12), + // (16,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object Y = 3; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Y").WithArguments("struct field initializers", "10.0").WithLocation(16, 21), + // (17,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S3").WithArguments("parameterless struct constructors", "10.0").WithLocation(17, 12), + // (17,12): error CS0171: Field 'S3.X' must be fully assigned before control is returned to the caller + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_UnassignedThis, "S3").WithArguments("S3.X").WithLocation(17, 12), + // (22,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object Y = 4; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Y").WithArguments("struct field initializers", "10.0").WithLocation(22, 21), + // (23,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // public S4() { X = 4; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S4").WithArguments("parameterless struct constructors", "10.0").WithLocation(23, 12)); + + var expectedDiagnostics = new[] + { + // (2,8): error CS0171: Field 'S1.Y' must be fully assigned before control is returned to the caller + // struct S1 + Diagnostic(ErrorCode.ERR_UnassignedThis, "S1").WithArguments("S1.Y").WithLocation(2, 8), + // (11,12): error CS0171: Field 'S2.Y' must be fully assigned before control is returned to the caller + // public S2() { } + Diagnostic(ErrorCode.ERR_UnassignedThis, "S2").WithArguments("S2.Y").WithLocation(11, 12), + // (17,12): error CS0171: Field 'S3.X' must be fully assigned before control is returned to the caller + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_UnassignedThis, "S3").WithArguments("S3.X").WithLocation(17, 12), + }; + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [WorkItem(57870, "https://github.com/dotnet/roslyn/issues/57870")] + [Fact] + public void FieldInitializers_07() + { + var source = +@"#pragma warning disable 649 +struct S1 +{ + internal object X { get; } = null; + internal object Y { get; } +} +struct S2 +{ + internal object X { get; } = 2; + internal object Y { get; } + public S2() { } +} +struct S3 +{ + internal object X { get; } + internal object Y { get; } = 3; + public S3() { Y = 3; } +} +struct S4 +{ + internal object X { get; } + internal object Y { get; } = 4; + public S4() { X = 4; } +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (2,8): error CS0843: Auto-implemented property 'S1.Y' must be fully assigned before control is returned to the caller. + // struct S1 + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S1").WithArguments("S1.Y").WithLocation(2, 8), + // (4,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object X { get; } = null; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "X").WithArguments("struct field initializers", "10.0").WithLocation(4, 21), + // (9,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object X { get; } = 2; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "X").WithArguments("struct field initializers", "10.0").WithLocation(9, 21), + // (11,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // public S2() { } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S2").WithArguments("parameterless struct constructors", "10.0").WithLocation(11, 12), + // (11,12): error CS0843: Auto-implemented property 'S2.Y' must be fully assigned before control is returned to the caller. + // public S2() { } + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S2").WithArguments("S2.Y").WithLocation(11, 12), + // (16,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object Y { get; } = 3; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Y").WithArguments("struct field initializers", "10.0").WithLocation(16, 21), + // (17,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S3").WithArguments("parameterless struct constructors", "10.0").WithLocation(17, 12), + // (17,12): error CS0843: Auto-implemented property 'S3.X' must be fully assigned before control is returned to the caller. + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S3").WithArguments("S3.X").WithLocation(17, 12), + // (22,21): error CS8773: Feature 'struct field initializers' is not available in C# 9.0. Please use language version 10.0 or greater. + // internal object Y { get; } = 4; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Y").WithArguments("struct field initializers", "10.0").WithLocation(22, 21), + // (23,12): error CS8773: Feature 'parameterless struct constructors' is not available in C# 9.0. Please use language version 10.0 or greater. + // public S4() { X = 4; } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "S4").WithArguments("parameterless struct constructors", "10.0").WithLocation(23, 12)); + + var expectedDiagnostics = new[] + { + // (2,8): error CS0843: Auto-implemented property 'S1.Y' must be fully assigned before control is returned to the caller. + // struct S1 + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S1").WithArguments("S1.Y").WithLocation(2, 8), + // (11,12): error CS0843: Auto-implemented property 'S2.Y' must be fully assigned before control is returned to the caller. + // public S2() { } + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S2").WithArguments("S2.Y").WithLocation(11, 12), + // (17,12): error CS0843: Auto-implemented property 'S3.X' must be fully assigned before control is returned to the caller. + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S3").WithArguments("S3.X").WithLocation(17, 12), + }; + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [WorkItem(57870, "https://github.com/dotnet/roslyn/issues/57870")] + [Fact] + public void FieldInitializers_08() + { + var source = +@"#pragma warning disable 649 +record struct S1 +{ + internal object X = 1; + internal object Y; +} +record struct S2 +{ + internal object X { get; } = 2; + internal object Y { get; } + public S2() { } +} +record struct S3 +{ + internal object X { get; init; } + internal object Y { get; init; } = 3; + public S3() { Y = 3; } +} +record struct S4 +{ + internal object X { get; init; } + internal object Y { get; init; } = 4; + public S4() { X = 4; } +} +"; + + var expectedDiagnostics = new[] + { + // (2,15): error CS0171: Field 'S1.Y' must be fully assigned before control is returned to the caller + // record struct S1 + Diagnostic(ErrorCode.ERR_UnassignedThis, "S1").WithArguments("S1.Y").WithLocation(2, 15), + // (11,12): error CS0843: Auto-implemented property 'S2.Y' must be fully assigned before control is returned to the caller. + // public S2() { } + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S2").WithArguments("S2.Y").WithLocation(11, 12), + // (17,12): error CS0843: Auto-implemented property 'S3.X' must be fully assigned before control is returned to the caller. + // public S3() { Y = 3; } + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S3").WithArguments("S3.X").WithLocation(17, 12) + }; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void FieldInitializers_09() + { + var source = +@"#pragma warning disable 649 +record struct S1() +{ + internal object X = 1; + internal object Y; +} +record struct S2() +{ + internal object X { get; } = 2; + internal object Y { get; } +} +record struct S3() +{ + internal object X { get; init; } + internal object Y { get; init; } = 3; +} +"; + + var expectedDiagnostics = new[] + { + // (2,15): error CS0171: Field 'S1.Y' must be fully assigned before control is returned to the caller + // record struct S1() + Diagnostic(ErrorCode.ERR_UnassignedThis, "S1").WithArguments("S1.Y").WithLocation(2, 15), + // (7,15): error CS0843: Auto-implemented property 'S2.Y' must be fully assigned before control is returned to the caller. + // record struct S2() + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S2").WithArguments("S2.Y").WithLocation(7, 15), + // (12,15): error CS0843: Auto-implemented property 'S3.X' must be fully assigned before control is returned to the caller. + // record struct S3() + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S3").WithArguments("S3.X").WithLocation(12, 15) + }; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void FieldInitializers_10() + { + var source = +@"#pragma warning disable 649 +record struct S1(object X) +{ + internal object X = 1; + internal object Y; +} +record struct S2(object X) +{ + internal object X { get; } = 2; + internal object Y { get; } +} +record struct S3(object Y) +{ + internal object X { get; init; } + internal object Y { get; init; } = 3; +} +"; + + var expectedDiagnostics = new[] + { + // (2,15): error CS0171: Field 'S1.Y' must be fully assigned before control is returned to the caller + // record struct S1(object X) + Diagnostic(ErrorCode.ERR_UnassignedThis, "S1").WithArguments("S1.Y").WithLocation(2, 15), + // (2,25): warning CS8907: Parameter 'X' is unread. Did you forget to use it to initialize the property with that name? + // record struct S1(object X) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "X").WithArguments("X").WithLocation(2, 25), + // (7,15): error CS0843: Auto-implemented property 'S2.Y' must be fully assigned before control is returned to the caller. + // record struct S2(object X) + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S2").WithArguments("S2.Y").WithLocation(7, 15), + // (7,25): warning CS8907: Parameter 'X' is unread. Did you forget to use it to initialize the property with that name? + // record struct S2(object X) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "X").WithArguments("X").WithLocation(7, 25), + // (12,15): error CS0843: Auto-implemented property 'S3.X' must be fully assigned before control is returned to the caller. + // record struct S3(object Y) + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S3").WithArguments("S3.X").WithLocation(12, 15), + // (12,25): warning CS8907: Parameter 'Y' is unread. Did you forget to use it to initialize the property with that name? + // record struct S3(object Y) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "Y").WithArguments("Y").WithLocation(12, 25) + }; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void FieldInitializers_11() + { + var source = +@"#pragma warning disable 649 +record struct S1(object X) +{ + internal object X; + internal object Y = 1; +} +record struct S2(object X) +{ + internal object X { get; } + internal object Y { get; } = 2; +} +record struct S3(object Y) +{ + internal object X { get; init; } = 3; + internal object Y { get; init; } +} +"; + + var expectedDiagnostics = new[] + { + // (2,15): error CS0171: Field 'S1.X' must be fully assigned before control is returned to the caller + // record struct S1(object X) + Diagnostic(ErrorCode.ERR_UnassignedThis, "S1").WithArguments("S1.X").WithLocation(2, 15), + // (2,25): warning CS8907: Parameter 'X' is unread. Did you forget to use it to initialize the property with that name? + // record struct S1(object X) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "X").WithArguments("X").WithLocation(2, 25), + // (7,15): error CS0843: Auto-implemented property 'S2.X' must be fully assigned before control is returned to the caller. + // record struct S2(object X) + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S2").WithArguments("S2.X").WithLocation(7, 15), + // (7,25): warning CS8907: Parameter 'X' is unread. Did you forget to use it to initialize the property with that name? + // record struct S2(object X) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "X").WithArguments("X").WithLocation(7, 25), + // (12,15): error CS0843: Auto-implemented property 'S3.Y' must be fully assigned before control is returned to the caller. + // record struct S3(object Y) + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S3").WithArguments("S3.Y").WithLocation(12, 15), + // (12,25): warning CS8907: Parameter 'Y' is unread. Did you forget to use it to initialize the property with that name? + // record struct S3(object Y) + Diagnostic(ErrorCode.WRN_UnreadRecordParameter, "Y").WithArguments("Y").WithLocation(12, 25) + }; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void FieldInitializers_12() + { + var source = +@"#pragma warning disable 649 +using System; +record struct S1(object X) +{ + internal object Y = 1; +} +record struct S2(object X) +{ + internal object Y { get; } = 2; +} +record struct S3(object X) +{ + internal object Y { get; init; } = 3; +} +class Program +{ + static void Main() + { + Console.WriteLine(new S1()); + Console.WriteLine(new S1(10)); + Console.WriteLine(new S2()); + Console.WriteLine(new S2(20)); + Console.WriteLine(new S3()); + Console.WriteLine(new S3(30)); + } +} +"; + + var expectedOutput = +@"S1 { X = } +S1 { X = 10 } +S2 { X = } +S2 { X = 20 } +S3 { X = } +S3 { X = 30 } +"; + + CompileAndVerify(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10, verify: Verification.Skipped, expectedOutput: expectedOutput); + CompileAndVerify(new[] { source, IsExternalInitTypeDefinition }, verify: Verification.Skipped, expectedOutput: expectedOutput); + } + + [WorkItem(57870, "https://github.com/dotnet/roslyn/issues/57870")] + [Fact] + public void FieldInitializers_13() + { + var source = +@"#nullable enable + +using System; + +var x = new S { P1 = ""x1"", P2 = ""x2"" }; +var y = new S { P2 = ""y2"" }; + +Console.WriteLine(y.P1); + +record struct S +{ + public string? P1 { get; init; } + public string? P2 { get; init; } = """"; +}"; + + var expectedDiagnostics = new[] + { + // (10,15): error CS0843: Auto-implemented property 'S.P1' must be fully assigned before control is returned to the caller. + // record struct S + Diagnostic(ErrorCode.ERR_UnassignedThisAutoProperty, "S").WithArguments("S.P1").WithLocation(10, 15) + }; + + var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + [Fact] public void ExpressionTrees() { From 503b65ca8edf25d303162c2951c055766b06e837 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 15 Dec 2021 16:38:25 -0800 Subject: [PATCH 006/187] Support live diagnostics in source generated files --- .../Squiggles/ErrorSquiggleProducerTests.cs | 28 +++++- .../CSharpTest/Workspaces/WorkspaceTests.cs | 96 +++++++++++++++++++ .../AbstractDiagnosticsTaggerProvider.cs | 3 +- .../DiagnosticAnalyzerServiceTests.cs | 30 +++++- .../Test/Diagnostics/DiagnosticDataTests.cs | 57 +++++++++++ .../AbstractLanguageServerProtocolTests.cs | 42 +++++--- .../Workspaces/TestHostDocument.cs | 18 +++- .../TestUtilities/Workspaces/TestWorkspace.cs | 28 +++++- ...gnosticIncrementalAnalyzer.ProjectState.cs | 3 +- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 3 +- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 3 +- ...WorkCoordinator.NormalPriorityProcessor.cs | 2 +- .../SolutionCrawler/WorkCoordinator.cs | 8 ++ .../Portable/Workspace/BackgroundCompiler.cs | 4 + .../Portable/Workspace/BackgroundParser.cs | 2 + .../Protocol/Extensions/Extensions.cs | 6 +- .../Extensions/ProtocolConversions.cs | 8 ++ .../AbstractPullDiagnosticHandler.cs | 4 +- .../DocumentPullDiagnosticHandler.cs | 5 +- ...erimentalDocumentPullDiagnosticsHandler.cs | 5 +- ...rimentalWorkspacePullDiagnosticsHandler.cs | 4 +- .../WorkspacePullDiagnosticHandler.cs | 25 +++-- .../Diagnostics/PullDiagnosticTests.cs | 29 +++++- .../Workspaces/LspWorkspaceManagerTests.cs | 4 +- .../Lsif/GeneratorTest/FoldingRangeTests.vb | 2 +- .../Core/Test/Venus/DocumentServiceTests.vb | 20 ++++ .../Diagnostics/DiagnosticAnalysisResult.cs | 3 +- .../Core/Portable/Diagnostics/Extensions.cs | 13 ++- .../Shared/Extensions/ProjectExtensions.cs | 13 +++ .../Solution/SourceGeneratedDocumentState.cs | 38 +++++++- .../Portable/Workspace/Workspace_Editor.cs | 18 ++++ .../Portable/Workspace/Workspace_Events.cs | 68 +++++++++++++ 32 files changed, 537 insertions(+), 55 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs index 599213a3cc41b..2fec6449809e5 100644 --- a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CodeStyle; @@ -16,7 +17,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; -using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; @@ -47,6 +47,16 @@ public async Task ErrorTagGeneratedForError() Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); } + [WpfFact, Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] + public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument() + { + var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {"); + Assert.Equal(1, spans.Count()); + + var firstSpan = spans.First(); + Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] public async Task ErrorTagGeneratedForWarning() { @@ -350,6 +360,22 @@ public LiveId() private static async Task>> GetTagSpansAsync(string content) { using var workspace = TestWorkspace.CreateCSharp(content, composition: SquiggleUtilities.CompositionWithSolutionCrawler); + return await GetTagSpansAsync(workspace); + } + + private static async Task>> GetTagSpansInSourceGeneratedDocumentAsync(string content) + { + var workspaceElement = $@" + + {new XText(content)} + +"; + using var workspace = TestWorkspace.Create(workspaceElement, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); + return await GetTagSpansAsync(workspace); + } + + private static async Task>> GetTagSpansAsync(TestWorkspace workspace) + { return (await TestDiagnosticTagProducer.GetDiagnosticsAndErrorSpans(workspace)).Item2; } diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs index cf4a26490a9f3..225283a85cc2c 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs @@ -6,9 +6,11 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.UnitTests; @@ -783,6 +785,100 @@ public async Task TestDocumentEvents() shortEventTimeout.Seconds)); } + [Fact] + public async Task TestSourceGeneratedDocumentEvents() + { + var doc1Text = "public class C { }"; + var workspaceElement = $@" + + {new XText(doc1Text)} + +"; + using var workspace = TestWorkspace.Create(workspaceElement, composition: EditorTestCompositions.EditorFeatures); + var document = workspace.Documents.Single(); + + var longEventTimeout = TimeSpan.FromMinutes(5); + var shortEventTimeout = TimeSpan.FromSeconds(5); + + // Creating two waiters that will allow us to know for certain if the events have fired. + using var closeWaiter = new EventWaiter(); + using var openWaiter = new EventWaiter(); + using var sourceGeneratedCloseWaiter = new EventWaiter(); + using var sourceGeneratedOpenWaiter = new EventWaiter(); + // Wrapping event handlers so they can notify us on being called. + var documentOpenedEventHandler = openWaiter.Wrap( + (sender, args) => Assert.True(false, $"Unexpected raising of {nameof(Workspace.DocumentOpened)}.")); + + var documentClosedEventHandler = closeWaiter.Wrap( + (sender, args) => Assert.True(false, $"Unexpected raising of {nameof(Workspace.DocumentClosed)}.")); + + var sourceGeneratedDocumentOpenedEventHandler = sourceGeneratedOpenWaiter.Wrap( + (sender, args) => Assert.True(args.Document.Id == document.Id, + $"The document given to the '{nameof(Workspace.SourceGeneratedDocumentOpened)}' event handler did not have the same id as the one created for the test.")); + + var sourceGeneratedDocumentClosedEventHandler = sourceGeneratedCloseWaiter.Wrap( + (sender, args) => Assert.True(args.Document.Id == document.Id, + $"The document given to the '{nameof(Workspace.SourceGeneratedDocumentClosed)}' event handler did not have the same id as the one created for the test.")); + + workspace.DocumentOpened += documentOpenedEventHandler; + workspace.DocumentClosed += documentClosedEventHandler; + workspace.SourceGeneratedDocumentOpened += sourceGeneratedDocumentOpenedEventHandler; + workspace.SourceGeneratedDocumentClosed += sourceGeneratedDocumentClosedEventHandler; + + workspace.OpenSourceGeneratedDocument(document.Id); + var sourceGeneratedDocumentId = workspace.GetDocumentIdInCurrentContext(document.GetOpenTextContainer()); + Assert.Equal(document.Id, sourceGeneratedDocumentId); + + workspace.CloseSourceGeneratedDocument(document.Id); + + // Wait for all workspace tasks to finish. After this is finished executing, all handlers should have been notified. + await WaitForWorkspaceOperationsToComplete(workspace); + + // Wait to receive signal that events have fired. + Assert.False(openWaiter.WaitForEventToFire(shortEventTimeout), + string.Format("event handler 'DocumentOpened' was called within {0} seconds though it was not expected.", + shortEventTimeout.Seconds)); + + Assert.False(closeWaiter.WaitForEventToFire(shortEventTimeout), + string.Format("event handler 'DocumentClosed' was called within {0} seconds though it was not expected.", + shortEventTimeout.Seconds)); + + Assert.True(sourceGeneratedOpenWaiter.WaitForEventToFire(longEventTimeout), + string.Format("event 'SourceGeneratedDocumentOpened' was not fired within {0} minutes.", + longEventTimeout.Minutes)); + + Assert.True(sourceGeneratedCloseWaiter.WaitForEventToFire(longEventTimeout), + string.Format("event 'SourceGeneratedDocumentClosed' was not fired within {0} minutes.", + longEventTimeout.Minutes)); + + workspace.SourceGeneratedDocumentOpened -= sourceGeneratedDocumentOpenedEventHandler; + workspace.SourceGeneratedDocumentClosed -= sourceGeneratedDocumentClosedEventHandler; + + workspace.OpenSourceGeneratedDocument(document.Id); + workspace.CloseSourceGeneratedDocument(document.Id); + + // Wait for all workspace tasks to finish. After this is finished executing, all handlers should have been notified. + await WaitForWorkspaceOperationsToComplete(workspace); + + // Verifying that an event has not been called is difficult to prove. + // All events should have already been called so we wait 5 seconds and then assume the event handler was removed correctly. + Assert.False(openWaiter.WaitForEventToFire(shortEventTimeout), + string.Format("event handler 'DocumentOpened' was called within {0} seconds though it was not expected.", + shortEventTimeout.Seconds)); + + Assert.False(closeWaiter.WaitForEventToFire(shortEventTimeout), + string.Format("event handler 'DocumentClosed' was called within {0} seconds though it was not expected.", + shortEventTimeout.Seconds)); + + Assert.False(sourceGeneratedOpenWaiter.WaitForEventToFire(shortEventTimeout), + string.Format("event handler 'SourceGeneratedDocumentOpened' was called within {0} seconds though it was removed from the list.", + shortEventTimeout.Seconds)); + + Assert.False(sourceGeneratedCloseWaiter.WaitForEventToFire(shortEventTimeout), + string.Format("event handler 'SourceGeneratedDocumentClosed' was called within {0} seconds though it was removed from the list.", + shortEventTimeout.Seconds)); + } + [Fact] public async Task TestAdditionalFile_Properties() { diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs index 337881d45f6f1..5e48835453051 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -75,7 +75,8 @@ private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) return; } - var document = e.Solution.GetDocument(e.DocumentId); + var document = e.Solution.GetDocument(e.DocumentId) + ?? e.Solution.GetProject(e.DocumentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(e.DocumentId); // Open documents *should* always have their SourceText available, but we cannot guarantee // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index b1a8a2ae99378..d11ccf8dde4c2 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions; using Microsoft.CodeAnalysis.Diagnostics; @@ -793,7 +794,7 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS } [Theory, CombinatorialData] - internal async Task TestRemoveUnnecessaryInlineSuppressionsAnalyzer(BackgroundAnalysisScope analysisScope, bool testPragma) + internal async Task TestRemoveUnnecessaryInlineSuppressionsAnalyzer(BackgroundAnalysisScope analysisScope, bool isSourceGenerated, bool testPragma) { var analyzers = ImmutableArray.Create( new CSharpCompilerDiagnosticAnalyzer(), @@ -837,14 +838,25 @@ void M() "; } - using var workspace = TestWorkspace.CreateCSharp(code, composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService))); + var documentElementName = isSourceGenerated ? "DocumentFromSourceGenerator" : "Document"; + var workspaceElement = $@" + + <{documentElementName} FilePath=""test1.cs"">{new XText(code)} + +"; + + using var workspace = TestWorkspace.Create(workspaceElement, composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService))); var options = workspace.Options.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope); workspace.SetOptions(options); workspace.TryApplyChanges(workspace.CurrentSolution.WithAnalyzerReferences(new[] { analyzerReference })); var project = workspace.CurrentSolution.Projects.Single(); - var document = project.Documents.Single(); + var document = isSourceGenerated ? (await project.GetSourceGeneratedDocumentsAsync(CancellationToken.None)).Single() : project.Documents.Single(); + if (isSourceGenerated) + Assert.IsType(document); + else + Assert.IsType(document); Assert.IsType(workspace.GetService()); var service = Assert.IsType(workspace.GetService()); @@ -864,14 +876,22 @@ void M() switch (analysisScope) { case BackgroundAnalysisScope.ActiveFile: - workspace.OpenDocument(document.Id); + if (isSourceGenerated) + workspace.OpenSourceGeneratedDocument(document.Id); + else + workspace.OpenDocument(document.Id); + var documentTrackingService = (TestDocumentTrackingService)workspace.Services.GetRequiredService(); documentTrackingService.SetActiveDocument(document.Id); await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); break; case BackgroundAnalysisScope.OpenFilesAndProjects: - workspace.OpenDocument(document.Id); + if (isSourceGenerated) + workspace.OpenSourceGeneratedDocument(document.Id); + else + workspace.OpenDocument(document.Id); + await incrementalAnalyzer.AnalyzeDocumentAsync(document, bodyOpt: null, InvocationReasons.SemanticChanged, CancellationToken.None); break; diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs index 63ede8d678a31..357bf95bc8f73 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticDataTests.cs @@ -4,12 +4,15 @@ #nullable disable +using System; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; @@ -174,5 +177,59 @@ public async Task DiagnosticData_ExternalAdditionalLocationIsPreserved() Assert.Equal(externalAdditionalLocation.OriginalEndLine, roundTripAdditionalLocation.OriginalEndLine); Assert.Equal(externalAdditionalLocation.OriginalEndLine, roundTripAdditionalLocation.OriginalEndLine); } + + [Fact, Trait(Traits.Feature, Traits.Features.Diagnostics)] + public async Task DiagnosticData_SourceGeneratedDocumentLocationIsPreserved() + { + var content = @" +namespace B +{ + class A + { + } +} +"; + using var workspace = TestWorkspace.CreateCSharp(files: Array.Empty(), sourceGeneratedFiles: new[] { content }, composition: EditorTestCompositions.EditorFeatures); + var hostDocument = workspace.Documents.Single(); + Assert.True(hostDocument.IsSourceGenerated); + + var documentId = hostDocument.Id; + var project = workspace.CurrentSolution.GetRequiredProject(documentId.ProjectId); + var document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None); + + await VerifyTextSpanAsync(content, 3, 10, 3, 11, new TextSpan(28, 1)); + var location = new DiagnosticDataLocation( + documentId, sourceSpan: new TextSpan(28, 1), originalFilePath: document.FilePath, + originalStartLine: 3, originalStartColumn: 10, originalEndLine: 3, originalEndColumn: 11); + + var diagnosticData = new DiagnosticData( + id: "test1", + category: "Test", + message: "test1 message", + enuMessageForBingSearch: "test1 message format", + severity: DiagnosticSeverity.Info, + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: true, + warningLevel: 1, + projectId: documentId.ProjectId, + customTags: ImmutableArray.Empty, + properties: ImmutableDictionary.Empty, + location: location, + additionalLocations: ImmutableArray.Empty, + language: project.Language); + + var diagnostic = await diagnosticData.ToDiagnosticAsync(project, CancellationToken.None); + var roundTripDiagnosticData = DiagnosticData.Create(diagnostic, document); + + var roundTripLocation = roundTripDiagnosticData.DataLocation; + Assert.NotNull(roundTripDiagnosticData.DataLocation); + Assert.Equal(location.DocumentId, roundTripLocation.DocumentId); + Assert.Equal(location.SourceSpan, roundTripLocation.SourceSpan); + Assert.Equal(location.OriginalFilePath, roundTripLocation.OriginalFilePath); + Assert.Equal(location.OriginalStartLine, roundTripLocation.OriginalStartLine); + Assert.Equal(location.OriginalStartColumn, roundTripLocation.OriginalStartColumn); + Assert.Equal(location.OriginalEndLine, roundTripLocation.OriginalEndLine); + Assert.Equal(location.OriginalEndLine, roundTripLocation.OriginalEndLine); + } } } diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 261b85d0d82b1..2c46002bc4acf 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; @@ -278,10 +277,10 @@ private protected static CodeActionResolveData CreateCodeActionResolveData(strin /// Creates an LSP server backed by a workspace instance with a solution containing the markup. /// protected Task CreateTestLspServerAsync(string markup) - => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.CSharp); + => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), LanguageNames.CSharp); protected Task CreateVisualBasicTestLspServerAsync(string markup) - => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.VisualBasic); + => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), LanguageNames.VisualBasic); protected Task CreateMultiProjectLspServerAsync(string xmlMarkup) => CreateTestLspServerAsync(TestWorkspace.Create(xmlMarkup, composition: Composition)); @@ -290,14 +289,20 @@ protected Task CreateMultiProjectLspServerAsync(string xmlMarkup) /// Creates an LSP server backed by a workspace instance with a solution containing the specified documents. /// protected Task CreateTestLspServerAsync(string[] markups) - => CreateTestLspServerAsync(markups, LanguageNames.CSharp); + => CreateTestLspServerAsync(markups, Array.Empty()); - private Task CreateTestLspServerAsync(string[] markups, string languageName) + /// + /// Creates an LSP server backed by a workspace instance with a solution containing the specified documents. + /// + protected Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups) + => CreateTestLspServerAsync(markups, sourceGeneratedMarkups, LanguageNames.CSharp); + + private Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups, string languageName) { var workspace = languageName switch { - LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, composition: Composition), - LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, composition: Composition), + LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, sourceGeneratedMarkups, composition: Composition), + LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, sourceGeneratedMarkups, composition: Composition), _ => throw new ArgumentException($"language name {languageName} is not valid for a test workspace"), }; @@ -310,6 +315,9 @@ private static async Task CreateTestLspServerAsync(TestWorkspace foreach (var document in workspace.Documents) { + if (document.IsSourceGenerated) + continue; + solution = solution.WithDocumentFilePath(document.Id, GetDocumentFilePathFromName(document.Name)); } @@ -320,7 +328,7 @@ private static async Task CreateTestLspServerAsync(TestWorkspace // created by the initial test steps. This can interfere with the expected test state. await WaitForWorkspaceOperationsAsync(workspace); - return new TestLspServer(workspace); + return await TestLspServer.CreateAsync(workspace); } protected async Task CreateXmlTestLspServerAsync(string xmlContent, string? workspaceKind = null) @@ -331,7 +339,7 @@ protected async Task CreateXmlTestLspServerAsync(string xmlConten // Otherwise we could have a race where workspace change events triggered by creation are changing the state // created by the initial test steps. This can interfere with the expected test state. await WaitForWorkspaceOperationsAsync(workspace); - return new TestLspServer(workspace); + return await TestLspServer.CreateAsync(workspace); } /// @@ -361,13 +369,13 @@ protected static void AddMappedDocument(Workspace workspace, string markup) workspace.TryApplyChanges(newSolution); } - public static Dictionary> GetAnnotatedLocations(TestWorkspace workspace, Solution solution) + public static async Task>> GetAnnotatedLocationsAsync(TestWorkspace workspace, Solution solution) { var locations = new Dictionary>(); foreach (var testDocument in workspace.Documents) { - var document = solution.GetRequiredDocument(testDocument.Id); - var text = document.GetTextSynchronously(CancellationToken.None); + var document = await solution.GetRequiredDocumentAsync(testDocument.Id, includeSourceGenerated: true, CancellationToken.None); + var text = await document.GetTextAsync(CancellationToken.None); foreach (var (name, spans) in testDocument.AnnotatedSpans) { var locationsForName = locations.GetValueOrDefault(name, new List()); @@ -456,10 +464,10 @@ public sealed class TestLspServer : IDisposable private readonly RequestExecutionQueue _executionQueue; private readonly Dictionary> _locations; - internal TestLspServer(TestWorkspace testWorkspace) + private TestLspServer(TestWorkspace testWorkspace, Dictionary> locations) { TestWorkspace = testWorkspace; - _locations = GetAnnotatedLocations(testWorkspace, testWorkspace.CurrentSolution); + _locations = locations; _requestDispatcher = CreateRequestDispatcher(testWorkspace); _executionQueue = CreateRequestQueue(testWorkspace); @@ -471,6 +479,12 @@ internal TestLspServer(TestWorkspace testWorkspace) GetManagerAccessor().ResetLspSolutions(); } + public static async ValueTask CreateAsync(TestWorkspace workspace) + { + var locations = await GetAnnotatedLocationsAsync(workspace, workspace.CurrentSolution); + return new TestLspServer(workspace, locations); + } + public Task ExecuteRequestAsync(string methodName, RequestType request, LSP.ClientCapabilities clientCapabilities, string? clientName, CancellationToken cancellationToken) where RequestType : class { diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index 74caef655974a..8edb388c240fd 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -14,6 +15,7 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Roslyn.Test.Utilities; +using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces @@ -261,8 +263,20 @@ public ITextBuffer2 GetTextBuffer() { if (!workspace.IsDocumentOpen(linkedId)) { - // If there is a linked file, we'll start the non-linked one as being the primary context, which some tests depend on. - workspace.OnDocumentOpened(linkedId, _textBuffer.AsTextContainer(), isCurrentContext: !testDocument.IsLinkFile); + if (testDocument.IsSourceGenerated) + { + var hintName = testDocument.Name; + var generator = new SingleFileTestGenerator(); + var generatorAssemblyName = SourceGeneratedDocumentIdentity.GetGeneratorAssemblyName(generator); + var generatorTypeName = SourceGeneratedDocumentIdentity.GetGeneratorTypeName(generator); + var filePath = testDocument.FilePath ?? throw new InvalidOperationException(); + workspace.OnSourceGeneratedDocumentOpened(new SourceGeneratedDocumentIdentity(linkedId, hintName, generatorAssemblyName, generatorTypeName, filePath), _textBuffer.AsTextContainer()); + } + else + { + // If there is a linked file, we'll start the non-linked one as being the primary context, which some tests depend on. + workspace.OnDocumentOpened(linkedId, _textBuffer.AsTextContainer(), isCurrentContext: !testDocument.IsLinkFile); + } } } } diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs index e72ae63318d1e..b0e7cefa996c9 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs @@ -671,16 +671,40 @@ private static void MapMarkupSpans( public override void OpenDocument(DocumentId documentId, bool activate = true) { - // Fetching the open SourceTextContanier implicitly opens the document. - GetTestDocument(documentId).GetOpenTextContainer(); + // Fetching the open SourceTextContainer implicitly opens the document. + var testDocument = GetTestDocument(documentId); + Contract.ThrowIfTrue(testDocument.IsSourceGenerated); + + testDocument.GetOpenTextContainer(); } public override void CloseDocument(DocumentId documentId) { var testDocument = this.GetTestDocument(documentId); + Contract.ThrowIfTrue(testDocument.IsSourceGenerated); + Contract.ThrowIfFalse(IsDocumentOpen(documentId)); + this.OnDocumentClosed(documentId, testDocument.Loader); } + public void OpenSourceGeneratedDocument(DocumentId documentId) + { + // Fetching the open SourceTextContainer implicitly opens the document. + var testDocument = GetTestDocument(documentId); + Contract.ThrowIfFalse(testDocument.IsSourceGenerated); + + testDocument.GetOpenTextContainer(); + } + + public void CloseSourceGeneratedDocument(DocumentId documentId) + { + var testDocument = this.GetTestDocument(documentId); + Contract.ThrowIfFalse(testDocument.IsSourceGenerated); + Contract.ThrowIfFalse(IsDocumentOpen(documentId)); + + this.OnSourceGeneratedDocumentClosed(documentId); + } + public void ChangeDocument(DocumentId documentId, SourceText text) { ChangeDocumentAsync(documentId, text); diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 4c8861098d704..2ec14d3ae9331 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -199,7 +199,8 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { - var document = project.GetTextDocument(documentId); + var document = project.GetTextDocument(documentId) + ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); if (document == null) { // it can happen with build synchronization since, in build case, diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index d73034d64dc17..570e2742d12ce 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -204,7 +204,8 @@ private static async Task> GetProjectStateDiagnos if (documentId != null) { // file doesn't exist in current solution - var document = project.Solution.GetDocument(documentId); + var document = project.Solution.GetDocument(documentId) + ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); if (document == null) { return ImmutableArray.Empty; diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index f775b51e9b8ea..8dc1e0fd033b3 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -535,7 +535,8 @@ private void RaiseProjectDiagnosticsCreated(Project project, StateSet stateSet, foreach (var documentId in newAnalysisResult.DocumentIds) { - var document = project.GetTextDocument(documentId); + var document = project.GetTextDocument(documentId) + ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); if (document == null) { // it can happen with build synchronization since, in build case, diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index fa298bc3000d1..e88d2d515f0e5 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -334,7 +334,7 @@ private async Task ProcessDocumentAsync(ImmutableArray ana { using (Logger.LogBlock(FunctionId.WorkCoordinator_ProcessDocumentAsync, w => w.ToString(), workItem, cancellationToken)) { - var textDocument = solution.GetTextDocument(documentId); + var textDocument = solution.GetTextDocument(documentId) ?? await solution.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); if (textDocument != null) { diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index c3ee256080e42..1fdfa9c7f61f3 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -76,6 +76,8 @@ public WorkCoordinator( _registration.Workspace.WorkspaceChanged += OnWorkspaceChanged; _registration.Workspace.DocumentOpened += OnDocumentOpened; _registration.Workspace.DocumentClosed += OnDocumentClosed; + _registration.Workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; + _registration.Workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } // subscribe to option changed event after all required fields are set @@ -107,6 +109,8 @@ public void Shutdown(bool blockingShutdown) _registration.Workspace.WorkspaceChanged -= OnWorkspaceChanged; _registration.Workspace.DocumentOpened -= OnDocumentOpened; _registration.Workspace.DocumentClosed -= OnDocumentClosed; + _registration.Workspace.SourceGeneratedDocumentOpened -= OnDocumentOpened; + _registration.Workspace.SourceGeneratedDocumentClosed -= OnDocumentClosed; // cancel any pending blocks _shutdownNotificationSource.Cancel(); @@ -151,12 +155,16 @@ private void OnOptionChanged(object? sender, OptionChangedEventArgs e) _registration.Workspace.WorkspaceChanged += OnWorkspaceChanged; _registration.Workspace.DocumentOpened += OnDocumentOpened; _registration.Workspace.DocumentClosed += OnDocumentClosed; + _registration.Workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; + _registration.Workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } else { _registration.Workspace.WorkspaceChanged -= OnWorkspaceChanged; _registration.Workspace.DocumentOpened -= OnDocumentOpened; _registration.Workspace.DocumentClosed -= OnDocumentClosed; + _registration.Workspace.SourceGeneratedDocumentOpened -= OnDocumentOpened; + _registration.Workspace.SourceGeneratedDocumentClosed -= OnDocumentClosed; } SolutionCrawlerLogger.LogOptionChanged(CorrelationId, value); diff --git a/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs b/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs index 304bd7e15bc61..abedeb179faa5 100644 --- a/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs +++ b/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs @@ -39,6 +39,8 @@ public BackgroundCompiler(Workspace workspace) _workspace.WorkspaceChanged += OnWorkspaceChanged; _workspace.DocumentOpened += OnDocumentOpened; _workspace.DocumentClosed += OnDocumentClosed; + _workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; + _workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } public void Dispose() @@ -49,6 +51,8 @@ public void Dispose() _workspace.DocumentClosed -= OnDocumentClosed; _workspace.DocumentOpened -= OnDocumentOpened; + _workspace.SourceGeneratedDocumentClosed -= OnDocumentClosed; + _workspace.SourceGeneratedDocumentOpened -= OnDocumentOpened; _workspace.WorkspaceChanged -= OnWorkspaceChanged; _workspace = null!; diff --git a/src/Features/Core/Portable/Workspace/BackgroundParser.cs b/src/Features/Core/Portable/Workspace/BackgroundParser.cs index f39ab27ce372d..1d1fff8dac186 100644 --- a/src/Features/Core/Portable/Workspace/BackgroundParser.cs +++ b/src/Features/Core/Portable/Workspace/BackgroundParser.cs @@ -49,6 +49,8 @@ public BackgroundParser(Workspace workspace) workspace.DocumentOpened += OnDocumentOpened; workspace.DocumentClosed += OnDocumentClosed; + workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; + workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } private void OnActiveDocumentChanged(object sender, DocumentId activeDocumentId) diff --git a/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs b/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs index 2d72c6c2f4110..5bc75f6b3cb00 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/Extensions.cs @@ -21,7 +21,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer internal static class Extensions { public static Uri GetURI(this TextDocument document) - => ProtocolConversions.GetUriFromFilePath(document.FilePath); + { + return document is SourceGeneratedDocument + ? ProtocolConversions.GetUriFromPartialFilePath(document.FilePath) + : ProtocolConversions.GetUriFromFilePath(document.FilePath); + } public static Uri? TryGetURI(this TextDocument document, RequestContext? context = null) => ProtocolConversions.TryGetUriFromFilePath(document.FilePath, context); diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 2f5615180fb92..fcdcdfc0012c4 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -149,6 +149,14 @@ public static Uri GetUriFromFilePath(string? filePath) return new Uri(filePath, UriKind.Absolute); } + public static Uri GetUriFromPartialFilePath(string? filePath) + { + if (filePath is null) + throw new ArgumentNullException(nameof(filePath)); + + return new Uri(filePath, UriKind.Relative); + } + public static Uri? TryGetUriFromFilePath(string? filePath, RequestContext? context = null) { if (Uri.TryCreate(filePath, UriKind.Absolute, out var uri)) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index f669bb8845ce8..cfccf46e5bbec 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -91,7 +91,7 @@ protected AbstractPullDiagnosticHandler( /// /// Returns all the documents that should be processed in the desired order to process them in. /// - protected abstract ImmutableArray GetOrderedDocuments(RequestContext context); + protected abstract ValueTask> GetOrderedDocumentsAsync(RequestContext context, CancellationToken cancellationToken); /// /// Creates the instance we'll report back to clients to let them know our @@ -135,7 +135,7 @@ protected AbstractPullDiagnosticHandler( // Next process each file in priority order. Determine if diagnostics are changed or unchanged since the // last time we notified the client. Report back either to the client so they can update accordingly. - var orderedDocuments = GetOrderedDocuments(context); + var orderedDocuments = await GetOrderedDocumentsAsync(context, cancellationToken).ConfigureAwait(false); context.TraceInformation($"Processing {orderedDocuments.Length} documents"); foreach (var document in orderedDocuments) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index 6f60253f40474..fd93d2625a37a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { @@ -57,9 +58,9 @@ protected override VSInternalDiagnosticReport CreateReport(TextDocumentIdentifie protected override DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData) => ConvertTags(diagnosticData, potentialDuplicate: false); - protected override ImmutableArray GetOrderedDocuments(RequestContext context) + protected override ValueTask> GetOrderedDocumentsAsync(RequestContext context, CancellationToken cancellationToken) { - return GetRequestedDocument(context); + return ValueTaskFactory.FromResult(GetRequestedDocument(context)); } protected override Task> GetDiagnosticsAsync( diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs index f43e56c991451..573af82e44d90 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServer.Protocol; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental; @@ -70,9 +71,9 @@ protected override Task> GetDiagnosticsAsync(Requ return _analyzerService.GetDiagnosticsForSpanAsync(document, range: null, cancellationToken: cancellationToken); } - protected override ImmutableArray GetOrderedDocuments(RequestContext context) + protected override ValueTask> GetOrderedDocumentsAsync(RequestContext context, CancellationToken cancellationToken) { - return DocumentPullDiagnosticHandler.GetRequestedDocument(context); + return ValueTaskFactory.FromResult(DocumentPullDiagnosticHandler.GetRequestedDocument(context)); } protected override ImmutableArray? GetPreviousResults(DocumentDiagnosticParams diagnosticsParams) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs index 48942ac61c635..6fb45523616ea 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs @@ -60,9 +60,9 @@ protected override async Task> GetDiagnosticsAsyn return diagnostics; } - protected override ImmutableArray GetOrderedDocuments(RequestContext context) + protected override ValueTask> GetOrderedDocumentsAsync(RequestContext context, CancellationToken cancellationToken) { - return WorkspacePullDiagnosticHandler.GetWorkspacePullDocuments(context); + return WorkspacePullDiagnosticHandler.GetWorkspacePullDocumentsAsync(context, cancellationToken); } protected override ImmutableArray? GetPreviousResults(WorkspaceDiagnosticParams diagnosticsParams) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index f3fd53c73f73e..356600dd1fbb6 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -49,9 +49,9 @@ protected override DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData) return ConvertTags(diagnosticData, potentialDuplicate: true); } - protected override ImmutableArray GetOrderedDocuments(RequestContext context) + protected override ValueTask> GetOrderedDocumentsAsync(RequestContext context, CancellationToken cancellationToken) { - return GetWorkspacePullDocuments(context); + return GetWorkspacePullDocumentsAsync(context, cancellationToken); } protected override Task> GetDiagnosticsAsync( @@ -68,7 +68,7 @@ protected override Task> GetDiagnosticsAsync( return progress.GetValues(); } - internal static ImmutableArray GetWorkspacePullDocuments(RequestContext context) + internal static async ValueTask> GetWorkspacePullDocumentsAsync(RequestContext context, CancellationToken cancellationToken) { Contract.ThrowIfNull(context.Solution); @@ -92,19 +92,19 @@ internal static ImmutableArray GetWorkspacePullDocuments(RequestContex var visibleDocuments = documentTrackingService.GetVisibleDocuments(solution); // Now, prioritize the projects related to the active/visible files. - AddDocumentsFromProject(activeDocument?.Project, context.SupportedLanguages, isOpen: true); + await AddDocumentsFromProjectAsync(activeDocument?.Project, context.SupportedLanguages, isOpen: true, cancellationToken).ConfigureAwait(false); foreach (var doc in visibleDocuments) - AddDocumentsFromProject(doc.Project, context.SupportedLanguages, isOpen: true); + await AddDocumentsFromProjectAsync(doc.Project, context.SupportedLanguages, isOpen: true, cancellationToken).ConfigureAwait(false); // finally, add the remainder of all documents. foreach (var project in solution.Projects) - AddDocumentsFromProject(project, context.SupportedLanguages, isOpen: false); + await AddDocumentsFromProjectAsync(project, context.SupportedLanguages, isOpen: false, cancellationToken).ConfigureAwait(false); // Ensure that we only process documents once. result.RemoveDuplicates(); return result.ToImmutable(); - void AddDocumentsFromProject(Project? project, ImmutableArray supportedLanguages, bool isOpen) + async Task AddDocumentsFromProjectAsync(Project? project, ImmutableArray supportedLanguages, bool isOpen, CancellationToken cancellationToken) { if (project == null) return; @@ -129,8 +129,15 @@ void AddDocumentsFromProject(Project? project, ImmutableArray supportedL } // Otherwise, if the user has an open file from this project, or FSA is on, then include all the - // documents from it. - foreach (var document in project.Documents) + // documents from it. If all features are enabled for source generated documents, make sure they are + // included as well. + var documents = project.Documents; + if (WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(project.Solution.Options)) + { + documents = documents.Concat(await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)); + } + + foreach (var document in documents) { // Only consider closed documents here (and only open ones in the DocumentPullDiagnosticHandler). // Each handler treats those as separate worlds that they are responsible for. diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 0dc05d7138dec..16b3144a4d62b 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -501,6 +501,28 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec Assert.True(results.All(r => r.TextDocument!.Uri.OriginalString == "C:\\C.cs")); } + [Theory, CombinatorialData] + public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiagnostics) + { + var markup1 = +@"class A {"; + var markup2 = ""; + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + markups: Array.Empty(), + sourceGeneratedMarkups: new[] { markup1, markup2 }, + BackgroundAnalysisScope.FullSolution); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); + + // Project.GetSourceGeneratedDocumentsAsync does not return documents in a deterministic order, so we sort + // the results here to ensure subsequent assertions are successful. + results = results.Sort((x, y) => x.Uri.ToString().CompareTo(y.Uri.ToString())); + + Assert.Equal(2, results.Length); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Empty(results[1].Diagnostics); + } + [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnostics) { @@ -1221,9 +1243,12 @@ private async Task CreateTestWorkspaceFromXmlAsync(string xmlMark return testLspServer; } - private async Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, BackgroundAnalysisScope scope, bool pullDiagnostics = true) + private Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, BackgroundAnalysisScope scope, bool pullDiagnostics = true) + => CreateTestWorkspaceWithDiagnosticsAsync(markups, Array.Empty(), scope, pullDiagnostics); + + private async Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, string[] sourceGeneratedMarkups, BackgroundAnalysisScope scope, bool pullDiagnostics = true) { - var testLspServer = await CreateTestLspServerAsync(markups); + var testLspServer = await CreateTestLspServerAsync(markups, sourceGeneratedMarkups); InitializeDiagnostics(scope, testLspServer.TestWorkspace, pullDiagnostics ? DiagnosticMode.Pull : DiagnosticMode.Push); return testLspServer; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs index bce9d51c8a109..91d9a8bba9acf 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs @@ -510,8 +510,8 @@ public async Task TestSeparateWorkspaceManagerPerServerAsync() var documentUri = testWorkspace.CurrentSolution.Projects.First().Documents.First().GetURI(); - using var testLspServerOne = new TestLspServer(testWorkspace); - using var testLspServerTwo = new TestLspServer(testWorkspace); + using var testLspServerOne = await TestLspServer.CreateAsync(testWorkspace); + using var testLspServerTwo = await TestLspServer.CreateAsync(testWorkspace); Assert.NotEqual(testLspServerOne.GetManager(), testLspServerTwo.GetManager()); diff --git a/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb b/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb index 1081fdc11d039..1955bed51b060 100644 --- a/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb +++ b/src/Features/Lsif/GeneratorTest/FoldingRangeTests.vb @@ -53,7 +53,7 @@ using System.Linq;|}", FoldingRangeKind.Imports)> ) - Dim annotatedLocations = AbstractLanguageServerProtocolTests.GetAnnotatedLocations(workspace, workspace.CurrentSolution) + Dim annotatedLocations = Await AbstractLanguageServerProtocolTests.GetAnnotatedLocationsAsync(workspace, workspace.CurrentSolution) Dim expectedRanges = annotatedLocations("foldingRange").Select(Function(location) CreateFoldingRange(rangeKind, location.Range)).OrderBy(Function(range) range.StartLine).ToArray() Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single() diff --git a/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb index dcb7bc67b4e56..6170f494ddc05 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb @@ -100,6 +100,26 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus End Using End Sub + + Public Sub TestSourceGeneratedDocumentOperation() + Using workspace = TestWorkspace.Create( + + + class C { } + + , composition:=EditorTestCompositions.EditorFeatures) + + Dim subjectDocument = workspace.Documents.Single() + Dim openDocument = subjectDocument.GetOpenTextContainer() + Dim document = Assert.IsType(Of SourceGeneratedDocument)(openDocument.GetOpenDocumentInCurrentContext()) + Dim documentServices = document.State.Services + Dim documentOperations = documentServices.GetService(Of IDocumentOperationService)() + + Assert.False(documentOperations.CanApplyChange) + Assert.True(documentOperations.SupportDiagnostics) + End Using + End Sub + Public Async Function TestExcerptService_SingleLine() As Task Using workspace = TestWorkspace.Create( diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index d03c93ec9569a..3cf9988168438 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -320,7 +320,8 @@ private static void VerifyDocumentMap(Project project, ImmutableDictionary ConvertLocationAsync( return Location.None; } - var textDocument = project.GetTextDocument(dataLocation.DocumentId); + var textDocument = project.GetTextDocument(dataLocation.DocumentId) + ?? await project.GetSourceGeneratedDocumentAsync(dataLocation.DocumentId, cancellationToken).ConfigureAwait(false); if (textDocument == null) { return Location.None; @@ -422,6 +423,11 @@ private static async Task> GetPragmaSuppressionAnalyz tasks.Add(AnalyzeDocumentAsync(suppressionAnalyzer, document, span: null, bag.Add)); } + foreach (var document in await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) + { + tasks.Add(AnalyzeDocumentAsync(suppressionAnalyzer, document, span: null, bag.Add)); + } + await Task.WhenAll(tasks).ConfigureAwait(false); return bag.ToImmutableArray(); } @@ -433,6 +439,11 @@ private static async Task> GetPragmaSuppressionAnalyz await AnalyzeDocumentAsync(suppressionAnalyzer, document, span: null, diagnosticsBuilder.Add).ConfigureAwait(false); } + foreach (var document in await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)) + { + await AnalyzeDocumentAsync(suppressionAnalyzer, document, span: null, diagnosticsBuilder.Add).ConfigureAwait(false); + } + return diagnosticsBuilder.ToImmutable(); } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs index 08a642b53c413..3f41bb353c65c 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ProjectExtensions.cs @@ -5,6 +5,8 @@ using System; using System.Diagnostics; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Shared.Extensions @@ -50,5 +52,16 @@ public static TextDocument GetRequiredTextDocument(this Project project, Documen return document; } + + public static async ValueTask GetRequiredSourceGeneratedDocumentAsync(this Project project, DocumentId documentId, CancellationToken cancellationToken) + { + var document = await project.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); + if (document is null) + { + throw new InvalidOperationException(WorkspaceExtensionsResources.The_solution_does_not_contain_the_specified_document); + } + + return document; + } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index 0c4eb80b47eb4..e88f3d105f482 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -36,7 +36,7 @@ public static SourceGeneratedDocumentState Create( documentIdentity, languageServices, solutionServices, - documentServiceProvider: null, + documentServiceProvider: SourceGeneratedTextDocumentServiceProvider.Instance, new DocumentInfo.DocumentAttributes( documentIdentity.DocumentId, name: documentIdentity.HintName, @@ -89,5 +89,41 @@ public SourceGeneratedDocumentState WithUpdatedGeneratedContent(SourceText sourc this.LanguageServices, this.solutionServices); } + + internal sealed class SourceGeneratedTextDocumentServiceProvider : IDocumentServiceProvider + { + public static readonly SourceGeneratedTextDocumentServiceProvider Instance = new(); + + private SourceGeneratedTextDocumentServiceProvider() + { + } + + public TService? GetService() + where TService : class, IDocumentService + { + // right now, it doesn't implement much services but we expect it to implements all + // document services in future so that we can remove all if branches in feature code + // but just delegate work to default document services. + if (SourceGeneratedDocumentOperationService.Instance is TService documentOperationService) + { + return documentOperationService; + } + + if (DocumentPropertiesService.Default is TService documentPropertiesService) + { + return documentPropertiesService; + } + + return null; + } + + private class SourceGeneratedDocumentOperationService : IDocumentOperationService + { + public static readonly SourceGeneratedDocumentOperationService Instance = new(); + + public bool CanApplyChange => false; + public bool SupportDiagnostics => true; + } + } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 3933a55127b98..e9a8aea765f03 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -428,6 +428,15 @@ internal void OnSourceGeneratedDocumentOpened( _openSourceGeneratedDocumentIdentities.Add(documentId, documentIdentity); UpdateCurrentContextMapping_NoLock(textContainer, documentId, isCurrentContext: true); + + // Fire and forget that the workspace is changing. + _ = RaiseSourceGeneratedDocumentOpenedAsync(this, CurrentSolution, documentId); + + static async Task RaiseSourceGeneratedDocumentOpenedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) + { + var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); + await workspace.RaiseSourceGeneratedDocumentOpenedEventAsync(document).ConfigureAwait(false); + } } this.RegisterText(textContainer); @@ -441,6 +450,15 @@ internal void OnSourceGeneratedDocumentClosed(DocumentId documentId) Contract.ThrowIfFalse(_openSourceGeneratedDocumentIdentities.Remove(documentId)); ClearOpenDocument(documentId); + + // Fire and forget that the workspace is changing. + _ = RaiseSourceGeneratedDocumentClosedAsync(this, CurrentSolution, documentId); + + static async Task RaiseSourceGeneratedDocumentClosedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) + { + var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); + await workspace.RaiseSourceGeneratedDocumentClosedEventAsync(document).ConfigureAwait(false); + } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs index 289af65d863b8..acc05b6b27912 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs @@ -22,6 +22,8 @@ public abstract partial class Workspace private const string WorkspaceFailedEventName = "WorkspaceFailed"; private const string DocumentOpenedEventName = "DocumentOpened"; private const string DocumentClosedEventName = "DocumentClosed"; + private const string SourceGeneratedDocumentOpenedEventName = "SourceGeneratedDocumentOpened"; + private const string SourceGeneratedDocumentClosedEventName = "SourceGeneratedDocumentClosed"; private const string DocumentActiveContextChangedName = "DocumentActiveContextChanged"; /// @@ -168,6 +170,72 @@ protected Task RaiseDocumentClosedEventAsync(Document document) } } + /// + /// An event that is fired when a documents is opened in the editor. + /// + internal event EventHandler SourceGeneratedDocumentOpened + { + add + { + _eventMap.AddEventHandler(SourceGeneratedDocumentOpenedEventName, value); + } + + remove + { + _eventMap.RemoveEventHandler(SourceGeneratedDocumentOpenedEventName, value); + } + } + + private protected Task RaiseSourceGeneratedDocumentOpenedEventAsync(Document document) + { + var ev = GetEventHandlers(SourceGeneratedDocumentOpenedEventName); + if (ev.HasHandlers && document != null) + { + return this.ScheduleTask(() => + { + var args = new DocumentEventArgs(document); + ev.RaiseEvent(handler => handler(this, args)); + }, SourceGeneratedDocumentOpenedEventName); + } + else + { + return Task.CompletedTask; + } + } + + /// + /// An event that is fired when a document is closed in the editor. + /// + internal event EventHandler SourceGeneratedDocumentClosed + { + add + { + _eventMap.AddEventHandler(SourceGeneratedDocumentClosedEventName, value); + } + + remove + { + _eventMap.RemoveEventHandler(SourceGeneratedDocumentClosedEventName, value); + } + } + + private protected Task RaiseSourceGeneratedDocumentClosedEventAsync(Document document) + { + var ev = GetEventHandlers(SourceGeneratedDocumentClosedEventName); + if (ev.HasHandlers && document != null) + { + return this.ScheduleTask(() => + { + var args = new DocumentEventArgs(document); + ev.RaiseEvent(handler => handler(this, args)); + }, SourceGeneratedDocumentClosedEventName); + } + else + { + return Task.CompletedTask; + } + } + /// /// An event that is fired when the active context document associated with a buffer /// changes. From bbdc65e8830fcced890ddcd1ed3d2b3ef63b9177 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 15 Dec 2021 19:28:59 -0800 Subject: [PATCH 007/187] Enable navigation to source generated diagnostics --- .../AbstractRoslynTableDataSource.cs | 4 +- .../AbstractTableDataSource.cs | 8 ++- .../AbstractTableEntriesSnapshot.cs | 35 +++++++++-- .../MiscellaneousDiagnosticListTable.cs | 10 ++- .../MiscellaneousTodoListTable.cs | 15 +++-- .../TableDataSource/TableEntriesFactory.cs | 16 ++--- ...DiagnosticListTable.LiveTableDataSource.cs | 10 +-- .../VisualStudioBaseTodoListTable.cs | 15 ++--- ...iagnosticListTable.BuildTableDataSource.cs | 47 ++++++++++---- .../VisualStudioDiagnosticListTable.cs | 22 ++++--- .../VisualStudioTodoListTable.cs | 15 +++-- .../DiagnosticTableDataSourceTests.vb | 61 +++++++++++++------ .../TodoListTableDataSourceTests.vb | 31 +++++++--- .../Impl/Client/RemoteDiagnosticListTable.cs | 4 +- .../Client/RemoteLanguageServiceWorkspace.cs | 2 +- 15 files changed, 203 insertions(+), 92 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractRoslynTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractRoslynTableDataSource.cs index b94af3b40e42e..934fc7922d176 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractRoslynTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractRoslynTableDataSource.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; namespace Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource @@ -15,7 +16,8 @@ internal abstract class AbstractRoslynTableDataSource : AbstractTa where TItem : TableItem where TData : notnull { - public AbstractRoslynTableDataSource(Workspace workspace) : base(workspace) + public AbstractRoslynTableDataSource(Workspace workspace, IThreadingContext threadingContext) + : base(workspace, threadingContext) => ConnectToSolutionCrawlerService(workspace); protected ImmutableArray GetDocumentsWithSameFilePath(Solution solution, DocumentId documentId) diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableDataSource.cs index dab7f4e9b7c19..7f1328a0ba083 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableDataSource.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Shell.TableManager; using Microsoft.VisualStudio.Text; @@ -34,7 +35,7 @@ internal abstract class AbstractTableDataSource : ITableDataSource private ImmutableArray _subscriptions; protected bool IsStable; - public AbstractTableDataSource(Workspace workspace) + public AbstractTableDataSource(Workspace workspace, IThreadingContext threadingContext) { _gate = new object(); _map = new Dictionary>(); @@ -43,6 +44,7 @@ public AbstractTableDataSource(Workspace workspace) _subscriptions = ImmutableArray.Empty; Workspace = workspace; + ThreadingContext = threadingContext; IsStable = true; } @@ -54,6 +56,8 @@ public AbstractTableDataSource(Workspace workspace) public abstract string Identifier { get; } + protected IThreadingContext ThreadingContext { get; } + public void RefreshAllFactories() { ImmutableArray snapshot; @@ -285,7 +289,7 @@ private void GetOrCreateFactory_NoLock(TData data, out TableEntriesFactory(this, source); + factory = new TableEntriesFactory(ThreadingContext, this, source); _map.Add(key, factory); newFactory = true; diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs index 4eb9c8682af8f..0ed45a9ffa317 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs @@ -4,8 +4,11 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Shell.TableManager; @@ -28,8 +31,9 @@ internal abstract class AbstractTableEntriesSnapshot : ITableEntriesSnaps private readonly ImmutableArray _items; private ImmutableArray _trackingPoints; - protected AbstractTableEntriesSnapshot(int version, ImmutableArray items, ImmutableArray trackingPoints) + protected AbstractTableEntriesSnapshot(IThreadingContext threadingContext, int version, ImmutableArray items, ImmutableArray trackingPoints) { + ThreadingContext = threadingContext; _version = version; _items = items; _trackingPoints = trackingPoints; @@ -54,6 +58,8 @@ public int Count } } + protected IThreadingContext ThreadingContext { get; } + public int IndexOf(int index, ITableEntriesSnapshot newerSnapshot) { var item = GetItem(index); @@ -169,14 +175,33 @@ protected static bool TryNavigateTo(Workspace workspace, DocumentId documentId, protected bool TryNavigateToItem(int index, bool previewTab, bool activate, CancellationToken cancellationToken) { var item = GetItem(index); - if (item is not { DocumentId: { } documentId }) - { + if (item is null) return false; - } var workspace = item.Workspace; var solution = workspace.CurrentSolution; - var document = solution.GetDocument(documentId); + var documentId = item.DocumentId; + if (documentId is null) + { + if (item is { ProjectId: { } projectId } + && solution.GetProject(projectId) is { } project) + { + // We couldn't find a document ID when the item was created, so it may be a source generator + // output. + var documents = ThreadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentsAsync(cancellationToken).AsTask()); + var projectDirectory = Path.GetDirectoryName(project.FilePath); + documentId = documents.FirstOrDefault(document => Path.Combine(projectDirectory, document.FilePath) == item.GetOriginalFilePath())?.Id; + if (documentId is null) + return false; + } + else + { + return false; + } + } + + var document = solution.GetDocument(documentId) + ?? solution.GetProject(documentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); if (document == null) { return false; diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs index b796f7dce35d3..d26be03769036 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousDiagnosticListTable.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; @@ -19,30 +20,33 @@ internal sealed class MiscellaneousDiagnosticListTableWorkspaceEventListener : I { internal const string IdentifierString = nameof(MiscellaneousDiagnosticListTable); + private readonly IThreadingContext _threadingContext; private readonly ITableManagerProvider _tableManagerProvider; private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public MiscellaneousDiagnosticListTableWorkspaceEventListener( + IThreadingContext threadingContext, ITableManagerProvider tableManagerProvider, IGlobalOptionService globalOptions) { + _threadingContext = threadingContext; _tableManagerProvider = tableManagerProvider; _globalOptions = globalOptions; } public void StartListening(Workspace workspace, IDiagnosticService diagnosticService) - => _ = new MiscellaneousDiagnosticListTable(workspace, _globalOptions, diagnosticService, _tableManagerProvider); + => _ = new MiscellaneousDiagnosticListTable(workspace, _threadingContext, _globalOptions, diagnosticService, _tableManagerProvider); private sealed class MiscellaneousDiagnosticListTable : VisualStudioBaseDiagnosticListTable { private readonly LiveTableDataSource _source; - public MiscellaneousDiagnosticListTable(Workspace workspace, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider) + public MiscellaneousDiagnosticListTable(Workspace workspace, IThreadingContext threadingContext, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider) : base(workspace, provider) { - _source = new LiveTableDataSource(workspace, globalOptions, diagnosticService, IdentifierString); + _source = new LiveTableDataSource(workspace, threadingContext, globalOptions, diagnosticService, IdentifierString); AddInitialTableSource(workspace.CurrentSolution, _source); ConnectWorkspaceEvents(); diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs index 0b312669e7c49..e2ffb649d2bd1 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/MiscellaneousTodoListTable.cs @@ -6,6 +6,7 @@ using System.Composition; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Shell.TableManager; @@ -17,20 +18,24 @@ internal sealed class MiscellaneousTodoListTableWorkspaceEventListener : IEventL { internal const string IdentifierString = nameof(MiscellaneousTodoListTable); + private readonly IThreadingContext _threadingContext; private readonly ITableManagerProvider _tableManagerProvider; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public MiscellaneousTodoListTableWorkspaceEventListener(ITableManagerProvider tableManagerProvider) - => _tableManagerProvider = tableManagerProvider; + public MiscellaneousTodoListTableWorkspaceEventListener(IThreadingContext threadingContext, ITableManagerProvider tableManagerProvider) + { + _threadingContext = threadingContext; + _tableManagerProvider = tableManagerProvider; + } public void StartListening(Workspace workspace, ITodoListProvider service) - => _ = new MiscellaneousTodoListTable(workspace, service, _tableManagerProvider); + => _ = new MiscellaneousTodoListTable(workspace, _threadingContext, service, _tableManagerProvider); private sealed class MiscellaneousTodoListTable : VisualStudioBaseTodoListTable { - public MiscellaneousTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, ITableManagerProvider provider) - : base(workspace, todoListProvider, IdentifierString, provider) + public MiscellaneousTodoListTable(Workspace workspace, IThreadingContext threadingContext, ITodoListProvider todoListProvider, ITableManagerProvider provider) + : base(workspace, threadingContext, todoListProvider, IdentifierString, provider) { ConnectWorkspaceEvents(); } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/TableEntriesFactory.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/TableEntriesFactory.cs index 77313c0a9bf29..1d538446a4c20 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/TableEntriesFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/TableEntriesFactory.cs @@ -8,7 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; -using EnvDTE; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Shell.TableManager; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -27,10 +27,10 @@ internal class TableEntriesFactory : ITableEntriesSnapshotFactory private int _lastVersion = 0; - public TableEntriesFactory(AbstractTableDataSource tableSource, AbstractTableEntriesSource entriesSource) + public TableEntriesFactory(IThreadingContext threadingContext, AbstractTableDataSource tableSource, AbstractTableEntriesSource entriesSource) { _tableSource = tableSource; - _entriesSources = new AggregatedEntriesSource(_tableSource, entriesSource); + _entriesSources = new AggregatedEntriesSource(threadingContext, _tableSource, entriesSource); } public int CurrentVersionNumber @@ -130,11 +130,13 @@ private ITableEntriesSnapshot CreateSnapshot(int version, ImmutableArray private class AggregatedEntriesSource { + private readonly IThreadingContext _threadingContext; private readonly EntriesSourceCollections _sources; private readonly AbstractTableDataSource _tableSource; - public AggregatedEntriesSource(AbstractTableDataSource tableSource, AbstractTableEntriesSource primary) + public AggregatedEntriesSource(IThreadingContext threadingContext, AbstractTableDataSource tableSource, AbstractTableEntriesSource primary) { + _threadingContext = threadingContext; _tableSource = tableSource; _sources = new EntriesSourceCollections(primary); } @@ -187,7 +189,7 @@ public AbstractTableEntriesSnapshot CreateSnapshot(int version, Immutable var source = _sources.GetSources().FirstOrDefault(); if (source == null) { - return new EmptySnapshot(version); + return new EmptySnapshot(_threadingContext, version); } return _tableSource.CreateSnapshot(source, version, items, trackingPoints); @@ -195,8 +197,8 @@ public AbstractTableEntriesSnapshot CreateSnapshot(int version, Immutable private class EmptySnapshot : AbstractTableEntriesSnapshot { - public EmptySnapshot(int version) - : base(version, ImmutableArray.Empty, ImmutableArray.Empty) + public EmptySnapshot(IThreadingContext threadingContext, int version) + : base(threadingContext, version, ImmutableArray.Empty, ImmutableArray.Empty) { } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs index 64c938f530f67..d47deb690dc3f 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseDiagnosticListTable.LiveTableDataSource.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.Common; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; @@ -55,8 +56,8 @@ protected class LiveTableDataSource : AbstractRoslynTableDataSource private bool _isBuildRunning; - public LiveTableDataSource(Workspace workspace, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, string identifier, ExternalErrorDiagnosticUpdateSource? buildUpdateSource = null) - : base(workspace) + public LiveTableDataSource(Workspace workspace, IThreadingContext threadingContext, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, string identifier, ExternalErrorDiagnosticUpdateSource? buildUpdateSource = null) + : base(workspace, threadingContext) { _workspace = workspace; _globalOptions = globalOptions; @@ -106,7 +107,7 @@ public override AbstractTableEntriesSnapshot CreateSnapshot ImmutableArray trackingPoints) { var diagnosticSource = (DiagnosticTableEntriesSource)source; - var snapshot = new TableEntriesSnapshot(diagnosticSource, version, items, trackingPoints); + var snapshot = new TableEntriesSnapshot(ThreadingContext, diagnosticSource, version, items, trackingPoints); if (diagnosticSource.SupportSpanTracking && !trackingPoints.IsDefaultOrEmpty) { @@ -316,11 +317,12 @@ private class TableEntriesSnapshot : AbstractTableEntriesSnapshot items, ImmutableArray trackingPoints) - : base(version, items, trackingPoints) + : base(threadingContext, version, items, trackingPoints) { _source = source; } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseTodoListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseTodoListTable.cs index f19633906b706..8cf2d4cf86d52 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseTodoListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioBaseTodoListTable.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.Implementation.Diagnostics; using Microsoft.VisualStudio.Shell.Interop; @@ -26,10 +27,10 @@ internal class VisualStudioBaseTodoListTable : AbstractTable { private readonly TableDataSource _source; - protected VisualStudioBaseTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, string identifier, ITableManagerProvider provider) + protected VisualStudioBaseTodoListTable(Workspace workspace, IThreadingContext threadingContext, ITodoListProvider todoListProvider, string identifier, ITableManagerProvider provider) : base(workspace, provider, StandardTables.TasksTable) { - _source = new TableDataSource(workspace, todoListProvider, identifier); + _source = new TableDataSource(workspace, threadingContext, todoListProvider, identifier); AddInitialTableSource(workspace.CurrentSolution, _source); } @@ -70,8 +71,8 @@ private class TableDataSource : AbstractRoslynTableDataSource CreateSnapshot(AbstractTableEntriesSource source, int version, ImmutableArray items, ImmutableArray trackingPoints) - => new TableEntriesSnapshot(version, items, trackingPoints); + => new TableEntriesSnapshot(ThreadingContext, version, items, trackingPoints); public override IEqualityComparer GroupingComparer => TodoTableItem.GroupingComparer.Instance; @@ -192,8 +193,8 @@ public override ImmutableArray GetTrackingPoints(ImmutableArray< private sealed class TableEntriesSnapshot : AbstractTableEntriesSnapshot { - public TableEntriesSnapshot(int version, ImmutableArray items, ImmutableArray trackingPoints) - : base(version, items, trackingPoints) + public TableEntriesSnapshot(IThreadingContext threadingContext, int version, ImmutableArray items, ImmutableArray trackingPoints) + : base(threadingContext, version, items, trackingPoints) { } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs index a2d8af8bba50d..14fae42696ea7 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs @@ -6,11 +6,13 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.Shell.TableManager; using Microsoft.VisualStudio.Text; @@ -33,8 +35,8 @@ private class BuildTableDataSource : AbstractTableDataSource CreateTableEntri public override AbstractTableEntriesSnapshot CreateSnapshot(AbstractTableEntriesSource source, int version, ImmutableArray items, ImmutableArray trackingPoints) { // Build doesn't support tracking point. - return new TableEntriesSnapshot((DiagnosticTableEntriesSource)source, version, items); + return new TableEntriesSnapshot(ThreadingContext, (DiagnosticTableEntriesSource)source, version, items); } public override IEqualityComparer GroupingComparer @@ -124,8 +126,9 @@ private class TableEntriesSnapshot : AbstractTableEntriesSnapshot items) - : base(version, items, ImmutableArray.Empty) + : base(threadingContext, version, items, ImmutableArray.Empty) { _source = source; } @@ -212,19 +215,24 @@ public override bool TryGetValue(int index, string columnName, [NotNullWhen(true public override bool TryNavigateTo(int index, bool previewTab, bool activate, CancellationToken cancellationToken) { var item = GetItem(index); - if (item?.DocumentId == null) - { + if (item is null) + return false; + + var documentId = GetProperDocumentId(ThreadingContext, item, cancellationToken); + if (documentId is null) return false; - } - var documentId = GetProperDocumentId(item); var solution = item.Workspace.CurrentSolution; + if (!solution.ContainsDocument(documentId) + && solution.GetProject(documentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId) is null) + { + return false; + } - return solution.ContainsDocument(documentId) && - TryNavigateTo(item.Workspace, documentId, item.GetOriginalPosition(), previewTab, activate, cancellationToken); + return TryNavigateTo(item.Workspace, documentId, item.GetOriginalPosition(), previewTab, activate, cancellationToken); } - private DocumentId? GetProperDocumentId(DiagnosticTableItem item) + private static DocumentId? GetProperDocumentId(IThreadingContext threadingContext, DiagnosticTableItem item, CancellationToken cancellationToken) { var documentId = item.DocumentId; var projectId = item.ProjectId; @@ -236,6 +244,23 @@ public override bool TryNavigateTo(int index, bool previewTab, bool activate, Ca return documentId; } + if (documentId is not null + && solution.GetProject(projectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId) is not null) + { + return documentId; + } + + if (documentId is null && solution.GetProject(projectId) is { } project) + { + // We couldn't find a document ID when the item was created, so it may be a source generator + // output. + var documents = threadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentsAsync(cancellationToken).AsTask()); + var projectDirectory = Path.GetDirectoryName(project.FilePath); + documentId = documents.FirstOrDefault(document => Path.Combine(projectDirectory, document.FilePath) == item.GetOriginalFilePath())?.Id; + if (documentId is not null) + return documentId; + } + // okay, documentId no longer exist in current solution, find it by file path. var filePath = item.GetOriginalFilePath(); if (string.IsNullOrWhiteSpace(filePath)) diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs index 283acb321c4f9..34cf205ce7e21 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.cs @@ -65,6 +65,7 @@ public void StartListening(Workspace workspace, IDiagnosticService diagnosticSer var table = new VisualStudioDiagnosticListTable( (VisualStudioWorkspaceImpl)workspace, + _threadingContext, _globalOptions, diagnosticService, _tableManagerProvider, @@ -80,6 +81,7 @@ internal partial class VisualStudioDiagnosticListTable : VisualStudioBaseDiagnos public VisualStudioDiagnosticListTable( VisualStudioWorkspaceImpl workspace, + IThreadingContext threadingContext, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider, @@ -88,8 +90,8 @@ public VisualStudioDiagnosticListTable( { _errorList = errorList; - _liveTableSource = new LiveTableDataSource(workspace, globalOptions, diagnosticService, IdentifierString, workspace.ExternalErrorDiagnosticUpdateSource); - _buildTableSource = new BuildTableDataSource(workspace, workspace.ExternalErrorDiagnosticUpdateSource); + _liveTableSource = new LiveTableDataSource(workspace, threadingContext, globalOptions, diagnosticService, IdentifierString, workspace.ExternalErrorDiagnosticUpdateSource); + _buildTableSource = new BuildTableDataSource(workspace, threadingContext, workspace.ExternalErrorDiagnosticUpdateSource); AddInitialTableSource(Workspace.CurrentSolution, GetCurrentDataSource()); ConnectWorkspaceEvents(); @@ -108,25 +110,25 @@ private ITableDataSource GetCurrentDataSource() } /// this is for test only - private VisualStudioDiagnosticListTable(Workspace workspace, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider) + private VisualStudioDiagnosticListTable(Workspace workspace, IThreadingContext threadingContext, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider) : base(workspace, provider) { _liveTableSource = null!; _buildTableSource = null!; _errorList = null!; - AddInitialTableSource(workspace.CurrentSolution, new LiveTableDataSource(workspace, globalOptions, diagnosticService, IdentifierString)); + AddInitialTableSource(workspace.CurrentSolution, new LiveTableDataSource(workspace, threadingContext, globalOptions, diagnosticService, IdentifierString)); } /// this is for test only - private VisualStudioDiagnosticListTable(Workspace workspace, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) + private VisualStudioDiagnosticListTable(Workspace workspace, IThreadingContext threadingContext, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) : base(workspace, provider) { _liveTableSource = null!; _buildTableSource = null!; _errorList = null!; - AddInitialTableSource(workspace.CurrentSolution, new BuildTableDataSource(workspace, errorSource)); + AddInitialTableSource(workspace.CurrentSolution, new BuildTableDataSource(workspace, threadingContext, errorSource)); } protected override void AddTableSourceIfNecessary(Solution solution) @@ -188,14 +190,14 @@ private void OnErrorListPropertyChanged(object sender, PropertyChangedEventArgs internal static class TestAccessor { - public static VisualStudioDiagnosticListTable Create(Workspace workspace, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider) + public static VisualStudioDiagnosticListTable Create(Workspace workspace, IThreadingContext threadingContext, IGlobalOptionService globalOptions, IDiagnosticService diagnosticService, ITableManagerProvider provider) { - return new VisualStudioDiagnosticListTable(workspace, globalOptions, diagnosticService, provider); + return new VisualStudioDiagnosticListTable(workspace, threadingContext, globalOptions, diagnosticService, provider); } - public static VisualStudioDiagnosticListTable Create(Workspace workspace, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) + public static VisualStudioDiagnosticListTable Create(Workspace workspace, IThreadingContext threadingContext, ExternalErrorDiagnosticUpdateSource errorSource, ITableManagerProvider provider) { - return new VisualStudioDiagnosticListTable(workspace, errorSource, provider); + return new VisualStudioDiagnosticListTable(workspace, threadingContext, errorSource, provider); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs index c43180aca49ba..b7779bdbd22ec 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioTodoListTable.cs @@ -6,6 +6,7 @@ using System.Composition; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Shell.TableManager; @@ -17,21 +18,25 @@ internal class VisualStudioTodoListTableWorkspaceEventListener : IEventListener< { internal const string IdentifierString = nameof(VisualStudioTodoListTable); + private readonly IThreadingContext _threadingContext; private readonly ITableManagerProvider _tableManagerProvider; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VisualStudioTodoListTableWorkspaceEventListener(ITableManagerProvider tableManagerProvider) - => _tableManagerProvider = tableManagerProvider; + public VisualStudioTodoListTableWorkspaceEventListener(IThreadingContext threadingContext, ITableManagerProvider tableManagerProvider) + { + _threadingContext = threadingContext; + _tableManagerProvider = tableManagerProvider; + } public void StartListening(Workspace workspace, ITodoListProvider service) - => _ = new VisualStudioTodoListTable(workspace, service, _tableManagerProvider); + => _ = new VisualStudioTodoListTable(workspace, _threadingContext, service, _tableManagerProvider); internal class VisualStudioTodoListTable : VisualStudioBaseTodoListTable { // internal for testing - internal VisualStudioTodoListTable(Workspace workspace, ITodoListProvider todoListProvider, ITableManagerProvider provider) - : base(workspace, todoListProvider, IdentifierString, provider) + internal VisualStudioTodoListTable(Workspace workspace, IThreadingContext threadingContext, ITodoListProvider todoListProvider, ITableManagerProvider provider) + : base(workspace, threadingContext, todoListProvider, IdentifierString, provider) { ConnectWorkspaceEvents(); } diff --git a/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb index 10fc028f97f36..23db0c525fd80 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/DiagnosticTableDataSourceTests.vb @@ -9,6 +9,7 @@ Imports System.Windows.Controls Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.Shared +Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.TestHooks @@ -28,11 +29,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestCreation() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim provider = New TestDiagnosticService() Dim tableManagerProvider = New TestTableManagerProvider() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) Assert.Equal(manager.Identifier, StandardTables.ErrorsTable) @@ -61,12 +63,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestInitialEntries() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim provider = New TestDiagnosticService(CreateItem(documentId)) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -81,12 +84,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestEntryChanged() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim provider = New TestDiagnosticService() Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) Dim source = DirectCast(manager.Sources.First(), AbstractRoslynTableDataSource(Of DiagnosticTableItem, DiagnosticsUpdatedArgs)) @@ -107,6 +111,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() @@ -114,7 +119,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim provider = New TestDiagnosticService(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -149,6 +154,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestSnapshotEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() @@ -156,7 +162,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim provider = New TestDiagnosticService(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -198,6 +204,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestInvalidEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() @@ -205,7 +212,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim provider = New TestDiagnosticService(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -229,6 +236,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestNoHiddenEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() @@ -238,7 +246,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -257,6 +265,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestProjectEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim projectId = workspace.CurrentSolution.Projects.First().Id @@ -265,7 +274,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -293,8 +302,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() + Dim threadingContext = workspace1.GetService(Of IThreadingContext)() Dim globalOptions = workspace1.GetService(Of IGlobalOptionService)() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace1, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace1, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace1) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -320,6 +330,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestDetailExpander() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -329,7 +340,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -358,6 +369,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestHyperLink() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -367,7 +379,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -391,6 +403,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestBingHyperLink() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -400,7 +413,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -424,6 +437,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestHelpLink() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -433,7 +447,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -454,6 +468,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestHelpKeyword() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -463,7 +478,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -484,6 +499,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestBingHelpLink() Using workspace = TestWorkspace.CreateVisualBasic(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -493,7 +509,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -531,6 +547,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestErrorSource() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -540,7 +557,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -561,6 +578,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestWorkspaceDiagnostic() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -570,7 +588,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -592,6 +610,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestProjectDiagnostic() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim projectId = documentId.ProjectId @@ -601,7 +620,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, provider, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, provider, tableManagerProvider) provider.RaiseDiagnosticsUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -634,6 +653,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Using workspace = TestWorkspace.Create(markup) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim analyzerReference = New TestAnalyzerReferenceByLanguage(DiagnosticExtensions.GetCompilerDiagnosticAnalyzersMap()) @@ -643,7 +663,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim service = Assert.IsType(Of DiagnosticService)(workspace.ExportProvider.GetExportedValue(Of IDiagnosticService)()) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, globalOptions, service, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, globalOptions, service, tableManagerProvider) RunCompilerAnalyzer(workspace) @@ -686,6 +706,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Using workspace = TestWorkspace.Create(markup) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim globalOptions = workspace.GetService(Of IGlobalOptionService)() Dim listenerProvider = workspace.GetService(Of IAsynchronousOperationListenerProvider) Dim listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService) @@ -695,7 +716,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Using updateSource = New ExternalErrorDiagnosticUpdateSource(workspace, analyzerService, listener, CancellationToken.None) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, updateSource, tableManagerProvider) + Dim table = VisualStudioDiagnosticListTableWorkspaceEventListener.VisualStudioDiagnosticListTable.TestAccessor.Create(workspace, threadingContext, updateSource, tableManagerProvider) Dim document1 = workspace.CurrentSolution.Projects.First(Function(p) p.Name = "Proj1").Documents.First() Dim document2 = workspace.CurrentSolution.Projects.First(Function(p) p.Name = "Proj2").Documents.First() diff --git a/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb b/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb index 8930912141cc7..f449a94628847 100644 --- a/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb +++ b/src/VisualStudio/Core/Test/Diagnostics/TodoListTableDataSourceTests.vb @@ -7,6 +7,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Common Imports Microsoft.CodeAnalysis.Editor +Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.TodoComments @@ -21,10 +22,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestCreation() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim provider = New TestTodoListProvider() Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) Assert.Equal(manager.Identifier, StandardTables.TasksTable) @@ -53,11 +55,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestInitialEntries() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim provider = New TestTodoListProvider(CreateItem(documentId)) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -72,11 +75,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestEntryChanged() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim provider = New TestTodoListProvider() Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) Dim source = DirectCast(manager.Sources.First(), AbstractRoslynTableDataSource(Of TodoTableItem, TodoItemsUpdatedArgs)) @@ -97,13 +101,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim item = CreateItem(documentId) Dim provider = New TestTodoListProvider(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -138,13 +143,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestSnapshotEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim item = CreateItem(documentId) Dim provider = New TestTodoListProvider(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -186,13 +192,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestSnapshotTranslateTo() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim item = CreateItem(documentId) Dim provider = New TestTodoListProvider(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -216,13 +223,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestSnapshotTranslateTo2() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim item = CreateItem(documentId) Dim provider = New TestTodoListProvider(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -250,13 +258,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestSnapshotTranslateTo3() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim item = CreateItem(documentId) Dim provider = New TestTodoListProvider(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -284,13 +293,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Public Sub TestInvalidEntry() Using workspace = TestWorkspace.CreateCSharp(String.Empty) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim documentId = workspace.CurrentSolution.Projects.First().DocumentIds.First() Dim item = CreateItem(documentId) Dim provider = New TestTodoListProvider(item) Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.RaiseTodoListUpdated(workspace) Dim manager = DirectCast(table.TableManager, TestTableManagerProvider.TestTableManager) @@ -321,6 +331,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Using workspace = TestWorkspace.Create(markup) + Dim threadingContext = workspace.GetService(Of IThreadingContext)() Dim projects = workspace.CurrentSolution.Projects.ToArray() Dim item1 = CreateItem(projects(0).DocumentIds.First()) @@ -329,7 +340,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Diagnostics Dim provider = New TestTodoListProvider() Dim tableManagerProvider = New TestTableManagerProvider() - Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, provider, tableManagerProvider) + Dim table = New VisualStudioTodoListTableWorkspaceEventListener.VisualStudioTodoListTable(workspace, threadingContext, provider, tableManagerProvider) provider.Items = New TodoCommentData() {item1, item2} provider.RaiseTodoListUpdated(workspace) diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs index 6d293f0b6dfce..bbe769e436b28 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteDiagnosticListTable.cs @@ -9,6 +9,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; using Microsoft.VisualStudio.Shell; @@ -31,6 +32,7 @@ internal class RemoteDiagnosticListTable : VisualStudioBaseDiagnosticListTable [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Incorrectly used in production code: https://github.com/dotnet/roslyn/issues/42839")] public RemoteDiagnosticListTable( + IThreadingContext threadingContext, SVsServiceProvider serviceProvider, RemoteLanguageServiceWorkspace workspace, IGlobalOptionService globalOptions, @@ -38,7 +40,7 @@ public RemoteDiagnosticListTable( ITableManagerProvider provider) : base(workspace, provider) { - _source = new LiveTableDataSource(workspace, globalOptions, diagnosticService, IdentifierString); + _source = new LiveTableDataSource(workspace, threadingContext, globalOptions, diagnosticService, IdentifierString); AddInitialTableSource(workspace.CurrentSolution, _source); ConnectWorkspaceEvents(); diff --git a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs index 48c14d0159ce6..0b8bd200f5741 100644 --- a/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs +++ b/src/VisualStudio/LiveShare/Impl/Client/RemoteLanguageServiceWorkspace.cs @@ -90,7 +90,7 @@ public RemoteLanguageServiceWorkspace( { _serviceProvider = serviceProvider; - _remoteDiagnosticListTable = new RemoteDiagnosticListTable(serviceProvider, this, globalOptions, diagnosticService, tableManagerProvider); + _remoteDiagnosticListTable = new RemoteDiagnosticListTable(threadingContext, serviceProvider, this, globalOptions, diagnosticService, tableManagerProvider); var runningDocumentTable = (IVsRunningDocumentTable)serviceProvider.GetService(typeof(SVsRunningDocumentTable)); _runningDocumentTableEventTracker = new RunningDocumentTableEventTracker(threadingContext, editorAdaptersFactoryService, runningDocumentTable, this); From 8a47d54e12f93c6a7876c6be2bd9d79c1c9d0f62 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 17 Dec 2021 10:30:40 -0800 Subject: [PATCH 008/187] Avoid showing Generate Local and Generate Parameter in source generated documents --- .../AbstractGenerateVariableService.State.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs index 0b4c1eb7bf22e..e08ea91095f4b 100644 --- a/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Features/Core/Portable/GenerateMember/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -52,6 +52,7 @@ private partial class State public bool IsInOutContext { get; private set; } public bool IsInMemberContext { get; private set; } + public bool IsInSourceGeneratedDocument { get; private set; } public bool IsInExecutableBlock { get; private set; } public bool IsInConditionalAccessExpression { get; private set; } @@ -143,7 +144,7 @@ internal bool CanGeneratePropertyOrField() internal bool CanGenerateLocal() { // !this.IsInMemberContext prevents us offering this fix for `x.goo` where `goo` does not exist - return !IsInMemberContext && IsInExecutableBlock; + return !IsInMemberContext && IsInExecutableBlock && !IsInSourceGeneratedDocument; } internal bool CanGenerateParameter() @@ -151,7 +152,7 @@ internal bool CanGenerateParameter() // !this.IsInMemberContext prevents us offering this fix for `x.goo` where `goo` does not exist // Workaround: The compiler returns IsImplicitlyDeclared = false for
$. return ContainingMethod is { IsImplicitlyDeclared: false, Name: not WellKnownMemberNames.TopLevelStatementsEntryPointMethodName } - && !IsInMemberContext && !IsConstant; + && !IsInMemberContext && !IsConstant && !IsInSourceGeneratedDocument; } private bool TryInitializeExplicitInterface( @@ -278,6 +279,7 @@ private bool TryInitializeSimpleName( IsInMemberContext = simpleName != SimpleNameOrMemberAccessExpressionOpt || syntaxFacts.IsMemberInitializerNamedAssignmentIdentifier(SimpleNameOrMemberAccessExpressionOpt); + IsInSourceGeneratedDocument = semanticDocument.Document is SourceGeneratedDocument; ContainingMethod = FindContainingMethodSymbol(IdentifierToken.SpanStart, semanticModel, cancellationToken); From 4faacef6ed1bfbfd0a6590d1969d0e73b3725812 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 17 Dec 2021 10:31:01 -0800 Subject: [PATCH 009/187] Avoid showing Spell Check fixes in read-only documents --- .../Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs b/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs index 99c77bc334599..f28cc52f48d58 100644 --- a/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs +++ b/src/Features/Core/Portable/SpellCheck/AbstractSpellCheckCodeFixProvider.cs @@ -12,9 +12,9 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SpellCheck @@ -39,6 +39,9 @@ public override FixAllProvider GetFixAllProvider() public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; + if (!document.CanApplyChange()) + return; + var span = context.Span; var cancellationToken = context.CancellationToken; From 8926634087070da28092ece62e7e2a9802bb868e Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 17 Dec 2021 10:37:12 -0800 Subject: [PATCH 010/187] Update InlineDiagnosticsTaggerProvider to work for source generated documents --- .../InlineDiagnosticsTaggerProviderTests.cs | 22 +++++++++++++++++++ .../InlineDiagnosticsTaggerProvider.cs | 8 +++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs index fcbb5b673911a..e9926c48a4295 100644 --- a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using System.Xml.Linq; using Microsoft.CodeAnalysis.Editor.InlineDiagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; @@ -32,12 +33,33 @@ public async Task ErrorTagGeneratedForError() Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); } + [WpfFact, Trait(Traits.Feature, Traits.Features.ErrorSquiggles)] + public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument() + { + var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {"); + Assert.Equal(1, spans.Count()); + + var firstSpan = spans.First(); + Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); + } + private static async Task>> GetTagSpansAsync(string content) { using var workspace = TestWorkspace.CreateCSharp(content, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); return await GetTagSpansAsync(workspace); } + private static async Task>> GetTagSpansInSourceGeneratedDocumentAsync(string content) + { + var workspaceElement = $@" + + {new XText(content)} + +"; + using var workspace = TestWorkspace.Create(workspaceElement, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); + return await GetTagSpansAsync(workspace); + } + private static async Task>> GetTagSpansAsync(TestWorkspace workspace) { workspace.ApplyOptions(new[] { KeyValuePairUtil.Create(new OptionKey2(InlineDiagnosticsOptions.EnableInlineDiagnostics, LanguageNames.CSharp), (object)true) }); diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs index 8f71dc18992b8..4d3383525fb74 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs @@ -79,18 +79,18 @@ diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error && return null; } - if (diagnostic.DocumentId is null) + if (diagnostic.DocumentId is not { ProjectId: var projectId }) { return null; } - var document = workspace.CurrentSolution.GetDocument(diagnostic.DocumentId); - if (document is null) + var project = workspace.CurrentSolution.GetProject(projectId); + if (project is null) { return null; } - var locationOption = GlobalOptions.GetOption(InlineDiagnosticsOptions.Location, document.Project.Language); + var locationOption = GlobalOptions.GetOption(InlineDiagnosticsOptions.Location, project.Language); var navigateService = workspace.Services.GetRequiredService(); return new InlineDiagnosticsTag(errorType, diagnostic, _editorFormatMap, _classificationFormatMapService, _classificationTypeRegistryService, locationOption, navigateService); From b3b8502450834f9ceb204a13bfba5ae4ed6e6200 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 17 Dec 2021 12:22:49 -0800 Subject: [PATCH 011/187] Run solution crawler on source generated documents that change --- .../AbstractLanguageServerProtocolTests.cs | 8 +++- .../SolutionCrawler/WorkCoordinator.cs | 42 +++++++++++++++++++ .../Options/AdvancedOptionPageControl.xaml.cs | 4 +- .../Workspace/SourceGeneratedFileManager.cs | 29 +------------ .../VisualStudioWorkspace_OutOfProc.cs | 4 +- .../Options/AdvancedOptionPageControl.xaml.vb | 4 +- .../WorkspaceConfigurationOptions.cs | 20 ++++++++- 7 files changed, 74 insertions(+), 37 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 2c46002bc4acf..301571a71db5e 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -299,10 +299,14 @@ protected Task CreateTestLspServerAsync(string[] markups, string[ private Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups, string languageName) { + var exportProvider = Composition.ExportProviderFactory.CreateExportProvider(); + var globalOptions = exportProvider.GetExportedValue(); + globalOptions.SetGlobalOption(new OptionKey(WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace), true); + var workspace = languageName switch { - LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, sourceGeneratedMarkups, composition: Composition), - LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, sourceGeneratedMarkups, composition: Composition), + LanguageNames.CSharp => TestWorkspace.CreateCSharp(markups, sourceGeneratedMarkups, exportProvider: exportProvider), + LanguageNames.VisualBasic => TestWorkspace.CreateVisualBasic(markups, sourceGeneratedMarkups, exportProvider: exportProvider), _ => throw new ArgumentException($"language name {languageName} is not valid for a test workspace"), }; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 1fdfa9c7f61f3..48cb7ac02eb0c 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; @@ -434,6 +435,39 @@ private void EnqueueDocumentChangedEvent(Solution oldSolution, Solution newSolut var newProject = newSolution.GetRequiredProject(documentId.ProjectId); await EnqueueChangedDocumentWorkItemAsync(oldProject.GetRequiredDocument(documentId), newProject.GetRequiredDocument(documentId)).ConfigureAwait(false); + + // If all features are enabled for source generated documents, the solution crawler needs to + // include them in incremental analysis. + if (WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(newSolution.Options)) + { + var oldProjectSourceGeneratedDocuments = await oldProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); + var oldProjectSourceGeneratedDocumentsById = oldProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); + var newProjectSourceGeneratedDocuments = await newProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); + var newProjectSourceGeneratedDocumentsById = newProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); + + foreach (var (oldDocumentId, _) in oldProjectSourceGeneratedDocumentsById) + { + if (!newProjectSourceGeneratedDocumentsById.ContainsKey(oldDocumentId)) + { + // This source generated document was removed + EnqueueFullDocumentEvent(oldSolution, oldDocumentId, InvocationReasons.DocumentRemoved, "OnWorkspaceChanged"); + } + } + + foreach (var (newDocumentId, newDocument) in newProjectSourceGeneratedDocumentsById) + { + if (!oldProjectSourceGeneratedDocumentsById.TryGetValue(newDocumentId, out var oldDocument)) + { + // This source generated document was added + EnqueueFullDocumentEvent(newSolution, newDocumentId, InvocationReasons.DocumentAdded, "OnWorkspaceChanged"); + } + else + { + // This source generated document may have changed + await EnqueueChangedDocumentWorkItemAsync(oldDocument, newDocument).ConfigureAwait(continueOnCapturedContext: false); + } + } + } }, _shutdownToken); } @@ -487,6 +521,14 @@ private async Task EnqueueFullProjectWorkItemAsync(Project project, InvocationRe foreach (var documentId in project.AnalyzerConfigDocumentIds) await EnqueueDocumentWorkItemAsync(project, documentId, document: null, invocationReasons).ConfigureAwait(false); + + // If all features are enabled for source generated documents, the solution crawler needs to + // include them in incremental analysis. + if (WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(project.Solution.Options)) + { + foreach (var document in await project.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false)) + await EnqueueDocumentWorkItemAsync(project, document.Id, document, invocationReasons).ConfigureAwait(false); + } } private async Task EnqueueWorkItemAsync(IIncrementalAnalyzer analyzer, ReanalyzeScope scope, bool highPriority) diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index f769d0fbfbe3a..3a8cbb6d81170 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -109,11 +109,11 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(RenameTrackingPreview, FeatureOnOffOptions.RenameTrackingPreview, LanguageNames.CSharp); BindToOption(Underline_reassigned_variables, ClassificationOptions.Metadata.ClassifyReassignedVariables, LanguageNames.CSharp); - BindToOption(Enable_all_features_in_opened_files_from_source_generators, SourceGeneratedFileManager.Options.EnableOpeningInWorkspace, () => + BindToOption(Enable_all_features_in_opened_files_from_source_generators, WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace, () => { // If the option has not been set by the user, check if the option is enabled from experimentation. // If so, default to that. - return optionStore.GetOption(SourceGeneratedFileManager.Options.EnableOpeningInWorkspaceFeatureFlag); + return optionStore.GetOption(WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag); }); BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptions.DontPutOutOrRefOnStruct, LanguageNames.CSharp); diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs index 8e69f8a199f03..74889642a0454 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs @@ -394,8 +394,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) // If the file isn't already open, open it now. We may transition between opening and closing // if the file is repeatedly appearing and disappearing. - var connectToWorkspace = _workspace.Options.GetOption(Options.EnableOpeningInWorkspace) ?? - _workspace.Options.GetOption(Options.EnableOpeningInWorkspaceFeatureFlag); + var connectToWorkspace = WorkspaceConfigurationOptions.ShouldConnectSourceGeneratedFilesToWorkspace(_workspace.Options); if (connectToWorkspace && !_workspace.IsDocumentOpen(_documentIdentity.DocumentId)) { @@ -505,31 +504,5 @@ public void NavigateToSpan(TextSpan sourceSpan, CancellationToken cancellationTo _fileManager._visualStudioDocumentNavigationService.NavigateTo(_textBuffer, sourceText.GetVsTextSpanForSpan(sourceSpan), cancellationToken); } } - - [Export(typeof(IOptionProvider))] - internal sealed class Options : IOptionProvider - { - private const string FeatureName = "SourceGeneratedFileManager"; - - /// - /// This option allows the user to enable this. We are putting this behind a feature flag for now since we could have extensions - /// surprised by this and we want some time to work through those issues. - /// - internal static readonly Option2 EnableOpeningInWorkspace = new(FeatureName, nameof(EnableOpeningInWorkspace), defaultValue: null, - new RoamingProfileStorageLocation("TextEditor.Roslyn.Specific.EnableOpeningSourceGeneratedFilesInWorkspaceExperiment")); - - internal static readonly Option2 EnableOpeningInWorkspaceFeatureFlag = new(FeatureName, nameof(EnableOpeningInWorkspaceFeatureFlag), defaultValue: false, - new FeatureFlagStorageLocation("Roslyn.SourceGeneratorsEnableOpeningInWorkspace")); - - ImmutableArray IOptionProvider.Options => ImmutableArray.Create( - EnableOpeningInWorkspace, - EnableOpeningInWorkspaceFeatureFlag); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Options() - { - } - } } } diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs index 6d7a73c7eafbc..21239d12b23ae 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/VisualStudioWorkspace_OutOfProc.cs @@ -122,8 +122,8 @@ public void SetFileScopedNamespaces(bool value) public void SetEnableOpeningSourceGeneratedFilesInWorkspaceExperiment(bool value) { SetOption( - optionName: LanguageServices.Implementation.SourceGeneratedFileManager.Options.EnableOpeningInWorkspace.Name, - feature: LanguageServices.Implementation.SourceGeneratedFileManager.Options.EnableOpeningInWorkspace.Feature, + optionName: WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace.Name, + feature: WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace.Feature, value: value); } diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index 9855802da5742..bc10ca01da249 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -114,10 +114,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options ' Go To Definition BindToOption(NavigateToObjectBrowser, VisualStudioNavigationOptions.NavigateToObjectBrowser, LanguageNames.VisualBasic) - BindToOption(Enable_all_features_in_opened_files_from_source_generators, SourceGeneratedFileManager.Options.EnableOpeningInWorkspace, + BindToOption(Enable_all_features_in_opened_files_from_source_generators, WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspace, Function() ' If the option has Not been set by the user, check if the option is enabled from experimentation. - Return optionStore.GetOption(SourceGeneratedFileManager.Options.EnableOpeningInWorkspaceFeatureFlag) + Return optionStore.GetOption(WorkspaceConfigurationOptions.EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag) End Function) ' Regular expressions diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs index d70cef1949bfb..d37fc48404ec7 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceConfigurationOptions.cs @@ -26,9 +26,27 @@ internal class WorkspaceConfigurationOptions : IOptionProvider nameof(WorkspaceConfigurationOptions), nameof(DisableProjectCacheService), defaultValue: false, new FeatureFlagStorageLocation("Roslyn.DisableProjectCacheService")); + /// + /// This option allows the user to enable this. We are putting this behind a feature flag for now since we could have extensions + /// surprised by this and we want some time to work through those issues. + /// + internal static readonly Option2 EnableOpeningSourceGeneratedFilesInWorkspace = new(nameof(WorkspaceConfigurationOptions), nameof(EnableOpeningSourceGeneratedFilesInWorkspace), defaultValue: null, + new RoamingProfileStorageLocation("TextEditor.Roslyn.Specific.EnableOpeningSourceGeneratedFilesInWorkspaceExperiment")); + + internal static readonly Option2 EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag = new(nameof(WorkspaceConfigurationOptions), nameof(EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag), defaultValue: false, + new FeatureFlagStorageLocation("Roslyn.SourceGeneratorsEnableOpeningInWorkspace")); + + internal static bool ShouldConnectSourceGeneratedFilesToWorkspace(OptionSet options) + { + return options.GetOption(EnableOpeningSourceGeneratedFilesInWorkspace) ?? + options.GetOption(EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag); + } + ImmutableArray IOptionProvider.Options { get; } = ImmutableArray.Create( DisableRecoverableTrees, - DisableProjectCacheService); + DisableProjectCacheService, + EnableOpeningSourceGeneratedFilesInWorkspace, + EnableOpeningSourceGeneratedFilesInWorkspaceFeatureFlag); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] From 34b9696fa59300650e49586b062c7b48270a07a8 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 20 Jan 2022 08:24:03 -0800 Subject: [PATCH 012/187] Use DocumentOpened for normal and source generated documents --- .../CSharpTest/Workspaces/WorkspaceTests.cs | 49 ++++--------- .../SolutionCrawler/WorkCoordinator.cs | 8 --- .../Portable/Workspace/BackgroundCompiler.cs | 4 -- .../Portable/Workspace/BackgroundParser.cs | 2 - .../Portable/Workspace/Workspace_Editor.cs | 4 +- .../Portable/Workspace/Workspace_Events.cs | 68 ------------------- 6 files changed, 14 insertions(+), 121 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs index 225283a85cc2c..e84d56180895e 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs @@ -803,27 +803,18 @@ public async Task TestSourceGeneratedDocumentEvents() // Creating two waiters that will allow us to know for certain if the events have fired. using var closeWaiter = new EventWaiter(); using var openWaiter = new EventWaiter(); - using var sourceGeneratedCloseWaiter = new EventWaiter(); - using var sourceGeneratedOpenWaiter = new EventWaiter(); + // Wrapping event handlers so they can notify us on being called. var documentOpenedEventHandler = openWaiter.Wrap( - (sender, args) => Assert.True(false, $"Unexpected raising of {nameof(Workspace.DocumentOpened)}.")); - - var documentClosedEventHandler = closeWaiter.Wrap( - (sender, args) => Assert.True(false, $"Unexpected raising of {nameof(Workspace.DocumentClosed)}.")); - - var sourceGeneratedDocumentOpenedEventHandler = sourceGeneratedOpenWaiter.Wrap( (sender, args) => Assert.True(args.Document.Id == document.Id, - $"The document given to the '{nameof(Workspace.SourceGeneratedDocumentOpened)}' event handler did not have the same id as the one created for the test.")); + $"The source generated document given to the '{nameof(Workspace.DocumentOpened)}' event handler did not have the same id as the one created for the test.")); - var sourceGeneratedDocumentClosedEventHandler = sourceGeneratedCloseWaiter.Wrap( + var documentClosedEventHandler = closeWaiter.Wrap( (sender, args) => Assert.True(args.Document.Id == document.Id, - $"The document given to the '{nameof(Workspace.SourceGeneratedDocumentClosed)}' event handler did not have the same id as the one created for the test.")); + $"The source generated document given to the '{nameof(Workspace.DocumentClosed)}' event handler did not have the same id as the one created for the test.")); workspace.DocumentOpened += documentOpenedEventHandler; workspace.DocumentClosed += documentClosedEventHandler; - workspace.SourceGeneratedDocumentOpened += sourceGeneratedDocumentOpenedEventHandler; - workspace.SourceGeneratedDocumentClosed += sourceGeneratedDocumentClosedEventHandler; workspace.OpenSourceGeneratedDocument(document.Id); var sourceGeneratedDocumentId = workspace.GetDocumentIdInCurrentContext(document.GetOpenTextContainer()); @@ -835,24 +826,16 @@ public async Task TestSourceGeneratedDocumentEvents() await WaitForWorkspaceOperationsToComplete(workspace); // Wait to receive signal that events have fired. - Assert.False(openWaiter.WaitForEventToFire(shortEventTimeout), - string.Format("event handler 'DocumentOpened' was called within {0} seconds though it was not expected.", - shortEventTimeout.Seconds)); - - Assert.False(closeWaiter.WaitForEventToFire(shortEventTimeout), - string.Format("event handler 'DocumentClosed' was called within {0} seconds though it was not expected.", - shortEventTimeout.Seconds)); - - Assert.True(sourceGeneratedOpenWaiter.WaitForEventToFire(longEventTimeout), - string.Format("event 'SourceGeneratedDocumentOpened' was not fired within {0} minutes.", + Assert.True(openWaiter.WaitForEventToFire(longEventTimeout), + string.Format("event 'DocumentOpened' was not fired within {0} minutes.", longEventTimeout.Minutes)); - Assert.True(sourceGeneratedCloseWaiter.WaitForEventToFire(longEventTimeout), - string.Format("event 'SourceGeneratedDocumentClosed' was not fired within {0} minutes.", + Assert.True(closeWaiter.WaitForEventToFire(longEventTimeout), + string.Format("event 'DocumentClosed' was not fired within {0} minutes.", longEventTimeout.Minutes)); - workspace.SourceGeneratedDocumentOpened -= sourceGeneratedDocumentOpenedEventHandler; - workspace.SourceGeneratedDocumentClosed -= sourceGeneratedDocumentClosedEventHandler; + workspace.DocumentOpened -= documentOpenedEventHandler; + workspace.DocumentClosed -= documentClosedEventHandler; workspace.OpenSourceGeneratedDocument(document.Id); workspace.CloseSourceGeneratedDocument(document.Id); @@ -863,19 +846,11 @@ public async Task TestSourceGeneratedDocumentEvents() // Verifying that an event has not been called is difficult to prove. // All events should have already been called so we wait 5 seconds and then assume the event handler was removed correctly. Assert.False(openWaiter.WaitForEventToFire(shortEventTimeout), - string.Format("event handler 'DocumentOpened' was called within {0} seconds though it was not expected.", + string.Format("event handler 'DocumentOpened' was called within {0} seconds though it was removed from the list.", shortEventTimeout.Seconds)); Assert.False(closeWaiter.WaitForEventToFire(shortEventTimeout), - string.Format("event handler 'DocumentClosed' was called within {0} seconds though it was not expected.", - shortEventTimeout.Seconds)); - - Assert.False(sourceGeneratedOpenWaiter.WaitForEventToFire(shortEventTimeout), - string.Format("event handler 'SourceGeneratedDocumentOpened' was called within {0} seconds though it was removed from the list.", - shortEventTimeout.Seconds)); - - Assert.False(sourceGeneratedCloseWaiter.WaitForEventToFire(shortEventTimeout), - string.Format("event handler 'SourceGeneratedDocumentClosed' was called within {0} seconds though it was removed from the list.", + string.Format("event handler 'DocumentClosed' was called within {0} seconds though it was removed from the list.", shortEventTimeout.Seconds)); } diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 48cb7ac02eb0c..33b408d23c330 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -77,8 +77,6 @@ public WorkCoordinator( _registration.Workspace.WorkspaceChanged += OnWorkspaceChanged; _registration.Workspace.DocumentOpened += OnDocumentOpened; _registration.Workspace.DocumentClosed += OnDocumentClosed; - _registration.Workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; - _registration.Workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } // subscribe to option changed event after all required fields are set @@ -110,8 +108,6 @@ public void Shutdown(bool blockingShutdown) _registration.Workspace.WorkspaceChanged -= OnWorkspaceChanged; _registration.Workspace.DocumentOpened -= OnDocumentOpened; _registration.Workspace.DocumentClosed -= OnDocumentClosed; - _registration.Workspace.SourceGeneratedDocumentOpened -= OnDocumentOpened; - _registration.Workspace.SourceGeneratedDocumentClosed -= OnDocumentClosed; // cancel any pending blocks _shutdownNotificationSource.Cancel(); @@ -156,16 +152,12 @@ private void OnOptionChanged(object? sender, OptionChangedEventArgs e) _registration.Workspace.WorkspaceChanged += OnWorkspaceChanged; _registration.Workspace.DocumentOpened += OnDocumentOpened; _registration.Workspace.DocumentClosed += OnDocumentClosed; - _registration.Workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; - _registration.Workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } else { _registration.Workspace.WorkspaceChanged -= OnWorkspaceChanged; _registration.Workspace.DocumentOpened -= OnDocumentOpened; _registration.Workspace.DocumentClosed -= OnDocumentClosed; - _registration.Workspace.SourceGeneratedDocumentOpened -= OnDocumentOpened; - _registration.Workspace.SourceGeneratedDocumentClosed -= OnDocumentClosed; } SolutionCrawlerLogger.LogOptionChanged(CorrelationId, value); diff --git a/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs b/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs index abedeb179faa5..304bd7e15bc61 100644 --- a/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs +++ b/src/Features/Core/Portable/Workspace/BackgroundCompiler.cs @@ -39,8 +39,6 @@ public BackgroundCompiler(Workspace workspace) _workspace.WorkspaceChanged += OnWorkspaceChanged; _workspace.DocumentOpened += OnDocumentOpened; _workspace.DocumentClosed += OnDocumentClosed; - _workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; - _workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } public void Dispose() @@ -51,8 +49,6 @@ public void Dispose() _workspace.DocumentClosed -= OnDocumentClosed; _workspace.DocumentOpened -= OnDocumentOpened; - _workspace.SourceGeneratedDocumentClosed -= OnDocumentClosed; - _workspace.SourceGeneratedDocumentOpened -= OnDocumentOpened; _workspace.WorkspaceChanged -= OnWorkspaceChanged; _workspace = null!; diff --git a/src/Features/Core/Portable/Workspace/BackgroundParser.cs b/src/Features/Core/Portable/Workspace/BackgroundParser.cs index 1d1fff8dac186..f39ab27ce372d 100644 --- a/src/Features/Core/Portable/Workspace/BackgroundParser.cs +++ b/src/Features/Core/Portable/Workspace/BackgroundParser.cs @@ -49,8 +49,6 @@ public BackgroundParser(Workspace workspace) workspace.DocumentOpened += OnDocumentOpened; workspace.DocumentClosed += OnDocumentClosed; - workspace.SourceGeneratedDocumentOpened += OnDocumentOpened; - workspace.SourceGeneratedDocumentClosed += OnDocumentClosed; } private void OnActiveDocumentChanged(object sender, DocumentId activeDocumentId) diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index e9a8aea765f03..e10100f73b0bd 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -435,7 +435,7 @@ internal void OnSourceGeneratedDocumentOpened( static async Task RaiseSourceGeneratedDocumentOpenedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) { var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - await workspace.RaiseSourceGeneratedDocumentOpenedEventAsync(document).ConfigureAwait(false); + await workspace.RaiseDocumentOpenedEventAsync(document).ConfigureAwait(false); } } @@ -457,7 +457,7 @@ internal void OnSourceGeneratedDocumentClosed(DocumentId documentId) static async Task RaiseSourceGeneratedDocumentClosedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) { var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - await workspace.RaiseSourceGeneratedDocumentClosedEventAsync(document).ConfigureAwait(false); + await workspace.RaiseDocumentClosedEventAsync(document).ConfigureAwait(false); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs index acc05b6b27912..289af65d863b8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs @@ -22,8 +22,6 @@ public abstract partial class Workspace private const string WorkspaceFailedEventName = "WorkspaceFailed"; private const string DocumentOpenedEventName = "DocumentOpened"; private const string DocumentClosedEventName = "DocumentClosed"; - private const string SourceGeneratedDocumentOpenedEventName = "SourceGeneratedDocumentOpened"; - private const string SourceGeneratedDocumentClosedEventName = "SourceGeneratedDocumentClosed"; private const string DocumentActiveContextChangedName = "DocumentActiveContextChanged"; /// @@ -170,72 +168,6 @@ protected Task RaiseDocumentClosedEventAsync(Document document) } } - /// - /// An event that is fired when a documents is opened in the editor. - /// - internal event EventHandler SourceGeneratedDocumentOpened - { - add - { - _eventMap.AddEventHandler(SourceGeneratedDocumentOpenedEventName, value); - } - - remove - { - _eventMap.RemoveEventHandler(SourceGeneratedDocumentOpenedEventName, value); - } - } - - private protected Task RaiseSourceGeneratedDocumentOpenedEventAsync(Document document) - { - var ev = GetEventHandlers(SourceGeneratedDocumentOpenedEventName); - if (ev.HasHandlers && document != null) - { - return this.ScheduleTask(() => - { - var args = new DocumentEventArgs(document); - ev.RaiseEvent(handler => handler(this, args)); - }, SourceGeneratedDocumentOpenedEventName); - } - else - { - return Task.CompletedTask; - } - } - - /// - /// An event that is fired when a document is closed in the editor. - /// - internal event EventHandler SourceGeneratedDocumentClosed - { - add - { - _eventMap.AddEventHandler(SourceGeneratedDocumentClosedEventName, value); - } - - remove - { - _eventMap.RemoveEventHandler(SourceGeneratedDocumentClosedEventName, value); - } - } - - private protected Task RaiseSourceGeneratedDocumentClosedEventAsync(Document document) - { - var ev = GetEventHandlers(SourceGeneratedDocumentClosedEventName); - if (ev.HasHandlers && document != null) - { - return this.ScheduleTask(() => - { - var args = new DocumentEventArgs(document); - ev.RaiseEvent(handler => handler(this, args)); - }, SourceGeneratedDocumentClosedEventName); - } - else - { - return Task.CompletedTask; - } - } - /// /// An event that is fired when the active context document associated with a buffer /// changes. From 6f9cf98b11138c9f56c776364019558b712f92c5 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 3 Feb 2022 22:36:46 -0800 Subject: [PATCH 013/187] Cleanup from code review * Avoid relying on GetOpenDocumentInCurrentContext for source generated documents * Clarify documentation * Ensure yield before invoking GetSourceGeneratedDocumentAsync --- .../TaggerEventSources.DiagnosticsChangedEventSource.cs | 5 ++--- src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb | 7 ++++--- .../Workspace/Solution/SourceGeneratedDocumentState.cs | 8 +++++--- .../Core/Portable/Workspace/Workspace_Editor.cs | 9 +++++++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.DiagnosticsChangedEventSource.cs b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.DiagnosticsChangedEventSource.cs index 20d2a9f9bd8e8..e33cf8d79f766 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.DiagnosticsChangedEventSource.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.DiagnosticsChangedEventSource.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; @@ -24,9 +23,9 @@ public DiagnosticsChangedEventSource(ITextBuffer subjectBuffer, IDiagnosticServi private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) { - var document = _subjectBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); + var documentId = e.Workspace.GetDocumentIdInCurrentContext(_subjectBuffer.AsTextContainer()); - if (document != null && document.Project.Solution.Workspace == e.Workspace && document.Id == e.DocumentId) + if (documentId == e.DocumentId) { this.RaiseChanged(); } diff --git a/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb index 6170f494ddc05..ab1e0c02ca92c 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb @@ -101,7 +101,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus End Sub - Public Sub TestSourceGeneratedDocumentOperation() + Public Async Function TestSourceGeneratedDocumentOperation() As Task Using workspace = TestWorkspace.Create( @@ -111,14 +111,15 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus Dim subjectDocument = workspace.Documents.Single() Dim openDocument = subjectDocument.GetOpenTextContainer() - Dim document = Assert.IsType(Of SourceGeneratedDocument)(openDocument.GetOpenDocumentInCurrentContext()) + Dim sourceGeneratedDocumentId = workspace.GetDocumentIdInCurrentContext(openDocument) + Dim document = Assert.IsType(Of SourceGeneratedDocument)(Await workspace.CurrentSolution.GetDocumentAsync(sourceGeneratedDocumentId, includeSourceGenerated:=True)) Dim documentServices = document.State.Services Dim documentOperations = documentServices.GetService(Of IDocumentOperationService)() Assert.False(documentOperations.CanApplyChange) Assert.True(documentOperations.SupportDiagnostics) End Using - End Sub + End Function Public Async Function TestExcerptService_SingleLine() As Task diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index e88f3d105f482..38adb7b6ebd71 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -90,6 +90,11 @@ public SourceGeneratedDocumentState WithUpdatedGeneratedContent(SourceText sourc this.solutionServices); } + /// + /// This is modeled after , but sets + /// to for source generated + /// documents. + /// internal sealed class SourceGeneratedTextDocumentServiceProvider : IDocumentServiceProvider { public static readonly SourceGeneratedTextDocumentServiceProvider Instance = new(); @@ -101,9 +106,6 @@ private SourceGeneratedTextDocumentServiceProvider() public TService? GetService() where TService : class, IDocumentService { - // right now, it doesn't implement much services but we expect it to implements all - // document services in future so that we can remove all if branches in feature code - // but just delegate work to default document services. if (SourceGeneratedDocumentOperationService.Instance is TService documentOperationService) { return documentOperationService; diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index e10100f73b0bd..81b95ee36e5b5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -430,10 +431,12 @@ internal void OnSourceGeneratedDocumentOpened( UpdateCurrentContextMapping_NoLock(textContainer, documentId, isCurrentContext: true); // Fire and forget that the workspace is changing. - _ = RaiseSourceGeneratedDocumentOpenedAsync(this, CurrentSolution, documentId); + var token = _taskQueue.Listener.BeginAsyncOperation(nameof(RaiseSourceGeneratedDocumentOpenedAsync)); + _ = RaiseSourceGeneratedDocumentOpenedAsync(this, CurrentSolution, documentId).CompletesAsyncOperation(token); static async Task RaiseSourceGeneratedDocumentOpenedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) { + await Task.Yield().ConfigureAwait(false); var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); await workspace.RaiseDocumentOpenedEventAsync(document).ConfigureAwait(false); } @@ -452,10 +455,12 @@ internal void OnSourceGeneratedDocumentClosed(DocumentId documentId) ClearOpenDocument(documentId); // Fire and forget that the workspace is changing. - _ = RaiseSourceGeneratedDocumentClosedAsync(this, CurrentSolution, documentId); + var token = _taskQueue.Listener.BeginAsyncOperation(nameof(RaiseSourceGeneratedDocumentClosedAsync)); + _ = RaiseSourceGeneratedDocumentClosedAsync(this, CurrentSolution, documentId).CompletesAsyncOperation(token); static async Task RaiseSourceGeneratedDocumentClosedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) { + await Task.Yield().ConfigureAwait(false); var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); await workspace.RaiseDocumentClosedEventAsync(document).ConfigureAwait(false); } From f889014e6e09fa8f9e124627caed1f842248bb35 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Fri, 4 Feb 2022 13:15:45 +0000 Subject: [PATCH 014/187] Update dependencies from https://github.com/dotnet/arcade build 20220203.1 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 7.0.0-beta.22080.1 -> To Version 7.0.0-beta.22103.1 --- eng/Version.Details.xml | 8 ++++---- eng/common/templates/job/job.yml | 1 + eng/common/templates/steps/source-build.yml | 4 ++-- global.json | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 646f210ed6971..c47dfaab57d67 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - 4d6406fa2e84c8516a338694be3a4097e6e1f104 + 70831f0d126fe88b81d7dc8de11358e17a5ce364 https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - 4d6406fa2e84c8516a338694be3a4097e6e1f104 + 70831f0d126fe88b81d7dc8de11358e17a5ce364 diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index c5c2a9915121b..92e98af7ebd5c 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -140,6 +140,7 @@ jobs: - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(parameters.disableComponentGovernance, 'true')) }}: - task: ComponentGovernanceComponentDetection@0 + continueOnError: true - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml index d85d6d07d5c7b..12a8ff94d8e96 100644 --- a/eng/common/templates/steps/source-build.yml +++ b/eng/common/templates/steps/source-build.yml @@ -43,8 +43,8 @@ steps: # 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 [ '$(dotnetclimsrc-read-sas-token-base64)' != '$''(dotnetclimsrc-read-sas-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetclimsrc.blob.core.windows.net/dotnet /p:DotNetRuntimeSourceFeedKey=$(dotnetclimsrc-read-sas-token-base64) --runtimesourcefeed https://dotnetclimsrc.blob.core.windows.net/dotnet --runtimesourcefeedkey $(dotnetclimsrc-read-sas-token-base64)' + 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 diff --git a/global.json b/global.json index 8b4d21042c9d0..08d61ed8027e7 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22080.1", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22080.1" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22103.1", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22103.1" } } From fe55ebbb4c7143fbd7ba7f2bcce17ba294f197d5 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 4 Feb 2022 10:50:55 -0800 Subject: [PATCH 015/187] Update diagnostic navigation to not rely on TryGetSourceGeneratedDocumentForAlreadyGeneratedId --- .../AbstractTableEntriesSnapshot.cs | 21 +++++------- ...iagnosticListTable.BuildTableDataSource.cs | 33 ++++++++----------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs index 0ed45a9ffa317..cd593caa7a4d4 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/AbstractTableEntriesSnapshot.cs @@ -200,23 +200,20 @@ protected bool TryNavigateToItem(int index, bool previewTab, bool activate, Canc } } - var document = solution.GetDocument(documentId) - ?? solution.GetProject(documentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); - if (document == null) - { - return false; - } - LinePosition position; - LinePosition trackingLinePosition; - - if (workspace.IsDocumentOpen(documentId) && - (trackingLinePosition = GetTrackingLineColumn(document, index)) != LinePosition.Zero) - { + var document = solution.GetDocument(documentId); + if (document is not null + && workspace.IsDocumentOpen(documentId) + && GetTrackingLineColumn(document, index) is { } trackingLinePosition + && trackingLinePosition != LinePosition.Zero) + { + // For normal documents already open, try to map the diagnostic location to its current position in a + // potentially-edited document. position = trackingLinePosition; } else { + // Otherwise navigate to the original reported location. position = item.GetOriginalPosition(); } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs index c7c6f6c6b9820..ccd5735cdee49 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/VisualStudioDiagnosticListTable.BuildTableDataSource.cs @@ -223,13 +223,6 @@ public override bool TryNavigateTo(int index, bool previewTab, bool activate, Ca if (documentId is null) return false; - var solution = item.Workspace.CurrentSolution; - if (!solution.ContainsDocument(documentId) - && solution.GetProject(documentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId) is null) - { - return false; - } - return TryNavigateTo(item.Workspace, documentId, item.GetOriginalPosition(), previewTab, activate, cancellationToken); } @@ -245,21 +238,23 @@ public override bool TryNavigateTo(int index, bool previewTab, bool activate, Ca return documentId; } - if (documentId is not null - && solution.GetProject(projectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId) is not null) + if (solution.GetProject(projectId) is { } project) { - return documentId; - } - - if (documentId is null && solution.GetProject(projectId) is { } project) - { - // We couldn't find a document ID when the item was created, so it may be a source generator - // output. + // We couldn't find a document matching a known ID when the item was created, so it may be a + // source generator output. var documents = threadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentsAsync(cancellationToken).AsTask()); - var projectDirectory = Path.GetDirectoryName(project.FilePath); - documentId = documents.FirstOrDefault(document => Path.Combine(projectDirectory, document.FilePath) == item.GetOriginalFilePath())?.Id; if (documentId is not null) - return documentId; + { + if (documents.Any(document => document.Id == documentId)) + return documentId; + } + else + { + var projectDirectory = Path.GetDirectoryName(project.FilePath); + documentId = documents.FirstOrDefault(document => Path.Combine(projectDirectory, document.FilePath) == item.GetOriginalFilePath())?.Id; + if (documentId is not null) + return documentId; + } } // okay, documentId no longer exist in current solution, find it by file path. From 664b9a6a8cbc58fb6b384419501019c88cda02b4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 4 Feb 2022 12:44:36 -0800 Subject: [PATCH 016/187] Document safe use of TryGetSourceGeneratedDocumentForAlreadyGeneratedId --- .../Diagnostics/AbstractDiagnosticsTaggerProvider.cs | 2 ++ .../EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs | 2 ++ .../DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 2 ++ .../Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs | 1 + 4 files changed, 7 insertions(+) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs index 3af9ca015acf6..a9689a92b7e62 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -75,6 +75,8 @@ private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) return; } + // TryGetSourceGeneratedDocumentForAlreadyGeneratedId is safe to use here because the only way to report + // a diagnostic in a source generated document is for the source generated document to exist. var document = e.Solution.GetDocument(e.DocumentId) ?? e.Solution.GetProject(e.DocumentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(e.DocumentId); diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index 2ec14d3ae9331..ea349ae83548b 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -199,6 +199,8 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { + // TryGetSourceGeneratedDocumentForAlreadyGeneratedId is safe to use here because the only way to + // report a diagnostic in a source generated document is for the source generated document to exist. var document = project.GetTextDocument(documentId) ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); if (document == null) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 8dc1e0fd033b3..9ab2e0d9241aa 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -535,6 +535,8 @@ private void RaiseProjectDiagnosticsCreated(Project project, StateSet stateSet, foreach (var documentId in newAnalysisResult.DocumentIds) { + // TryGetSourceGeneratedDocumentForAlreadyGeneratedId is safe to use here because the only way to report + // a diagnostic in a source generated document is for the source generated document to exist. var document = project.GetTextDocument(documentId) ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); if (document == null) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 3cf9988168438..6cbf3c5f6f632 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -320,6 +320,7 @@ private static void VerifyDocumentMap(Project project, ImmutableDictionary Date: Fri, 4 Feb 2022 13:36:25 -0800 Subject: [PATCH 017/187] Fix non-deterministic event ordering for opening/closing source generated documents --- .../CSharpTest/Workspaces/WorkspaceTests.cs | 4 +- .../Workspaces/TestHostDocument.cs | 8 +++- .../TestUtilities/Workspaces/TestWorkspace.cs | 6 ++- .../Workspace/SourceGeneratedFileManager.cs | 20 +++++---- .../Portable/Workspace/Workspace_Editor.cs | 33 +++++--------- .../SolutionWithSourceGeneratorTests.cs | 44 ++++++++++--------- 6 files changed, 59 insertions(+), 56 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs index e84d56180895e..133609cc90bf3 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs @@ -820,7 +820,7 @@ public async Task TestSourceGeneratedDocumentEvents() var sourceGeneratedDocumentId = workspace.GetDocumentIdInCurrentContext(document.GetOpenTextContainer()); Assert.Equal(document.Id, sourceGeneratedDocumentId); - workspace.CloseSourceGeneratedDocument(document.Id); + await workspace.CloseSourceGeneratedDocumentAsync(sourceGeneratedDocumentId); // Wait for all workspace tasks to finish. After this is finished executing, all handlers should have been notified. await WaitForWorkspaceOperationsToComplete(workspace); @@ -838,7 +838,7 @@ public async Task TestSourceGeneratedDocumentEvents() workspace.DocumentClosed -= documentClosedEventHandler; workspace.OpenSourceGeneratedDocument(document.Id); - workspace.CloseSourceGeneratedDocument(document.Id); + await workspace.CloseSourceGeneratedDocumentAsync(document.Id); // Wait for all workspace tasks to finish. After this is finished executing, all handlers should have been notified. await WaitForWorkspaceOperationsToComplete(workspace); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index 8edb388c240fd..a8f4a24a6dc35 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -270,7 +271,12 @@ public ITextBuffer2 GetTextBuffer() var generatorAssemblyName = SourceGeneratedDocumentIdentity.GetGeneratorAssemblyName(generator); var generatorTypeName = SourceGeneratedDocumentIdentity.GetGeneratorTypeName(generator); var filePath = testDocument.FilePath ?? throw new InvalidOperationException(); - workspace.OnSourceGeneratedDocumentOpened(new SourceGeneratedDocumentIdentity(linkedId, hintName, generatorAssemblyName, generatorTypeName, filePath), _textBuffer.AsTextContainer()); + + var threadingContext = workspace.GetService(); + var document = threadingContext.JoinableTaskFactory.Run(() => workspace.CurrentSolution.GetSourceGeneratedDocumentAsync(testDocument.Id, CancellationToken.None).AsTask()); + Contract.ThrowIfNull(document); + + workspace.OnSourceGeneratedDocumentOpened(new SourceGeneratedDocumentIdentity(linkedId, hintName, generatorAssemblyName, generatorTypeName, filePath), _textBuffer.AsTextContainer(), document); } else { diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs index b0e7cefa996c9..a8d10cab77156 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs @@ -696,13 +696,15 @@ public void OpenSourceGeneratedDocument(DocumentId documentId) testDocument.GetOpenTextContainer(); } - public void CloseSourceGeneratedDocument(DocumentId documentId) + public async Task CloseSourceGeneratedDocumentAsync(DocumentId documentId) { var testDocument = this.GetTestDocument(documentId); Contract.ThrowIfFalse(testDocument.IsSourceGenerated); Contract.ThrowIfFalse(IsDocumentOpen(documentId)); - this.OnSourceGeneratedDocumentClosed(documentId); + var document = await CurrentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None); + Contract.ThrowIfNull(document); + OnSourceGeneratedDocumentClosed(document); } public void ChangeDocument(DocumentId documentId, SourceText text) diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs index e57cba99b7df4..9790af57a3740 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs @@ -292,7 +292,9 @@ private void DisconnectFromWorkspaceIfOpen() if (_workspace.IsDocumentOpen(_documentIdentity.DocumentId)) { - _workspace.OnSourceGeneratedDocumentClosed(_documentIdentity.DocumentId); + var sourceGeneratedDocument = (SourceGeneratedDocument?)_textBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + Contract.ThrowIfNull(sourceGeneratedDocument); + _workspace.OnSourceGeneratedDocumentClosed(sourceGeneratedDocument); } } @@ -300,16 +302,16 @@ public void Dispose() { AssertIsForeground(); + _workspace.WorkspaceChanged -= OnWorkspaceChanged; + + DisconnectFromWorkspaceIfOpen(); + using (var readOnlyRegionEdit = _textBuffer.CreateReadOnlyRegionEdit()) { readOnlyRegionEdit.RemoveReadOnlyRegion(_readOnlyRegion); readOnlyRegionEdit.Apply(); } - _workspace.WorkspaceChanged -= OnWorkspaceChanged; - - DisconnectFromWorkspaceIfOpen(); - // Cancel any remaining asynchronous work we may have had to update this file _cancellationTokenSource.Cancel(); } @@ -318,6 +320,7 @@ public void Dispose() public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) { + SourceGeneratedDocument? generatedDocument = null; SourceText? generatedSource = null; var project = _workspace.CurrentSolution.GetProject(_documentIdentity.DocumentId.ProjectId); @@ -333,8 +336,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) } else { - var generatedDocument = await project.GetSourceGeneratedDocumentAsync(_documentIdentity.DocumentId, cancellationToken).ConfigureAwait(false); - + generatedDocument = await project.GetSourceGeneratedDocumentAsync(_documentIdentity.DocumentId, cancellationToken).ConfigureAwait(false); if (generatedDocument != null) { windowFrameMessageToShow = string.Format(ServicesVSResources.This_file_is_autogenerated_by_0_and_cannot_be_edited, GeneratorDisplayName); @@ -365,6 +367,8 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) // Update the text if we have new text if (generatedSource != null) { + RoslynDebug.AssertNotNull(generatedDocument); + try { // Allow us to do our own edits @@ -395,7 +399,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) if (connectToWorkspace && !_workspace.IsDocumentOpen(_documentIdentity.DocumentId)) { - _workspace.OnSourceGeneratedDocumentOpened(_documentIdentity, _textBuffer.AsTextContainer()); + _workspace.OnSourceGeneratedDocumentOpened(_documentIdentity, _textBuffer.AsTextContainer(), generatedDocument); } } finally diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs index 81b95ee36e5b5..a0235aab42df7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Editor.cs @@ -417,7 +417,8 @@ protected internal void OnDocumentOpened( // TODO: switch this protected once we have confidence in API shape internal void OnSourceGeneratedDocumentOpened( SourceGeneratedDocumentIdentity documentIdentity, - SourceTextContainer textContainer) + SourceTextContainer textContainer, + SourceGeneratedDocument document) { using (_serializationLock.DisposableWait()) { @@ -431,39 +432,25 @@ internal void OnSourceGeneratedDocumentOpened( UpdateCurrentContextMapping_NoLock(textContainer, documentId, isCurrentContext: true); // Fire and forget that the workspace is changing. - var token = _taskQueue.Listener.BeginAsyncOperation(nameof(RaiseSourceGeneratedDocumentOpenedAsync)); - _ = RaiseSourceGeneratedDocumentOpenedAsync(this, CurrentSolution, documentId).CompletesAsyncOperation(token); - - static async Task RaiseSourceGeneratedDocumentOpenedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) - { - await Task.Yield().ConfigureAwait(false); - var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - await workspace.RaiseDocumentOpenedEventAsync(document).ConfigureAwait(false); - } + var token = _taskQueue.Listener.BeginAsyncOperation(nameof(OnSourceGeneratedDocumentOpened)); + _ = RaiseDocumentOpenedEventAsync(document).CompletesAsyncOperation(token); } this.RegisterText(textContainer); } - internal void OnSourceGeneratedDocumentClosed(DocumentId documentId) + internal void OnSourceGeneratedDocumentClosed(SourceGeneratedDocument document) { using (_serializationLock.DisposableWait()) { - CheckDocumentIsOpen(documentId); + CheckDocumentIsOpen(document.Id); - Contract.ThrowIfFalse(_openSourceGeneratedDocumentIdentities.Remove(documentId)); - ClearOpenDocument(documentId); + Contract.ThrowIfFalse(_openSourceGeneratedDocumentIdentities.Remove(document.Id)); + ClearOpenDocument(document.Id); // Fire and forget that the workspace is changing. - var token = _taskQueue.Listener.BeginAsyncOperation(nameof(RaiseSourceGeneratedDocumentClosedAsync)); - _ = RaiseSourceGeneratedDocumentClosedAsync(this, CurrentSolution, documentId).CompletesAsyncOperation(token); - - static async Task RaiseSourceGeneratedDocumentClosedAsync(Workspace workspace, Solution currentSolution, DocumentId documentId) - { - await Task.Yield().ConfigureAwait(false); - var document = await currentSolution.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - await workspace.RaiseDocumentClosedEventAsync(document).ConfigureAwait(false); - } + var token = _taskQueue.Listener.BeginAsyncOperation(nameof(OnSourceGeneratedDocumentClosed)); + _ = RaiseDocumentClosedEventAsync(document).CompletesAsyncOperation(token); } } diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index bb85e4718ed9a..7f87e21544672 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -538,14 +538,14 @@ public async Task OpenSourceGeneratedUpdatedToBufferContentsWhenCallingGetOpenDo Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); - var generatedDocumentIdentity = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()).Identity; + var generatedDocument = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()); + var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer); + workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); - var generatedDocument = differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges(); - Assert.IsType(generatedDocument); - Assert.Same(differentOpenTextContainer.CurrentText, await generatedDocument!.GetTextAsync()); + generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); + Assert.Same(differentOpenTextContainer.CurrentText, await generatedDocument.GetTextAsync()); Assert.NotSame(workspace.CurrentSolution, generatedDocument.Project.Solution); var generatedTree = await generatedDocument.GetSyntaxTreeAsync(); @@ -563,12 +563,13 @@ public async Task OpenSourceGeneratedFileDoesNotCreateNewSnapshotIfContentsKnown Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); - var generatedDocumentIdentity = Assert.Single(await workspace.CurrentSolution.Projects.Single().GetSourceGeneratedDocumentsAsync()).Identity; + var generatedDocument = Assert.Single(await workspace.CurrentSolution.Projects.Single().GetSourceGeneratedDocumentsAsync()); + var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// StaticContent", Encoding.UTF8).Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer); + workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); - var generatedDocument = differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges(); + generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); Assert.Same(workspace.CurrentSolution, generatedDocument!.Project.Solution); } @@ -583,18 +584,18 @@ public async Task OpenSourceGeneratedFileMatchesBufferContentsEvenIfGeneratedFil Assert.True(workspace.SetCurrentSolution(_ => originalAdditionalFile.Project.Solution, WorkspaceChangeKind.SolutionChanged)); - var generatedDocumentIdentity = Assert.Single(await originalAdditionalFile.Project.GetSourceGeneratedDocumentsAsync()).Identity; + var generatedDocument = Assert.Single(await originalAdditionalFile.Project.GetSourceGeneratedDocumentsAsync()); + var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer); + workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); workspace.OnAdditionalDocumentRemoved(originalAdditionalFile.Id); // At this point there should be no generated documents, even though our file is still open Assert.Empty(await workspace.CurrentSolution.Projects.Single().GetSourceGeneratedDocumentsAsync()); - var generatedDocument = differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges(); - Assert.IsType(generatedDocument); - Assert.Same(differentOpenTextContainer.CurrentText, await generatedDocument!.GetTextAsync()); + generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); + Assert.Same(differentOpenTextContainer.CurrentText, await generatedDocument.GetTextAsync()); var generatedTree = await generatedDocument.GetSyntaxTreeAsync(); var compilation = await generatedDocument.Project.GetRequiredCompilationAsync(CancellationToken.None); @@ -615,13 +616,13 @@ public async Task OpenSourceGeneratedDocumentUpdatedAndVisibleInProjectReference Assert.True(workspace.SetCurrentSolution(_ => solution, WorkspaceChangeKind.SolutionChanged)); - var generatedDocumentIdentity = Assert.Single(await workspace.CurrentSolution.GetRequiredProject(projectIdWithGenerator).GetSourceGeneratedDocumentsAsync()).Identity; + var generatedDocument = Assert.Single(await workspace.CurrentSolution.GetRequiredProject(projectIdWithGenerator).GetSourceGeneratedDocumentsAsync()); + var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer); + workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); - var generatedDocument = differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges(); - AssertEx.NotNull(generatedDocument); + generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); var generatedTree = await generatedDocument.GetSyntaxTreeAsync(); // Fetch the compilation from the other project, it should have a compilation reference that @@ -643,14 +644,17 @@ public async Task OpenSourceGeneratedDocumentsUpdateIsDocumentOpenAndCloseWorks( Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); - var generatedDocumentIdentity = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()).Identity; + var generatedDocument = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()); + var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer); + workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); Assert.True(workspace.IsDocumentOpen(generatedDocumentIdentity.DocumentId)); - workspace.OnSourceGeneratedDocumentClosed(generatedDocumentIdentity.DocumentId); + var document = await workspace.CurrentSolution.GetSourceGeneratedDocumentAsync(generatedDocumentIdentity.DocumentId, CancellationToken.None); + Contract.ThrowIfNull(document); + workspace.OnSourceGeneratedDocumentClosed(document); Assert.False(workspace.IsDocumentOpen(generatedDocumentIdentity.DocumentId)); Assert.Null(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); From 64818b1f99a155d0bc61e950f43131aeb6d3435f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 9 Feb 2022 13:17:50 +0000 Subject: [PATCH 018/187] Update dependencies from https://github.com/dotnet/arcade build 20220208.1 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 7.0.0-beta.22080.1 -> To Version 7.0.0-beta.22108.1 --- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index c47dfaab57d67..e3bf402ebaeb3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - 70831f0d126fe88b81d7dc8de11358e17a5ce364 + dda61e4601d38b5d9d972f0541ff652ba5a16ad6 https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - 70831f0d126fe88b81d7dc8de11358e17a5ce364 + dda61e4601d38b5d9d972f0541ff652ba5a16ad6 diff --git a/global.json b/global.json index 08d61ed8027e7..3ef9453bb754c 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22103.1", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22103.1" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22108.1", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22108.1" } } From 526110052b3850dbb3c38fa050a75f9b75228daa Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 9 Feb 2022 21:13:50 -0800 Subject: [PATCH 019/187] Clarify test ordering --- .../ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 87cdfad7a12c6..12fa17d50dede 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -562,8 +562,9 @@ public async Task TestWorkspaceDiagnosticsForSourceGeneratedFiles(bool useVSDiag var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - // Project.GetSourceGeneratedDocumentsAsync does not return documents in a deterministic order, so we sort - // the results here to ensure subsequent assertions are successful. + // Project.GetSourceGeneratedDocumentsAsync may not return documents in a deterministic order, so we sort + // the results here to ensure subsequent assertions are not dependent on the order of items provided by the + // project. results = results.Sort((x, y) => x.Uri.ToString().CompareTo(y.Uri.ToString())); Assert.Equal(2, results.Length); From 9fdd65954d5b23d1f94524b8c22cee8ad809cf5a Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Thu, 10 Feb 2022 13:18:07 +0000 Subject: [PATCH 020/187] Update dependencies from https://github.com/dotnet/arcade build 20220209.1 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 7.0.0-beta.22080.1 -> To Version 7.0.0-beta.22109.1 --- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e3bf402ebaeb3..06c0284e9038b 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - dda61e4601d38b5d9d972f0541ff652ba5a16ad6 + 00b6cce8983065a9cf9fb108a724f28ec44eaf75 https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - dda61e4601d38b5d9d972f0541ff652ba5a16ad6 + 00b6cce8983065a9cf9fb108a724f28ec44eaf75 diff --git a/global.json b/global.json index 3ef9453bb754c..11ee799695a6f 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22108.1", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22108.1" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22109.1", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22109.1" } } From e9b8d17a5eeae29aba1e7b9a8e261c4f57ddfa49 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 10 Feb 2022 19:06:31 -0800 Subject: [PATCH 021/187] Add telemetry to log analyzer execution times during background analysis The additional telemetry is guarded by a new user facing option. This is an experiment being performed to see gauge if this additional telemetry is valuable/noisy/performant to help us improve the background analysis performance in future. If it turns out to be too much of an overhead, we may turn this off by default or revert it in future. --- .../Core/Portable/Diagnostics/DiagnosticOptions.cs | 7 ++++++- .../EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs | 5 +++-- ...iagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs | 3 ++- .../CSharp/Impl/Options/AdvancedOptionPageControl.xaml | 3 +++ .../Impl/Options/AdvancedOptionPageControl.xaml.cs | 1 + .../CSharp/Impl/Options/AdvancedOptionPageStrings.cs | 3 +++ src/VisualStudio/Core/Def/ServicesVSResources.resx | 3 +++ src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs | 10 ++++++++-- .../Core/Def/xlf/ServicesVSResources.cs.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.de.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.es.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.fr.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.it.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.ja.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.ko.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.pl.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.pt-BR.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.ru.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.tr.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.zh-Hans.xlf | 5 +++++ .../Core/Def/xlf/ServicesVSResources.zh-Hant.xlf | 5 +++++ .../Impl/Options/AdvancedOptionPageControl.xaml | 2 ++ .../Impl/Options/AdvancedOptionPageControl.xaml.vb | 1 + .../Impl/Options/AdvancedOptionPageStrings.vb | 3 +++ 24 files changed, 100 insertions(+), 6 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs index cca8fb8c55d9d..93d3920263974 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs @@ -20,8 +20,13 @@ internal sealed class DiagnosticOptions : IOptionProvider FeatureName, nameof(LspPullDiagnosticsFeatureFlag), defaultValue: false, new FeatureFlagStorageLocation("Lsp.PullDiagnostics")); + public static readonly Option2 LogTelemetryForBackgroundAnalyzerExecution = new( + FeatureName, nameof(LogTelemetryForBackgroundAnalyzerExecution), defaultValue: true, + new RoamingProfileStorageLocation($"TextEditor.Specific.LogTelemetryForBackgroundAnalyzerExecution")); + public ImmutableArray Options { get; } = ImmutableArray.Create( - LspPullDiagnosticsFeatureFlag); + LspPullDiagnosticsFeatureFlag, + LogTelemetryForBackgroundAnalyzerExecution); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs index e5637e7d681cb..05d6752f7c583 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.Executor.cs @@ -107,7 +107,7 @@ static bool IsAnalyzerEnabledForDocument( /// Computes all local diagnostics (syntax, semantic) that belong to given document for the given StateSet (analyzer). /// private static async Task ComputeDocumentAnalysisDataAsync( - DocumentAnalysisExecutor executor, StateSet stateSet, CancellationToken cancellationToken) + DocumentAnalysisExecutor executor, StateSet stateSet, bool logTelemetry, CancellationToken cancellationToken) { var kind = executor.AnalysisScope.Kind; var document = executor.AnalysisScope.TextDocument; @@ -115,7 +115,8 @@ private static async Task ComputeDocumentAnalysisDataAsync // get log title and functionId GetLogFunctionIdAndTitle(kind, out var functionId, out var title); - using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, stateSet.Analyzer, cancellationToken)) + var logLevel = logTelemetry ? LogLevel.Information : LogLevel.Trace; + using (Logger.LogBlock(functionId, GetDocumentLogMessage, title, document, stateSet.Analyzer, cancellationToken, logLevel: logLevel)) { try { diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index ec3d5c385fea6..f68c18a77ed04 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -84,9 +84,10 @@ private async Task AnalyzeDocumentForKindAsync(TextDocument document, AnalysisKi { var analysisScope = new DocumentAnalysisScope(document, span: null, nonCachedStateSets.SelectAsArray(s => s.Analyzer), kind); var executor = new DocumentAnalysisExecutor(analysisScope, compilationWithAnalyzers, _diagnosticAnalyzerRunner, logPerformanceInfo: true, onAnalysisException: OnAnalysisException); + var logTelemetry = document.Project.Solution.Options.GetOption(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); foreach (var stateSet in nonCachedStateSets) { - var computedData = await ComputeDocumentAnalysisDataAsync(executor, stateSet, cancellationToken).ConfigureAwait(false); + var computedData = await ComputeDocumentAnalysisDataAsync(executor, stateSet, logTelemetry, cancellationToken).ConfigureAwait(false); PersistAndRaiseDiagnosticsIfNeeded(computedData, stateSet); } } diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml index 45a1797761567..ab64d6b02b5aa 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml @@ -57,6 +57,9 @@ + + { // If the option has not been set by the user, check if the option to remove unused references diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs index a2c8199f320d2..a19c50fdca874 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs @@ -301,6 +301,9 @@ public static string Option_Enable_file_logging_for_diagnostics public static string Option_Skip_analyzers_for_implicitly_triggered_builds => ServicesVSResources.Skip_analyzers_for_implicitly_triggered_builds; + public static string Option_Log_telemetry_for_background_code_analysis + => ServicesVSResources.Log_telemetry_for_background_code_analysis; + public static string Show_inheritance_margin => ServicesVSResources.Show_inheritance_margin; diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index f559c9ccd768b..79bd32a930656 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1642,6 +1642,9 @@ Additional information: {1} Skip analyzers for implicitly triggered builds + + Log telemetry for background code analysis + This action cannot be undone. Do you wish to continue? diff --git a/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs b/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs index aa066c8cc3732..757d1631f4a1b 100644 --- a/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs +++ b/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs @@ -132,7 +132,7 @@ public void LogBlockEnd(FunctionId functionId, LogMessage logMessage, int blockI Contract.ThrowIfFalse(_pendingScopes.TryRemove(blockId, out var scope)); var endEvent = GetEndEvent(scope); - SetProperties(endEvent, functionId, logMessage); + SetProperties(endEvent, functionId, logMessage, delta); var result = cancellationToken.IsCancellationRequested ? TelemetryResult.UserCancel : TelemetryResult.Success; @@ -157,7 +157,7 @@ private static LogType GetKind(LogMessage logMessage) _ => LogType.Trace }; - private static void SetProperties(TelemetryEvent telemetryEvent, FunctionId functionId, LogMessage logMessage) + private static void SetProperties(TelemetryEvent telemetryEvent, FunctionId functionId, LogMessage logMessage, int? delta = null) { if (logMessage is KeyValueLogMessage kvLogMessage) { @@ -172,6 +172,12 @@ private static void SetProperties(TelemetryEvent telemetryEvent, FunctionId func telemetryEvent.Properties.Add(propertyName, message); } } + + if (delta.HasValue) + { + var propertyName = GetPropertyName(functionId, "Delta"); + telemetryEvent.Properties.Add(propertyName, delta.Value); + } } private static void AppendProperties(TelemetryEvent telemetryEvent, FunctionId functionId, KeyValueLogMessage logMessage) diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index fde17d09bb2ff..4ed62d0a6a99c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -607,6 +607,11 @@ Lokalita + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Nastavit {0} jako abstraktní diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index 4c4d815654aea..d96b1636b6cca 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -607,6 +607,11 @@ Speicherort + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract "{0}" als abstrakt festlegen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index fbe7daf4e4773..c92bb90ab735c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -607,6 +607,11 @@ Ubicación + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Convertir "{0}" en abstracto diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index 70a5e8690af1c..dbbf1f256d22a 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -607,6 +607,11 @@ Emplacement + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Rendre '{0}' abstrait diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index 0bc4a017089f2..2388f0fc149ce 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -607,6 +607,11 @@ Posizione + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Rendi astratto '{0}' diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 9b036cbe8215e..02d73d488d2a9 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -607,6 +607,11 @@ 場所 + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract '{0}' を抽象化する diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 30748321b0139..1d1d17cfffa7a 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -607,6 +607,11 @@ 위치 + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract '{0}'을(를) 추상으로 지정 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index f154ae6b133dc..6a57d16342992 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -607,6 +607,11 @@ Lokalizacja + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Ustaw element „{0}” jako abstrakcyjny diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 9e9816d55afa5..7ac5f5113c2da 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -607,6 +607,11 @@ Localização + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Fazer '{0}' abstrato diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 5e7eedb7924d7..7d57e5192c3a2 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -607,6 +607,11 @@ Расположение + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract Сделать "{0}" абстрактным diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index 45a919583d162..e3cb18fabdfcc 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -607,6 +607,11 @@ Konum + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract '{0}' değerini soyut yap diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index ede0d82c1c12f..12dcc268e6f01 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -607,6 +607,11 @@ 位置 + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract 将“{0}”设为抽象 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index dbfe591f75e96..e23c9df784a04 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -607,6 +607,11 @@ 位置 + + Log telemetry for background code analysis + Log telemetry for background code analysis + + Make '{0}' abstract 將 '{0}' 抽象化 diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml index 7c6481c63c970..b2790a6a34d2e 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml @@ -45,6 +45,8 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Enable_file_logging_for_diagnostics}" /> + Date: Thu, 10 Feb 2022 16:22:21 -0800 Subject: [PATCH 022/187] Port AbstractUpdateProjectTest and derived to the new integration test project Closes #44301 --- .../CSharpUpdateProjectToAllowUnsafe.cs | 111 ----------------- .../AbstractUpgradeProjectTest.cs} | 22 ++-- .../CSharpUpdateProjectToAllowUnsafe.cs | 114 ++++++++++++++++++ .../CSharp/CSharpUpgradeProject.cs | 95 ++++++++------- .../InProcess/SolutionExplorerInProcess.cs | 43 ++++++- .../InProcess/SolutionExplorer_InProc.cs | 12 -- .../SolutionExplorer_OutOfProc.cs | 6 - 7 files changed, 210 insertions(+), 193 deletions(-) delete mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs rename src/VisualStudio/IntegrationTest/{IntegrationTests/AbstractUpdateProjectTest.cs => New.IntegrationTests/AbstractUpgradeProjectTest.cs} (67%) create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs rename src/VisualStudio/IntegrationTest/{IntegrationTests => New.IntegrationTests}/CSharp/CSharpUpgradeProject.cs (51%) diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs deleted file mode 100644 index 7856319c5d6f9..0000000000000 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; - -namespace Roslyn.VisualStudio.IntegrationTests.CSharp -{ - [Collection(nameof(SharedIntegrationHostFixture))] - public class CSharpUpdateProjectToAllowUnsafe : AbstractUpdateProjectTest - { - public CSharpUpdateProjectToAllowUnsafe(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory) - { - } - - private void InvokeFix() - { - VisualStudio.Editor.SetText(@" -unsafe class C -{ -}"); - VisualStudio.Editor.Activate(); - - VisualStudio.Editor.PlaceCaret("C"); - VisualStudio.Editor.InvokeCodeActionList(); - VisualStudio.Editor.Verify.CodeAction("Allow unsafe code in this project", applyFix: true); - } - - [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/44301"), Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] - public void CPSProject_GeneralPropertyGroupUpdated() - { - var project = new ProjectUtils.Project(ProjectName); - - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp); - VisualStudio.SolutionExplorer.RestoreNuGetPackages(project); - - InvokeFix(); - VerifyPropertyOutsideConfiguration(GetProjectFileElement(project), "AllowUnsafeBlocks", "true"); - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] - public void LegacyProject_AllConfigurationsUpdated() - { - var project = new ProjectUtils.Project(ProjectName); - - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp); - - InvokeFix(); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "AllowUnsafeBlocks", "true"); - } - - [WorkItem(23342, "https://github.com/dotnet/roslyn/issues/23342")] - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] - public void LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() - { - var project = new ProjectUtils.Project(ProjectName); - - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddCustomProject(project, ".csproj", $@" - - - - Debug - x64 - {{F4233BA4-A4CB-498B-BBC1-65A42206B1BA}} - Library - {ProjectName} - {ProjectName} - v4.6 - - - bin\x86\Debug\ - x86 - - - bin\x86\Release\ - x86 - true - - - bin\x64\Debug\ - x64 - false - - - bin\x64\Release\ - x64 - - - - -"); - - VisualStudio.SolutionExplorer.AddFile(project, "C.cs", open: true); - - InvokeFix(); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "AllowUnsafeBlocks", "true"); - } - } -} diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/AbstractUpdateProjectTest.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/AbstractUpgradeProjectTest.cs similarity index 67% rename from src/VisualStudio/IntegrationTest/IntegrationTests/AbstractUpdateProjectTest.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/AbstractUpgradeProjectTest.cs index d91c9891241ae..abd8a1dcc6177 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/AbstractUpdateProjectTest.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/AbstractUpgradeProjectTest.cs @@ -2,31 +2,23 @@ // 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.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; -using Microsoft.VisualStudio.IntegrationTest.Utilities; using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; namespace Roslyn.VisualStudio.IntegrationTests { - public abstract class AbstractUpdateProjectTest : AbstractIntegrationTest + public abstract class AbstractUpgradeProjectTest : AbstractIntegrationTest { - protected AbstractUpdateProjectTest(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory) - { - } - - protected XElement GetProjectFileElement(ProjectUtils.Project project) + protected async Task GetProjectFileElementAsync(string projectName, CancellationToken cancellationToken) { // Save the project file. - VisualStudio.SolutionExplorer.SaveAll(); + await TestServices.SolutionExplorer.SaveAllAsync(cancellationToken); - var projectFileContent = VisualStudio.SolutionExplorer.GetFileContents(project, Path.GetFileName(project.RelativePath)); + var projectFileContent = await TestServices.SolutionExplorer.GetFileContentsAsync(projectName, $"{ProjectName}.csproj", cancellationToken); return XElement.Parse(projectFileContent); } @@ -50,7 +42,7 @@ static bool IsConditionalPropertyGroup(XElement element) => element.Name.LocalName == "PropertyGroup" && element.Attributes().Any(a => a.Name.LocalName == "Condition"); } - private static string GetPropertyValue(XElement propertyGroup, string propertyName) + private static string? GetPropertyValue(XElement propertyGroup, string propertyName) => propertyGroup.Elements().SingleOrDefault(e => e.Name.LocalName == propertyName)?.Value; } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs new file mode 100644 index 0000000000000..c297804d640d5 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpdateProjectToAllowUnsafe.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.CSharp +{ + [Trait(Traits.Feature, Traits.Features.CodeActionsUpdateProjectToAllowUnsafe)] + public class CSharpUpdateProjectToAllowUnsafe : AbstractUpgradeProjectTest + { + private async Task InvokeFixAsync(CancellationToken cancellationToken) + { + await TestServices.Editor.SetTextAsync(@" +unsafe class C +{ +}", cancellationToken); + await TestServices.Editor.ActivateAsync(cancellationToken); + + await TestServices.Editor.PlaceCaretAsync("C", charsOffset: 0, cancellationToken); + + // Suspend file change notification during code action application, since spurious file change notifications + // can cause silent failure to apply the code action if they occur within this block. + await using (var fileChangeRestorer = await TestServices.Shell.PauseFileChangesAsync(HangMitigatingCancellationToken)) + { + await TestServices.Editor.InvokeCodeActionListAsync(cancellationToken); + await TestServices.EditorVerifier.CodeActionAsync("Allow unsafe code in this project", applyFix: true, cancellationToken: cancellationToken); + } + } + + [IdeFact] + public async Task CPSProject_GeneralPropertyGroupUpdated() + { + var project = ProjectName; + + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(project, HangMitigatingCancellationToken); + + await InvokeFixAsync(HangMitigatingCancellationToken); + VerifyPropertyOutsideConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "AllowUnsafeBlocks", "true"); + } + + [IdeFact] + public async Task LegacyProject_AllConfigurationsUpdated() + { + var project = ProjectName; + + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(project, WellKnownProjectTemplates.ClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); + + await InvokeFixAsync(HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "AllowUnsafeBlocks", "true"); + } + + [IdeFact] + [WorkItem(23342, "https://github.com/dotnet/roslyn/issues/23342")] + public async Task LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() + { + var project = ProjectName; + + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddCustomProjectAsync( + project, + ".csproj", + $@" + + + + Debug + x64 + {{F4233BA4-A4CB-498B-BBC1-65A42206B1BA}} + Library + {ProjectName} + {ProjectName} + v4.6 + + + bin\x86\Debug\ + x86 + + + bin\x86\Release\ + x86 + true + + + bin\x64\Debug\ + x64 + false + + + bin\x64\Release\ + x64 + + + + +", + HangMitigatingCancellationToken); + + await TestServices.SolutionExplorer.AddFileAsync(project, "C.cs", open: true, cancellationToken: HangMitigatingCancellationToken); + + await InvokeFixAsync(HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "AllowUnsafeBlocks", "true"); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpgradeProject.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpgradeProject.cs similarity index 51% rename from src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpgradeProject.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpgradeProject.cs index c604b8acee107..0f336ef86fa7e 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpUpgradeProject.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpUpgradeProject.cs @@ -2,58 +2,60 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.IntegrationTest.Utilities; using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; namespace Roslyn.VisualStudio.IntegrationTests.CSharp { - [Collection(nameof(SharedIntegrationHostFixture))] - public class CSharpUpgradeProject : AbstractUpdateProjectTest + [Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] + public class CSharpUpgradeProject : AbstractUpgradeProjectTest { - public CSharpUpgradeProject(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory) + private async Task InvokeFixAsync(string version, CancellationToken cancellationToken) { - } - - private void InvokeFix(string version = "latest") - { - VisualStudio.Editor.SetText(@$" + await TestServices.Editor.SetTextAsync(@$" #error version:{version} -"); - VisualStudio.Editor.Activate(); +", cancellationToken); + await TestServices.Editor.ActivateAsync(cancellationToken); + + await TestServices.Editor.PlaceCaretAsync($"version:{version}", charsOffset: 0, cancellationToken); - VisualStudio.Editor.PlaceCaret($"version:{version}"); - VisualStudio.Editor.InvokeCodeActionList(); - VisualStudio.Editor.Verify.CodeAction($"Upgrade this project to C# language version '{version}'", applyFix: true); + // Suspend file change notification during code action application, since spurious file change notifications + // can cause silent failure to apply the code action if they occur within this block. + await using (var fileChangeRestorer = await TestServices.Shell.PauseFileChangesAsync(HangMitigatingCancellationToken)) + { + await TestServices.Editor.InvokeCodeActionListAsync(cancellationToken); + await TestServices.EditorVerifier.CodeActionAsync($"Upgrade this project to C# language version '{version}'", applyFix: true, cancellationToken: cancellationToken); + } } - [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/38301"), Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] - public void CPSProject_GeneralPropertyGroupUpdated() + [IdeFact(Skip = "https://github.com/dotnet/roslyn/issues/38301")] + public async Task CPSProject_GeneralPropertyGroupUpdated() { - var project = new ProjectUtils.Project(ProjectName); + var project = ProjectName; - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddProject(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp); - VisualStudio.SolutionExplorer.RestoreNuGetPackages(project); + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddProjectAsync(project, WellKnownProjectTemplates.CSharpNetStandardClassLibrary, LanguageNames.CSharp, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.RestoreNuGetPackagesAsync(project, HangMitigatingCancellationToken); - InvokeFix(); - VerifyPropertyOutsideConfiguration(GetProjectFileElement(project), "LangVersion", "latest"); + await InvokeFixAsync(version: "latest", HangMitigatingCancellationToken); + VerifyPropertyOutsideConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "LangVersion", "latest"); } - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] - public void LegacyProject_AllConfigurationsUpdated() + [IdeFact] + public async Task LegacyProject_AllConfigurationsUpdated() { - var project = new ProjectUtils.Project(ProjectName); + var project = ProjectName; - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddCustomProject(project, ".csproj", $@" + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddCustomProjectAsync( + project, + ".csproj", + $@" @@ -85,21 +87,25 @@ public void LegacyProject_AllConfigurationsUpdated() -"); - VisualStudio.SolutionExplorer.AddFile(project, "C.cs", open: true); +", + HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddFileAsync(project, "C.cs", open: true, cancellationToken: HangMitigatingCancellationToken); - InvokeFix(version: "7.3"); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "LangVersion", "7.3"); + await InvokeFixAsync(version: "7.3", HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "LangVersion", "7.3"); } + [IdeFact] [WorkItem(23342, "https://github.com/dotnet/roslyn/issues/23342")] - [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUpgradeProject)] - public void LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() + public async Task LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() { - var project = new ProjectUtils.Project(ProjectName); + var project = ProjectName; - VisualStudio.SolutionExplorer.CreateSolution(SolutionName); - VisualStudio.SolutionExplorer.AddCustomProject(project, ".csproj", $@" + await TestServices.SolutionExplorer.CreateSolutionAsync(SolutionName, HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.AddCustomProjectAsync( + project, + ".csproj", + $@" @@ -134,12 +140,13 @@ public void LegacyProject_MultiplePlatforms_AllConfigurationsUpdated() -"); +", + HangMitigatingCancellationToken); - VisualStudio.SolutionExplorer.AddFile(project, "C.cs", open: true); + await TestServices.SolutionExplorer.AddFileAsync(project, "C.cs", open: true, cancellationToken: HangMitigatingCancellationToken); - InvokeFix(version: "7.3"); - VerifyPropertyInEachConfiguration(GetProjectFileElement(project), "LangVersion", "7.3"); + await InvokeFixAsync(version: "7.3", HangMitigatingCancellationToken); + VerifyPropertyInEachConfiguration(await GetProjectFileElementAsync(project, HangMitigatingCancellationToken), "LangVersion", "7.3"); } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index 38aaff963ecbf..dee04f7fdc0d3 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.IntegrationTest.Utilities; +using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; @@ -151,6 +152,20 @@ public async Task AddProjectAsync(string projectName, string projectTemplate, st ErrorHandler.ThrowOnFailure(solution.AddNewProjectFromTemplate(projectTemplatePath, null, null, projectPath, projectName, null, out _)); } + public async Task AddCustomProjectAsync(string projectName, string projectFileExtension, string projectFileContent, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var projectPath = Path.Combine(await GetDirectoryNameAsync(cancellationToken), projectName); + Directory.CreateDirectory(projectPath); + + var projectFilePath = Path.Combine(projectPath, projectName + projectFileExtension); + File.WriteAllText(projectFilePath, projectFileContent); + + var solution = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(solution.AddExistingProject(projectFilePath, pParent: null, out _)); + } + public async Task RestoreNuGetPackagesAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -189,6 +204,14 @@ await Helper.RetryAsync( cancellationToken); } + public async Task SaveAllAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd97CmdID).GUID, (uint)VSConstants.VSStd97CmdID.SaveSolution, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + } + public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -340,6 +363,19 @@ public async Task GetFileContentsAsync(string projectName, string relati return File.ReadAllText(filePath); } + public async Task<(string solutionDirectory, string solutionFileFullPath, string userOptionsFile)> GetSolutionInfoAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + if (!await IsSolutionOpenAsync(cancellationToken)) + throw new InvalidOperationException("No solution is open."); + + var solution = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out var solutionDirectory, out var solutionFileFullPath, out var userOptionsFile)); + + return (solutionDirectory, solutionFileFullPath, userOptionsFile); + } + private static string ConvertLanguageName(string languageName) { return languageName switch @@ -365,16 +401,13 @@ private async Task GetAbsolutePathForProjectRelativeFilePathAsync(string private async Task GetDirectoryNameAsync(CancellationToken cancellationToken) { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var solution = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out _, out var solutionFileFullPath, out _)); + var (solutionDirectory, solutionFileFullPath, _) = await GetSolutionInfoAsync(cancellationToken); if (string.IsNullOrEmpty(solutionFileFullPath)) { throw new InvalidOperationException(); } - return Path.GetDirectoryName(solutionFileFullPath); + return solutionDirectory; } private async Task GetProjectTemplatePathAsync(string projectTemplate, string languageName, CancellationToken cancellationToken) diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs index 55cd499e9725d..8c62995cbdbe1 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/SolutionExplorer_InProc.cs @@ -332,18 +332,6 @@ public void AddProject(string projectName, string projectTemplate, string langua _solution.AddFromTemplate(projectTemplatePath, projectPath, projectName, Exclusive: false); } - public void AddCustomProject(string projectName, string projectFileExtension, string projectFileContent) - { - var projectPath = Path.Combine(DirectoryName, projectName); - Directory.CreateDirectory(projectPath); - - var projectFilePath = Path.Combine(projectPath, projectName + projectFileExtension); - File.WriteAllText(projectFilePath, projectFileContent); - - Contract.ThrowIfNull(_solution); - _solution.AddFromFile(projectFilePath); - } - // TODO: Adjust language name based on whether we are using a web template private string GetProjectTemplatePath(string projectTemplate, string languageName) { diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs index 7ed4ac8ef86fc..fb16e4f4fd34f 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/SolutionExplorer_OutOfProc.cs @@ -47,12 +47,6 @@ public void AddProject(ProjectUtils.Project projectName, string projectTemplate, _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Workspace); } - public void AddCustomProject(ProjectUtils.Project projectName, string projectFileExtension, string projectFileContent) - { - _inProc.AddCustomProject(projectName.Name, projectFileExtension, projectFileContent); - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Workspace); - } - public void AddProjectReference(ProjectUtils.Project fromProjectName, ProjectUtils.ProjectReference toProjectName) { _inProc.AddProjectReference(fromProjectName.Name, toProjectName.Name); From 8950d604b3f67c1cb479740b40e591083cde31c5 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 10 Feb 2022 23:50:23 -0800 Subject: [PATCH 023/187] Port CSharpGoToBase to the new integration test project --- .../IntegrationTests/CSharp/CSharpGoToBase.cs | 51 ------------------- .../CSharp/CSharpGoToBase.cs | 44 ++++++++++++++++ .../InProcess/EditorInProcess.cs | 13 +++++ .../TestUtilities/InProcess/Editor_InProc.cs | 3 -- .../OutOfProcess/Editor_OutOfProc.cs | 8 --- 5 files changed, 57 insertions(+), 62 deletions(-) delete mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs deleted file mode 100644 index dd22ee5c6dc46..0000000000000 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpGoToBase.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Microsoft.VisualStudio.Shell; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; -using ProjectUtils = Microsoft.VisualStudio.IntegrationTest.Utilities.Common.ProjectUtils; - -namespace Roslyn.VisualStudio.IntegrationTests.CSharp -{ - [Collection(nameof(SharedIntegrationHostFixture))] - public class CSharpGoToBase : AbstractEditorTest - { - protected override string LanguageName => LanguageNames.CSharp; - - public CSharpGoToBase(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(CSharpGoToBase)) - { - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.GoToBase)] - public void GoToBaseFromMetadataAsSource() - { - var project = new ProjectUtils.Project(ProjectName); - VisualStudio.SolutionExplorer.AddFile(project, "C.cs"); - VisualStudio.SolutionExplorer.OpenFile(project, "C.cs"); - VisualStudio.Editor.SetText( -@"using System; - -class C -{ - public override string ToString() - { - return ""C""; - } -}"); - VisualStudio.Editor.PlaceCaret("ToString", charsOffset: -1); - VisualStudio.Editor.GoToBase("Object [from metadata]"); - VisualStudio.Editor.Verify.TextContains(@"public virtual string ToString$$()", assertCaretPosition: true); - } - } -} diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs new file mode 100644 index 0000000000000..207ee11393f96 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToBase.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.CSharp +{ + [Trait(Traits.Feature, Traits.Features.GoToBase)] + public class CSharpGoToBase : AbstractEditorTest + { + protected override string LanguageName => LanguageNames.CSharp; + + public CSharpGoToBase() + : base(nameof(CSharpGoToBase)) + { + } + + [IdeFact] + public async Task GoToBaseFromMetadataAsSource() + { + await TestServices.SolutionExplorer.AddFileAsync(ProjectName, "C.cs", cancellationToken: HangMitigatingCancellationToken); + await TestServices.SolutionExplorer.OpenFileAsync(ProjectName, "C.cs", HangMitigatingCancellationToken); + await TestServices.Editor.SetTextAsync( +@"using System; + +class C +{ + public override string ToString() + { + return ""C""; + } +}", HangMitigatingCancellationToken); + await TestServices.Editor.PlaceCaretAsync("ToString", charsOffset: -1, HangMitigatingCancellationToken); + await TestServices.Editor.GoToBaseAsync(HangMitigatingCancellationToken); + Assert.Equal("Object [from metadata]", await TestServices.Shell.GetActiveWindowCaptionAsync(HangMitigatingCancellationToken)); + + await TestServices.EditorVerifier.TextContainsAsync(@"public virtual string ToString$$()", assertCaretPosition: true); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 92ece3a575b8d..632870b89dab2 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -846,6 +846,19 @@ public async Task GetCaretPositionAsync(CancellationToken cancellationToken return bufferPosition.Position; } + public async Task GoToBaseAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(dispatcher.Exec(EditorConstants.EditorCommandSet, (uint)EditorConstants.EditorCommandID.GoToBase, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.Workspace, cancellationToken); + + await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.GoToBase, cancellationToken); + await TestServices.Editor.WaitForEditorOperationsAsync(cancellationToken); + } + private async Task WaitForCompletionSetAsync(CancellationToken cancellationToken) { await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.CompletionSet, cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index 4cfe91ffe213b..22c4a7376e484 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -775,9 +775,6 @@ public void GoToDefinition() public void GoToImplementation() => ExecuteCommand(WellKnownCommandNames.Edit_GoToImplementation); - public void GoToBase() - => ExecuteCommand(WellKnownCommandNames.Edit_GoToBase); - /// /// Gets the spans where a particular tag appears in the active text view. /// diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index e7df5ae363793..e491862fda2b8 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -345,14 +345,6 @@ public void GoToImplementation(string? expectedNavigateWindowName) } } - public void GoToBase(string expectedNavigateWindowName) - { - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.Workspace); - _editorInProc.GoToBase(); - _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.GoToBase); - _editorInProc.WaitForActiveWindow(expectedNavigateWindowName); - } - public void SendExplicitFocus() => _editorInProc.SendExplicitFocus(); From 4f57804a553cbdc05dcfea585eae7e067f934344 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Fri, 11 Feb 2022 02:08:28 -0800 Subject: [PATCH 024/187] Update telemetry logger test --- .../Remote/ServiceHubTest/TelemetryLoggerTests.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs b/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs index 798670620eb89..907ae90e7ce24 100644 --- a/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs +++ b/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs @@ -59,8 +59,8 @@ protected override TelemetryEvent GetEndEvent(object scope) => ((TestScope)scope).EndEvent; } - private static IEnumerable InspectProperties(TelemetryEvent @event) - => @event.Properties.Select(p => $"{p.Key}={InspectPropertyValue(p.Value)}"); + private static IEnumerable InspectProperties(TelemetryEvent @event, string? keyToIgnoreValueInspection = null) + => @event.Properties.Select(p => $"{p.Key}={(keyToIgnoreValueInspection == p.Key ? string.Empty : InspectPropertyValue(p.Value))}"); private static string InspectPropertyValue(object? value) => value switch @@ -118,10 +118,12 @@ public void LogBlockStartEnd() Assert.Equal("vs/ide/vbcs/debugging/encsession/editsession/emitdeltaerrorid", scope.EndEvent.Name); + // We don't inspect the property value for "Delta" (time of execution) as that value will vary each time. AssertEx.Equal(new[] { - "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.test=end" - }, InspectProperties(scope.EndEvent)); + "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.test=end", + "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.delta=" + }, InspectProperties(scope.EndEvent, keyToIgnoreValueInspection: "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.delta")); } } } From f14a018129d326de4d7909614e3664862de7d171 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Fri, 11 Feb 2022 13:14:56 +0000 Subject: [PATCH 025/187] Update dependencies from https://github.com/dotnet/arcade build 20220210.7 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 7.0.0-beta.22080.1 -> To Version 7.0.0-beta.22110.7 --- eng/Version.Details.xml | 8 ++-- eng/common/cross/build-rootfs.sh | 9 +++++ eng/common/cross/ppc64le/sources.list.bionic | 11 +++++ eng/common/cross/toolchain.cmake | 7 +++- eng/common/generate-sbom-prep.ps1 | 19 +++++++++ eng/common/generate-sbom-prep.sh | 22 ++++++++++ eng/common/templates/job/job.yml | 11 +++++ eng/common/templates/jobs/jobs.yml | 5 +++ eng/common/templates/steps/generate-sbom.yml | 42 ++++++++++++++++++++ global.json | 4 +- 10 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 eng/common/cross/ppc64le/sources.list.bionic create mode 100644 eng/common/generate-sbom-prep.ps1 create mode 100644 eng/common/generate-sbom-prep.sh create mode 100644 eng/common/templates/steps/generate-sbom.yml diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 06c0284e9038b..322ad4d825000 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - 00b6cce8983065a9cf9fb108a724f28ec44eaf75 + 78eaf78761027d225030be2b28aaf4e8bf392929 https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - 00b6cce8983065a9cf9fb108a724f28ec44eaf75 + 78eaf78761027d225030be2b28aaf4e8bf392929 diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index f97dca7705408..7e4be9a0ccfbf 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -120,6 +120,15 @@ while :; do __UbuntuRepo="http://ftp.debian.org/debian/" __CodeName=jessie ;; + ppc64le) + __BuildArch=ppc64le + __UbuntuArch=ppc64el + __UbuntuRepo="http://ports.ubuntu.com/ubuntu-ports/" + __UbuntuPackages=$(echo ${__UbuntuPackages} | sed 's/ libunwind8-dev//') + __UbuntuPackages=$(echo ${__UbuntuPackages} | sed 's/ libomp-dev//') + __UbuntuPackages=$(echo ${__UbuntuPackages} | sed 's/ libomp5//') + unset __LLDB_Package + ;; s390x) __BuildArch=s390x __UbuntuArch=s390x diff --git a/eng/common/cross/ppc64le/sources.list.bionic b/eng/common/cross/ppc64le/sources.list.bionic new file mode 100644 index 0000000000000..2109557409576 --- /dev/null +++ b/eng/common/cross/ppc64le/sources.list.bionic @@ -0,0 +1,11 @@ +deb http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main restricted universe + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted + +deb http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse +deb-src http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted universe multiverse diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index fba2afda438b6..9fd345bde6d40 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -54,6 +54,9 @@ elseif(TARGET_ARCH_NAME STREQUAL "arm64") if(TIZEN) set(TIZEN_TOOLCHAIN "aarch64-tizen-linux-gnu/9.2.0") endif() +elseif(TARGET_ARCH_NAME STREQUAL "ppc64le") + set(CMAKE_SYSTEM_PROCESSOR ppc64le) + set(TOOLCHAIN "powerpc64le-linux-gnu") elseif(TARGET_ARCH_NAME STREQUAL "s390x") set(CMAKE_SYSTEM_PROCESSOR s390x) set(TOOLCHAIN "s390x-linux-gnu") @@ -67,7 +70,7 @@ elseif (ILLUMOS) set(CMAKE_SYSTEM_PROCESSOR "x86_64") set(TOOLCHAIN "x86_64-illumos") else() - message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only armel, arm, armv6, arm64, s390x and x86 are supported!") + message(FATAL_ERROR "Arch is ${TARGET_ARCH_NAME}. Only armel, arm, armv6, arm64, ppc64le, s390x and x86 are supported!") endif() if(DEFINED ENV{TOOLCHAIN}) @@ -201,7 +204,7 @@ endif() # Specify compile options -if((TARGET_ARCH_NAME MATCHES "^(arm|armv6|armel|arm64|s390x)$" AND NOT ANDROID) OR ILLUMOS) +if((TARGET_ARCH_NAME MATCHES "^(arm|armv6|armel|arm64|ppc64le|s390x)$" AND NOT ANDROID) OR ILLUMOS) set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN}) diff --git a/eng/common/generate-sbom-prep.ps1 b/eng/common/generate-sbom-prep.ps1 new file mode 100644 index 0000000000000..a733a8885824a --- /dev/null +++ b/eng/common/generate-sbom-prep.ps1 @@ -0,0 +1,19 @@ +Param( + [Parameter(Mandatory=$true)][string] $ManifestDirPath # Manifest directory where sbom will be placed +) + +Write-Host "Creating dir $ManifestDirPath" +# create directory for sbom manifest to be placed +if (!(Test-Path -path $ManifestDirPath)) +{ + New-Item -ItemType Directory -path $ManifestDirPath + Write-Host "Successfully created directory $ManifestDirPath" +} +else{ + Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." +} + +Write-Host "Updating artifact name" +$artifact_name = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM" -replace '["/:<>\\|?@*"() ]', '_' +Write-Host "Artifact name $artifact_name" +Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$artifact_name" diff --git a/eng/common/generate-sbom-prep.sh b/eng/common/generate-sbom-prep.sh new file mode 100644 index 0000000000000..f6c77453142a2 --- /dev/null +++ b/eng/common/generate-sbom-prep.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +source="${BASH_SOURCE[0]}" + +manifest_dir=$1 + +if [ ! -d "$manifest_dir" ] ; then + mkdir -p "$manifest_dir" + echo "Sbom directory created." $manifest_dir +else + Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." +fi + +artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM" +echo "Artifact name before : "$artifact_name +# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts. +safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}" +echo "Artifact name after : "$safe_artifact_name +export ARTIFACT_NAME=$safe_artifact_name +echo "##vso[task.setvariable variable=ARTIFACT_NAME]$safe_artifact_name" + +exit 0 diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 92e98af7ebd5c..e3ba9398016be 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -31,6 +31,10 @@ parameters: name: '' preSteps: [] runAsPublic: false +# Sbom related params + enableSbom: true + PackageVersion: 7.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' jobs: - job: ${{ parameters.name }} @@ -248,3 +252,10 @@ jobs: ArtifactName: AssetManifests continueOnError: ${{ parameters.continueOnError }} condition: and(succeeded(), eq(variables['_DotNetPublishToBlobFeed'], 'true')) + + - ${{ 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 }} + diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml index 70d44735ace4a..6976330862c20 100644 --- a/eng/common/templates/jobs/jobs.yml +++ b/eng/common/templates/jobs/jobs.yml @@ -41,6 +41,11 @@ parameters: # 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. +# Sbom related params + enableSbom: true + PackageVersion: 7.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + jobs: - ${{ each job in parameters.jobs }}: - template: ../job/job.yml diff --git a/eng/common/templates/steps/generate-sbom.yml b/eng/common/templates/steps/generate-sbom.yml new file mode 100644 index 0000000000000..3acba705f177c --- /dev/null +++ b/eng/common/templates/steps/generate-sbom.yml @@ -0,0 +1,42 @@ +# 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 + +parameters: + PackageVersion: 7.0.0 + BuildDropPath: '$(Build.SourcesDirectory)/artifacts' + PackageName: '.NET' + ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom + 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}} + +- script: | + ./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 }} + +- task: PublishPipelineArtifact@1 + displayName: Publish SBOM manifest + continueOnError: ${{parameters.sbomContinueOnError}} + inputs: + targetPath: '${{parameters.manifestDirPath}}' + artifactName: $(ARTIFACT_NAME) + diff --git a/global.json b/global.json index 11ee799695a6f..bab9032c1aa35 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22109.1", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22109.1" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22110.7", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22110.7" } } From a3cfe955ff6afe186576f25eb2801a3130a00311 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Sat, 12 Feb 2022 13:13:48 +0000 Subject: [PATCH 026/187] Update dependencies from https://github.com/dotnet/arcade build 20220211.10 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 7.0.0-beta.22080.1 -> To Version 7.0.0-beta.22111.10 --- eng/Version.Details.xml | 8 ++++---- eng/common/templates/steps/generate-sbom.yml | 2 ++ global.json | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 322ad4d825000..84c64000563d3 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - 78eaf78761027d225030be2b28aaf4e8bf392929 + ff6cc4e9c3eef575f62a33a642ca80e79d27c9bb https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - 78eaf78761027d225030be2b28aaf4e8bf392929 + ff6cc4e9c3eef575f62a33a642ca80e79d27c9bb diff --git a/eng/common/templates/steps/generate-sbom.yml b/eng/common/templates/steps/generate-sbom.yml index 3acba705f177c..4cea8c33187c9 100644 --- a/eng/common/templates/steps/generate-sbom.yml +++ b/eng/common/templates/steps/generate-sbom.yml @@ -18,7 +18,9 @@ steps: 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') diff --git a/global.json b/global.json index bab9032c1aa35..21705bd705307 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22110.7", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22110.7" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22111.10", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22111.10" } } From d7c407323d8cd5a8359c4ebc0ef5b32cbbfceb8c Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 21:39:57 -0800 Subject: [PATCH 027/187] Simplifications and clarifications from code review --- .../InlineDiagnosticsTaggerProviderTests.cs | 21 ++++++----------- .../Squiggles/ErrorSquiggleProducerTests.cs | 19 +++++---------- .../InlineDiagnosticsTaggerProvider.cs | 4 ++-- .../AbstractDiagnosticsTaggerProvider.cs | 14 +++++++---- .../DiagnosticAnalyzerServiceTests.cs | 23 +++++++++++-------- .../Workspaces/TestHostDocument.cs | 23 +++++++++---------- .../TestWorkspace_XmlConsumption.cs | 2 +- .../Diagnostics/DiagnosticAnalyzerService.cs | 7 ++++-- ...sticAnalyzerService_IncrementalAnalyzer.cs | 7 ++---- ...gnosticIncrementalAnalyzer.ProjectState.cs | 14 +++++++---- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 6 ++++- ...osticIncrementalAnalyzer_GetDiagnostics.cs | 7 ++++-- ...IncrementalAnalyzer_IncrementalAnalyzer.cs | 21 +++++++++++++---- .../SolutionCrawler/WorkCoordinator.cs | 6 +++-- .../WorkspacePullDiagnosticHandler.cs | 5 +--- .../Workspace/SourceGeneratedFileManager.cs | 6 ++--- .../Diagnostics/DiagnosticAnalysisResult.cs | 4 +++- .../Portable/Workspace/Workspace_Editor.cs | 5 ++-- .../SolutionWithSourceGeneratorTests.cs | 22 +++++++----------- 19 files changed, 114 insertions(+), 102 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs index e9926c48a4295..4fb1e8e6dfd76 100644 --- a/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/InlineDiagnostics/InlineDiagnosticsTaggerProviderTests.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; using System.Collections.Immutable; -using System.Linq; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis.Editor.InlineDiagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; @@ -27,9 +26,7 @@ public class InlineDiagnosticsTaggerProviderTests public async Task ErrorTagGeneratedForError() { var spans = await GetTagSpansAsync("class C {"); - Assert.Equal(1, spans.Count()); - - var firstSpan = spans.First(); + var firstSpan = Assert.Single(spans); Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); } @@ -37,9 +34,7 @@ public async Task ErrorTagGeneratedForError() public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument() { var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {"); - Assert.Equal(1, spans.Count()); - - var firstSpan = spans.First(); + var firstSpan = Assert.Single(spans); Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); } @@ -51,12 +46,10 @@ private static async Task>> GetTag private static async Task>> GetTagSpansInSourceGeneratedDocumentAsync(string content) { - var workspaceElement = $@" - - {new XText(content)} - -"; - using var workspace = TestWorkspace.Create(workspaceElement, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); + using var workspace = TestWorkspace.CreateCSharp( + files: Array.Empty(), + sourceGeneratedFiles: new[] { content }, + composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); return await GetTagSpansAsync(workspace); } diff --git a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs index 2fec6449809e5..21464164e1a7e 100644 --- a/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs +++ b/src/EditorFeatures/CSharpTest/Squiggles/ErrorSquiggleProducerTests.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CodeStyle; @@ -41,9 +40,7 @@ public class ErrorSquiggleProducerTests public async Task ErrorTagGeneratedForError() { var spans = await GetTagSpansAsync("class C {"); - Assert.Equal(1, spans.Count()); - - var firstSpan = spans.First(); + var firstSpan = Assert.Single(spans); Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); } @@ -51,9 +48,7 @@ public async Task ErrorTagGeneratedForError() public async Task ErrorTagGeneratedForErrorInSourceGeneratedDocument() { var spans = await GetTagSpansInSourceGeneratedDocumentAsync("class C {"); - Assert.Equal(1, spans.Count()); - - var firstSpan = spans.First(); + var firstSpan = Assert.Single(spans); Assert.Equal(PredefinedErrorTypeNames.SyntaxError, firstSpan.Tag.ErrorType); } @@ -365,12 +360,10 @@ private static async Task>> GetTagSpansAsync( private static async Task>> GetTagSpansInSourceGeneratedDocumentAsync(string content) { - var workspaceElement = $@" - - {new XText(content)} - -"; - using var workspace = TestWorkspace.Create(workspaceElement, composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); + using var workspace = TestWorkspace.CreateCSharp( + files: Array.Empty(), + sourceGeneratedFiles: new[] { content }, + composition: SquiggleUtilities.WpfCompositionWithSolutionCrawler); return await GetTagSpansAsync(workspace); } diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs index 4d3383525fb74..0b7da20026130 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTaggerProvider.cs @@ -79,12 +79,12 @@ diagnostic.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error && return null; } - if (diagnostic.DocumentId is not { ProjectId: var projectId }) + if (diagnostic.DocumentId is null) { return null; } - var project = workspace.CurrentSolution.GetProject(projectId); + var project = workspace.CurrentSolution.GetProject(diagnostic.DocumentId.ProjectId); if (project is null) { return null; diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs index a9689a92b7e62..41bf1776b403d 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -75,10 +75,16 @@ private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) return; } - // TryGetSourceGeneratedDocumentForAlreadyGeneratedId is safe to use here because the only way to report - // a diagnostic in a source generated document is for the source generated document to exist. - var document = e.Solution.GetDocument(e.DocumentId) - ?? e.Solution.GetProject(e.DocumentId.ProjectId)?.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(e.DocumentId); + var document = e.Solution.GetDocument(e.DocumentId); + + // If we couldn't find a normal document, and all features are enabled for source generated documents, + // attempt to locate a matching source generated document in the project. + if (document is null + && e.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true } + && e.Solution.GetProject(e.DocumentId.ProjectId) is { } project) + { + document = ThreadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentAsync(e.DocumentId, CancellationToken.None).AsTask()); + } // Open documents *should* always have their SourceText available, but we cannot guarantee // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index d11ccf8dde4c2..a2d96e5bff6f2 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -7,10 +7,8 @@ using System; using System.Collections.Immutable; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; -using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions; using Microsoft.CodeAnalysis.Diagnostics; @@ -22,7 +20,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote.Diagnostics; -using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; @@ -838,14 +835,20 @@ void M() "; } - var documentElementName = isSourceGenerated ? "DocumentFromSourceGenerator" : "Document"; - var workspaceElement = $@" - - <{documentElementName} FilePath=""test1.cs"">{new XText(code)} - -"; + string[] files; + string[] sourceGeneratedFiles; + if (isSourceGenerated) + { + files = Array.Empty(); + sourceGeneratedFiles = new[] { code }; + } + else + { + files = new[] { code }; + sourceGeneratedFiles = Array.Empty(); + } - using var workspace = TestWorkspace.Create(workspaceElement, composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService))); + using var workspace = TestWorkspace.CreateCSharp(files, sourceGeneratedFiles, composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService))); var options = workspace.Options.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope); workspace.SetOptions(options); diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs index a8f4a24a6dc35..20cc1693af52e 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestHostDocument.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -16,7 +16,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Roslyn.Test.Utilities; -using Roslyn.Test.Utilities.TestGenerators; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces @@ -96,10 +95,16 @@ public TestHostProject Project /// public bool IsLinkFile { get; } + /// + /// If this is a source generated file, the source generator that produced this document. + /// + public ISourceGenerator? Generator; + /// /// Returns true if this will be a source generated file instead of a regular one. /// - public bool IsSourceGenerated { get; } + [MemberNotNullWhen(true, nameof(Generator))] + public bool IsSourceGenerated => Generator is not null; internal TestHostDocument( ExportProvider exportProvider, @@ -115,7 +120,7 @@ internal TestHostDocument( IDocumentServiceProvider? documentServiceProvider = null, ImmutableArray roles = default, ITextBuffer2? textBuffer = null, - bool isSourceGenerated = false) + ISourceGenerator? generator = null) { Contract.ThrowIfNull(filePath); @@ -128,7 +133,7 @@ internal TestHostDocument( this.CursorPosition = cursorPosition; SourceCodeKind = sourceCodeKind; this.IsLinkFile = isLinkFile; - IsSourceGenerated = isSourceGenerated; + Generator = generator; _documentServiceProvider = documentServiceProvider; _roles = roles.IsDefault ? s_defaultRoles : roles; @@ -266,17 +271,11 @@ public ITextBuffer2 GetTextBuffer() { if (testDocument.IsSourceGenerated) { - var hintName = testDocument.Name; - var generator = new SingleFileTestGenerator(); - var generatorAssemblyName = SourceGeneratedDocumentIdentity.GetGeneratorAssemblyName(generator); - var generatorTypeName = SourceGeneratedDocumentIdentity.GetGeneratorTypeName(generator); - var filePath = testDocument.FilePath ?? throw new InvalidOperationException(); - var threadingContext = workspace.GetService(); var document = threadingContext.JoinableTaskFactory.Run(() => workspace.CurrentSolution.GetSourceGeneratedDocumentAsync(testDocument.Id, CancellationToken.None).AsTask()); Contract.ThrowIfNull(document); - workspace.OnSourceGeneratedDocumentOpened(new SourceGeneratedDocumentIdentity(linkedId, hintName, generatorAssemblyName, generatorTypeName, filePath), _textBuffer.AsTextContainer(), document); + workspace.OnSourceGeneratedDocumentOpened(_textBuffer.AsTextContainer(), document); } else { diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs index d9bd483a37eda..eb2463651c0bd 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs @@ -353,7 +353,7 @@ private static TestHostProject CreateProject( out var code, out var cursorPosition, out IDictionary> spans); var documentFilePath = typeof(SingleFileTestGenerator).Assembly.GetName().Name + '\\' + typeof(SingleFileTestGenerator).FullName + '\\' + name; - var document = new TestHostDocument(exportProvider, languageServices, code, name, documentFilePath, cursorPosition, spans, isSourceGenerated: true); + var document = new TestHostDocument(exportProvider, languageServices, code, name, documentFilePath, cursorPosition, spans, generator: testGenerator); documents.Add(document); testGenerator.AddSource(code, name); diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs index 009dc9ce00d5b..dfff67117aba0 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -15,6 +14,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -35,6 +35,7 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService public IAsynchronousOperationListener Listener { get; } + private readonly IWorkspaceThreadingService? _workspaceThreadingService; private readonly ConditionalWeakTable _map; private readonly ConditionalWeakTable.CreateValueCallback _createIncrementalAnalyzer; @@ -42,11 +43,13 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public DiagnosticAnalyzerService( IDiagnosticUpdateSourceRegistrationService registrationService, - IAsynchronousOperationListenerProvider listenerProvider) + IAsynchronousOperationListenerProvider listenerProvider, + [Import(AllowDefault = true)] IWorkspaceThreadingService? workspaceThreadingService) { AnalyzerInfoCache = new DiagnosticAnalyzerInfoCache(); Listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService); + _workspaceThreadingService = workspaceThreadingService; _map = new ConditionalWeakTable(); _createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback; _eventMap = new EventMap(); diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index 647c9d0dbc23e..b0cb7ab3ff507 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.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 Microsoft.CodeAnalysis.Diagnostics.EngineV2; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.Options; using Microsoft.CodeAnalysis.SolutionCrawler; using Roslyn.Utilities; @@ -37,10 +34,10 @@ private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspac // subscribe to active context changed event for new workspace workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - return new DiagnosticIncrementalAnalyzer(this, LogAggregator.GetNextId(), workspace, AnalyzerInfoCache); + return new DiagnosticIncrementalAnalyzer(this, LogAggregator.GetNextId(), workspace, AnalyzerInfoCache, _workspaceThreadingService); } - private void OnDocumentActiveContextChanged(object sender, DocumentActiveContextChangedEventArgs e) + private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) => Reanalyze(e.Solution.Workspace, documentIds: SpecializedCollections.SingletonEnumerable(e.NewActiveContextDocumentId), highPriority: true); } } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs index ea349ae83548b..8fd704ffd37e8 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.ProjectState.cs @@ -199,10 +199,16 @@ public async ValueTask SaveToInMemoryStorageAsync(Project project, DiagnosticAna var serializerVersion = result.Version; foreach (var documentId in result.DocumentIds) { - // TryGetSourceGeneratedDocumentForAlreadyGeneratedId is safe to use here because the only way to - // report a diagnostic in a source generated document is for the source generated document to exist. - var document = project.GetTextDocument(documentId) - ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); + var document = project.GetTextDocument(documentId); + + // If we couldn't find a normal document, and all features are enabled for source generated + // documents, attempt to locate a matching source generated document in the project. + if (document is null + && project.Solution.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true }) + { + document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); + } + if (document == null) { // it can happen with build synchronization since, in build case, diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 7d30d7e4fba0d..452bb3809cb45 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -33,6 +34,7 @@ internal partial class DiagnosticIncrementalAnalyzer : IIncrementalAnalyzer2 private readonly StateManager _stateManager; private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; private readonly IDocumentTrackingService _documentTrackingService; + private readonly IWorkspaceThreadingService? _workspaceThreadingService; private ConditionalWeakTable _projectCompilationsWithAnalyzers; internal DiagnosticAnalyzerService AnalyzerService { get; } @@ -43,12 +45,14 @@ public DiagnosticIncrementalAnalyzer( DiagnosticAnalyzerService analyzerService, int correlationId, Workspace workspace, - DiagnosticAnalyzerInfoCache analyzerInfoCache) + DiagnosticAnalyzerInfoCache analyzerInfoCache, + IWorkspaceThreadingService? workspaceThreadingService) { Contract.ThrowIfNull(analyzerService); AnalyzerService = analyzerService; Workspace = workspace; + _workspaceThreadingService = workspaceThreadingService; _documentTrackingService = workspace.Services.GetRequiredService(); _correlationId = correlationId; diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs index 570e2742d12ce..f232bf6d70564 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_GetDiagnostics.cs @@ -204,8 +204,11 @@ private static async Task> GetProjectStateDiagnos if (documentId != null) { // file doesn't exist in current solution - var document = project.Solution.GetDocument(documentId) - ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); + var document = await project.Solution.GetDocumentAsync( + documentId, + includeSourceGenerated: project.Solution.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true }, + cancellationToken).ConfigureAwait(false); + if (document == null) { return ImmutableArray.Empty; diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 9ab2e0d9241aa..db61515e76889 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -535,10 +535,23 @@ private void RaiseProjectDiagnosticsCreated(Project project, StateSet stateSet, foreach (var documentId in newAnalysisResult.DocumentIds) { - // TryGetSourceGeneratedDocumentForAlreadyGeneratedId is safe to use here because the only way to report - // a diagnostic in a source generated document is for the source generated document to exist. - var document = project.GetTextDocument(documentId) - ?? project.TryGetSourceGeneratedDocumentForAlreadyGeneratedId(documentId); + var document = project.GetTextDocument(documentId); + + // If we couldn't find a normal document, and all features are enabled for source generated documents, + // attempt to locate a matching source generated document in the project. + if (document is null + && project.Solution.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true }) + { + if (_workspaceThreadingService is not null) + { + document = _workspaceThreadingService.Run(() => project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).AsTask()); + } + else + { + document = project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).AsTask().WaitAndGetResult_CanCallOnBackground(CancellationToken.None); + } + } + if (document == null) { // it can happen with build synchronization since, in build case, diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index b4f8b916ab1f2..af319e0270fbb 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -431,8 +431,10 @@ private void EnqueueDocumentChangedEvent(Solution oldSolution, Solution newSolut // If all features are enabled for source generated documents, the solution crawler needs to // include them in incremental analysis. - if (_syntaxTreeConfigurationService?.EnableOpeningSourceGeneratedFilesInWorkspace != false) + if (_syntaxTreeConfigurationService is { EnableOpeningSourceGeneratedFilesInWorkspace: true }) { + // TODO: if this becomes a hot spot, we should be able to expose/access the dictionary + // underneath GetSourceGeneratedDocumentsAsync rather than create a new one here. var oldProjectSourceGeneratedDocuments = await oldProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); var oldProjectSourceGeneratedDocumentsById = oldProjectSourceGeneratedDocuments.ToDictionary(static document => document.Id); var newProjectSourceGeneratedDocuments = await newProject.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false); @@ -517,7 +519,7 @@ private async Task EnqueueFullProjectWorkItemAsync(Project project, InvocationRe // If all features are enabled for source generated documents, the solution crawler needs to // include them in incremental analysis. - if (_syntaxTreeConfigurationService?.EnableOpeningSourceGeneratedFilesInWorkspace != false) + if (_syntaxTreeConfigurationService is { EnableOpeningSourceGeneratedFilesInWorkspace: true }) { foreach (var document in await project.GetSourceGeneratedDocumentsAsync(_shutdownToken).ConfigureAwait(false)) await EnqueueDocumentWorkItemAsync(project, document.Id, document, invocationReasons).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index 6d4d95b66623f..6026835647f00 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.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.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -132,8 +130,7 @@ async Task AddDocumentsFromProjectAsync(Project? project, ImmutableArray // documents from it. If all features are enabled for source generated documents, make sure they are // included as well. var documents = project.Documents; - var syntaxTreeConfigurationService = solution.Workspace.Services.GetService(); - if (syntaxTreeConfigurationService?.EnableOpeningSourceGeneratedFilesInWorkspace != false) + if (solution.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true }) { documents = documents.Concat(await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)); } diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs index 9790af57a3740..e0f98d99e9435 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/SourceGeneratedFileManager.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel.Composition; using System.IO; using System.Linq; @@ -13,8 +12,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; @@ -304,6 +301,7 @@ public void Dispose() _workspace.WorkspaceChanged -= OnWorkspaceChanged; + // Disconnect the buffer from the workspace before making it eligible for edits DisconnectFromWorkspaceIfOpen(); using (var readOnlyRegionEdit = _textBuffer.CreateReadOnlyRegionEdit()) @@ -399,7 +397,7 @@ public async ValueTask RefreshFileAsync(CancellationToken cancellationToken) if (connectToWorkspace && !_workspace.IsDocumentOpen(_documentIdentity.DocumentId)) { - _workspace.OnSourceGeneratedDocumentOpened(_documentIdentity, _textBuffer.AsTextContainer(), generatedDocument); + _workspace.OnSourceGeneratedDocumentOpened(_textBuffer.AsTextContainer(), generatedDocument); } } finally diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 6cbf3c5f6f632..673c05d3fa983 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -320,7 +320,9 @@ private static void VerifyDocumentMap(Project project, ImmutableDictionary // TODO: switch this protected once we have confidence in API shape internal void OnSourceGeneratedDocumentOpened( - SourceGeneratedDocumentIdentity documentIdentity, SourceTextContainer textContainer, SourceGeneratedDocument document) { using (_serializationLock.DisposableWait()) { - var documentId = documentIdentity.DocumentId; + var documentId = document.Identity.DocumentId; CheckDocumentIsClosed(documentId); AddToOpenDocumentMap(documentId); _documentToAssociatedBufferMap.Add(documentId, textContainer); - _openSourceGeneratedDocumentIdentities.Add(documentId, documentIdentity); + _openSourceGeneratedDocumentIdentities.Add(documentId, document.Identity); UpdateCurrentContextMapping_NoLock(textContainer, documentId, isCurrentContext: true); diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index 7f87e21544672..6be0da7b7f88c 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.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.Linq; using System.Text; @@ -539,10 +538,9 @@ public async Task OpenSourceGeneratedUpdatedToBufferContentsWhenCallingGetOpenDo Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); var generatedDocument = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()); - var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); + workspace.OnSourceGeneratedDocumentOpened(differentOpenTextContainer, generatedDocument); generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); Assert.Same(differentOpenTextContainer.CurrentText, await generatedDocument.GetTextAsync()); @@ -564,10 +562,9 @@ public async Task OpenSourceGeneratedFileDoesNotCreateNewSnapshotIfContentsKnown Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); var generatedDocument = Assert.Single(await workspace.CurrentSolution.Projects.Single().GetSourceGeneratedDocumentsAsync()); - var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// StaticContent", Encoding.UTF8).Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); + workspace.OnSourceGeneratedDocumentOpened(differentOpenTextContainer, generatedDocument); generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); Assert.Same(workspace.CurrentSolution, generatedDocument!.Project.Solution); @@ -585,10 +582,9 @@ public async Task OpenSourceGeneratedFileMatchesBufferContentsEvenIfGeneratedFil Assert.True(workspace.SetCurrentSolution(_ => originalAdditionalFile.Project.Solution, WorkspaceChangeKind.SolutionChanged)); var generatedDocument = Assert.Single(await originalAdditionalFile.Project.GetSourceGeneratedDocumentsAsync()); - var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); + workspace.OnSourceGeneratedDocumentOpened(differentOpenTextContainer, generatedDocument); workspace.OnAdditionalDocumentRemoved(originalAdditionalFile.Id); // At this point there should be no generated documents, even though our file is still open @@ -617,10 +613,9 @@ public async Task OpenSourceGeneratedDocumentUpdatedAndVisibleInProjectReference Assert.True(workspace.SetCurrentSolution(_ => solution, WorkspaceChangeKind.SolutionChanged)); var generatedDocument = Assert.Single(await workspace.CurrentSolution.GetRequiredProject(projectIdWithGenerator).GetSourceGeneratedDocumentsAsync()); - var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); + workspace.OnSourceGeneratedDocumentOpened(differentOpenTextContainer, generatedDocument); generatedDocument = Assert.IsType(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); var generatedTree = await generatedDocument.GetSyntaxTreeAsync(); @@ -645,18 +640,17 @@ public async Task OpenSourceGeneratedDocumentsUpdateIsDocumentOpenAndCloseWorks( Assert.True(workspace.SetCurrentSolution(_ => project.Solution, WorkspaceChangeKind.SolutionChanged)); var generatedDocument = Assert.Single(await project.GetSourceGeneratedDocumentsAsync()); - var generatedDocumentIdentity = generatedDocument.Identity; var differentOpenTextContainer = SourceText.From("// Open Text").Container; - workspace.OnSourceGeneratedDocumentOpened(generatedDocumentIdentity, differentOpenTextContainer, generatedDocument); + workspace.OnSourceGeneratedDocumentOpened(differentOpenTextContainer, generatedDocument); - Assert.True(workspace.IsDocumentOpen(generatedDocumentIdentity.DocumentId)); + Assert.True(workspace.IsDocumentOpen(generatedDocument.Identity.DocumentId)); - var document = await workspace.CurrentSolution.GetSourceGeneratedDocumentAsync(generatedDocumentIdentity.DocumentId, CancellationToken.None); + var document = await workspace.CurrentSolution.GetSourceGeneratedDocumentAsync(generatedDocument.Identity.DocumentId, CancellationToken.None); Contract.ThrowIfNull(document); workspace.OnSourceGeneratedDocumentClosed(document); - Assert.False(workspace.IsDocumentOpen(generatedDocumentIdentity.DocumentId)); + Assert.False(workspace.IsDocumentOpen(generatedDocument.Identity.DocumentId)); Assert.Null(differentOpenTextContainer.CurrentText.GetOpenDocumentInCurrentContextWithChanges()); } From c24c7fabbe51a0a637297f1d245becebafc9e09f Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 13:39:06 -0800 Subject: [PATCH 028/187] Fix Generate-Equals for VB when invoked on type header. --- ...ateEqualsAndGetHashCodeFromMembersTests.vb | 22 +++++++++++++++++++ ...hCodeFromMembersCodeRefactoringProvider.cs | 6 ----- .../SyntaxFacts/VisualBasicHeaderFacts.vb | 2 +- .../ISymbolDeclarationService.cs | 2 -- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb b/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb index d399a92844c46..aa86e6fab072e 100644 --- a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb +++ b/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb @@ -818,5 +818,27 @@ End Class ", index:=1) End Function + + + + Public Async Function TestWithDialogOnClassHeader() As Task + Await TestWithPickMembersDialogAsync( +" +Class [||]Program + Public Property F() As Integer + +End Class", +" +Class Program + Public Property F() As Integer + + Public Overrides Function Equals(obj As Object) As Boolean + Dim program = TryCast(obj, Program) + Return program IsNot Nothing AndAlso + F = program.F + End Function +End Class", +chosenSymbols:=Nothing) + End Function End Class End Namespace diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs index 76156efe808b6..3dc0d28273dbd 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider.cs @@ -88,15 +88,11 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context) // Only supported on classes/structs. var containingType = semanticModel.GetDeclaredSymbol(typeDeclaration) as INamedTypeSymbol; if (containingType?.TypeKind is not TypeKind.Class and not TypeKind.Struct) - { return; - } // No overrides in static classes. if (containingType.IsStatic) - { return; - } // Find all the possible instance fields/properties. If there are any, then // show a dialog to the user to select the ones they want. @@ -108,9 +104,7 @@ private async Task HandleNonSelectionAsync(CodeRefactoringContext context) .ToImmutableArray(); if (viableMembers.Length == 0) - { return; - } GetExistingMemberInfo( containingType, out var hasEquals, out var hasGetHashCode); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicHeaderFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicHeaderFacts.vb index 4d60b18c00147..9a3acc9ee9da3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicHeaderFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicHeaderFacts.vb @@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices End If Dim typeStatement = typeBlock.BlockStatement - typeDeclaration = typeStatement + typeDeclaration = typeBlock Dim lastToken = If(typeStatement.TypeParameterList?.GetLastToken(), typeStatement.Identifier) If fullHeader Then diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SymbolDeclarationService/ISymbolDeclarationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SymbolDeclarationService/ISymbolDeclarationService.cs index 855354a4afab0..966e024e3f0d5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SymbolDeclarationService/ISymbolDeclarationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SymbolDeclarationService/ISymbolDeclarationService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using Microsoft.CodeAnalysis.Host; From 29a002127c79ca4ff40f39c0a39d0d5d58654842 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 13:39:37 -0800 Subject: [PATCH 029/187] Update --- .../GenerateEqualsAndGetHashCodeFromMembersTests.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb b/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb index aa86e6fab072e..d163974f4d4c1 100644 --- a/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb +++ b/src/EditorFeatures/VisualBasicTest/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndGetHashCodeFromMembersTests.vb @@ -819,8 +819,8 @@ End Class index:=1) End Function - + Public Async Function TestWithDialogOnClassHeader() As Task Await TestWithPickMembersDialogAsync( " From 1cfbd46fe3b352ed8b5ea116b2887a86294cb5e3 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Mon, 14 Feb 2022 15:49:37 -0800 Subject: [PATCH 030/187] Aggregate complstion session property data into a single object --- .../AsyncCompletion/CommitManager.cs | 12 ++-- .../AsyncCompletion/CompletionSessionData.cs | 36 +++++++++++ .../AsyncCompletion/CompletionSource.cs | 59 +++++++++--------- .../ItemManager.CompletionListUpdater.cs | 36 +++++------ .../AsyncCompletion/ItemManager.cs | 60 ++++++++++--------- .../CSharpCompletionCommandHandlerTests.vb | 19 ++++-- 6 files changed, 131 insertions(+), 91 deletions(-) create mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs index 192290e3d57bf..77c2a604b80fa 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs @@ -79,8 +79,9 @@ public bool ShouldCommitCompletion( return false; } - return !(session.Properties.TryGetProperty(CompletionSource.ExcludedCommitCharacters, out ImmutableArray excludedCommitCharacter) - && excludedCommitCharacter.Contains(typedChar)); + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + return !sessionData.ExcludedCommitCharacters.HasValue + || !sessionData.ExcludedCommitCharacters.Value.Contains(typedChar); } public AsyncCompletionData.CommitResult TryCommit( @@ -120,6 +121,7 @@ public AsyncCompletionData.CommitResult TryCommit( var options = _globalOptions.GetCompletionOptions(document.Project.Language); var serviceRules = completionService.GetRules(options); + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // We can be called before for ShouldCommitCompletion. However, that call does not provide rules applied for the completion item. // Now we check for the commit character in the context of Rules that could change the list of commit characters. @@ -139,11 +141,13 @@ public AsyncCompletionData.CommitResult TryCommit( return CommitResultUnhandled; } - if (!session.Properties.TryGetProperty(CompletionSource.CompletionListSpan, out TextSpan completionListSpan)) + if (!sessionData.CompletionListSpan.HasValue) { return CommitResultUnhandled; } + var completionListSpan = sessionData.CompletionListSpan.Value; + var triggerDocument = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (triggerDocument == null) { @@ -151,7 +155,7 @@ public AsyncCompletionData.CommitResult TryCommit( } // Telemetry - if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isExperimentEnabled) && isExperimentEnabled) + if (sessionData.TargetTypeFilterExperimentEnabled) { // Capture the % of committed completion items that would have appeared in the "Target type matches" filter // (regardless of whether that filter button was active at the time of commit). diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs new file mode 100644 index 0000000000000..d81b28d1cf5b7 --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs @@ -0,0 +1,36 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; +using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; +using Microsoft.VisualStudio.Text; +using RoslynCompletionList = Microsoft.CodeAnalysis.Completion.CompletionList; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion +{ + /// + /// Contains data need to be tracked over an entire IAsyncCompletionSession completion + /// session for various operations. + /// + internal sealed class CompletionSessionData + { + private const string SessionDataName = "RoslynCompletionSessionData"; + + public bool TargetTypeFilterExperimentEnabled { get; set; } + public bool TargetTypeFilterSelected { get; set; } + public bool HasSuggestionItemOptions { get; set; } + + public Optional ExpandedItemTriggerLocation { get; set; } + public Optional CompletionListSpan { get; set; } + public Optional> CombinedSortedList { get; set; } + public Optional> ExpandedItemsTask { get; set; } + public Optional> ExcludedCommitCharacters { get; set; } + + public static CompletionSessionData GetOrCreateSessionData(IAsyncCompletionSession session) + => session.Properties.GetOrCreateSingletonProperty(SessionDataName, static () => new CompletionSessionData()); + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 9244dd9679f20..56215f5bb52e7 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -41,16 +41,10 @@ internal sealed class CompletionSource : ForegroundThreadAffinitizedObject, IAsy { internal const string RoslynItem = nameof(RoslynItem); internal const string TriggerLocation = nameof(TriggerLocation); - internal const string ExpandedItemTriggerLocation = nameof(ExpandedItemTriggerLocation); - internal const string CompletionListSpan = nameof(CompletionListSpan); internal const string InsertionText = nameof(InsertionText); - internal const string HasSuggestionItemOptions = nameof(HasSuggestionItemOptions); internal const string Description = nameof(Description); internal const string PotentialCommitCharacters = nameof(PotentialCommitCharacters); - internal const string ExcludedCommitCharacters = nameof(ExcludedCommitCharacters); internal const string NonBlockingCompletion = nameof(NonBlockingCompletion); - internal const string TargetTypeFilterExperimentEnabled = nameof(TargetTypeFilterExperimentEnabled); - internal const string ExpandedItemsTask = nameof(ExpandedItemsTask); private static readonly ImmutableArray s_warningImageAttributeImagesArray = ImmutableArray.Create(new ImageElement(Glyph.CompletionWarning.GetImageId(), EditorFeaturesResources.Warning_image_element)); @@ -133,9 +127,6 @@ public AsyncCompletionData.CompletionStartData InitializeCompletion( // Set it later if met the condition. _snippetCompletionTriggeredIndirectly = false; - // For telemetry reporting purpose - _textView.Properties[TargetTypeFilterExperimentEnabled] = options.TargetTypedCompletionFilter; - var sourceText = document.GetTextSynchronously(cancellationToken); return ShouldTriggerCompletion(trigger, triggerLocation, sourceText, document, service, options) @@ -264,6 +255,10 @@ public async Task GetCompletionContextAsync( // to still provide those items later before they are truly required. var options = _globalOptions.GetCompletionOptions(document.Project.Language); + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + + // For telemetry reporting purpose + sessionData.TargetTypeFilterExperimentEnabled = options.TargetTypedCompletionFilter; if (!options.ShouldShowItemsFromUnimportNamspaces()) { @@ -271,7 +266,7 @@ public async Task GetCompletionContextAsync( var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - AddPropertiesToSession(session, list, triggerLocation); + UpdateSessionData(sessionData, list, triggerLocation); return context; } else if (!_responsiveCompletionEnabled) @@ -281,7 +276,7 @@ public async Task GetCompletionContextAsync( var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.AllItems }, cancellationToken).ConfigureAwait(false); - AddPropertiesToSession(session, list, triggerLocation); + UpdateSessionData(sessionData, list, triggerLocation); AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: true, delayed: false); return context; } @@ -309,13 +304,13 @@ public async Task GetCompletionContextAsync( // Now trigger and wait for core providers to return; var (nonExpandedContext, nonExpandedCompletionList) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - AddPropertiesToSession(session, nonExpandedCompletionList, triggerLocation); + UpdateSessionData(sessionData, nonExpandedCompletionList, triggerLocation); if (expandedItemsTask.IsCompleted) { // the task of expanded item is completed, get the result and combine it with result of non-expanded items. var (expandedContext, expandedCompletionList) = await expandedItemsTask.ConfigureAwait(false); - AddPropertiesToSession(session, expandedCompletionList, triggerLocation); + UpdateSessionData(sessionData, expandedCompletionList, triggerLocation); AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: false); return CombineCompletionContext(nonExpandedContext, expandedContext); @@ -327,7 +322,7 @@ public async Task GetCompletionContextAsync( // after core providers completed (instead of how long it takes end-to-end). stopwatch.Start(); - session.Properties[ExpandedItemsTask] = expandedItemsTask; + sessionData.ExpandedItemsTask = new(expandedItemsTask); AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: true); return nonExpandedContext; @@ -362,20 +357,25 @@ public async Task GetExpandedCompletionContextAsync( SnapshotSpan applicableToSpan, CancellationToken cancellationToken) { + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + // We only want to provide expanded items for Roslyn's expander. - if (expander == FilterSet.Expander && session.Properties.TryGetProperty(ExpandedItemTriggerLocation, out SnapshotPoint initialTriggerLocation)) + if (expander == FilterSet.Expander && sessionData.ExpandedItemTriggerLocation.HasValue) { + var initialTriggerLocation = sessionData.ExpandedItemTriggerLocation.Value; AsyncCompletionLogger.LogExpanderUsage(); // It's possible we didn't provide expanded items at the beginning of completion session because it was slow even if the feature is enabled. // ExpandedItemsTask would be available in this case, so we just need to return its result. - if (session.Properties.TryGetProperty(ExpandedItemsTask, out Task<(VSCompletionContext, CompletionList)> expandedItemsTask)) + if (sessionData.ExpandedItemsTask.HasValue) { // Make sure the task is removed when returning expanded items, // so duplicated items won't be added in subsequent list updates. - session.Properties.RemoveProperty(ExpandedItemsTask); - var (expandedContext, expandedCompletionList) = await expandedItemsTask.ConfigureAwait(false); - AddPropertiesToSession(session, expandedCompletionList, initialTriggerLocation); + var task = sessionData.ExpandedItemsTask.Value; + sessionData.ExpandedItemsTask = new(); + + var (expandedContext, expandedCompletionList) = await task.ConfigureAwait(false); + UpdateSessionData(sessionData, expandedCompletionList, initialTriggerLocation); return expandedContext; } @@ -393,7 +393,7 @@ public async Task GetExpandedCompletionContextAsync( }; var (context, completionList) = await GetCompletionContextWorkerAsync(document, intialTrigger, initialTriggerLocation, options, cancellationToken).ConfigureAwait(false); - AddPropertiesToSession(session, completionList, initialTriggerLocation); + UpdateSessionData(sessionData, completionList, initialTriggerLocation); return context; } @@ -449,30 +449,27 @@ public async Task GetExpandedCompletionContextAsync( return (new(items, suggestionItemOptions, selectionHint: AsyncCompletionData.InitialSelectionHint.SoftSelection, filters), completionList); } - private static void AddPropertiesToSession(IAsyncCompletionSession session, CompletionList completionList, SnapshotPoint triggerLocation) + private static void UpdateSessionData(CompletionSessionData sessionData, CompletionList completionList, SnapshotPoint triggerLocation) { // Store around the span this completion list applies to. We'll use this later // to pass this value in when we're committing a completion list item. // It's OK to overwrite this value when expanded items are requested. - session.Properties[CompletionListSpan] = completionList.Span; + sessionData.CompletionListSpan = completionList.Span; // This is a code supporting original completion scenarios: // Controller.Session_ComputeModel: if completionList.SuggestionModeItem != null, then suggestionMode = true // If there are suggestionItemOptions, then later HandleNormalFiltering should set selection to SoftSelection. - if (!session.Properties.TryGetProperty(HasSuggestionItemOptions, out bool hasSuggestionItemOptionsBefore) || !hasSuggestionItemOptionsBefore) - { - session.Properties[HasSuggestionItemOptions] = completionList.SuggestionModeItem != null; - } + sessionData.HasSuggestionItemOptions |= completionList.SuggestionModeItem != null; var excludedCommitCharacters = GetExcludedCommitCharacters(completionList.Items); if (excludedCommitCharacters.Length > 0) { - if (session.Properties.TryGetProperty(ExcludedCommitCharacters, out ImmutableArray excludedCommitCharactersBefore)) + if (sessionData.CompletionListSpan.HasValue) { - excludedCommitCharacters = excludedCommitCharacters.Union(excludedCommitCharactersBefore).ToImmutableArray(); + excludedCommitCharacters = excludedCommitCharacters.Union(sessionData.ExcludedCommitCharacters.Value).ToImmutableArray(); } - session.Properties[ExcludedCommitCharacters] = excludedCommitCharacters; + sessionData.ExcludedCommitCharacters = excludedCommitCharacters; } // We need to remember the trigger location for when a completion service claims expanded items are available @@ -480,9 +477,9 @@ private static void AddPropertiesToSession(IAsyncCompletionSession session, Comp // so when they are requested via expander later, we can retrieve it. // Technically we should save the trigger location for each individual service that made such claim, but in reality only Roslyn's // completion service uses expander, so we can get away with not making such distinction. - if (!session.Properties.ContainsProperty(ExpandedItemTriggerLocation)) + if (!sessionData.ExpandedItemTriggerLocation.HasValue) { - session.Properties[ExpandedItemTriggerLocation] = triggerLocation; + sessionData.ExpandedItemTriggerLocation = triggerLocation; } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index 030692e4ad810..ee5229d24710a 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; -using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -33,10 +32,11 @@ internal partial class ItemManager ///
private sealed class CompletionListUpdater { - private readonly IAsyncCompletionSession _session; + private readonly CompletionSessionData _sessionData; private readonly AsyncCompletionSessionDataSnapshot _data; private readonly RecentItemsManager _recentItemsManager; + private readonly ITrackingSpan _applicableToSpan; private readonly bool _hasSuggestedItemOptions; private readonly string _filterText; private readonly Document? _document; @@ -59,25 +59,20 @@ private sealed class CompletionListUpdater private static readonly ObjectPool>> s_listOfMatchResultPool = new(factory: () => new(), size: 1); public CompletionListUpdater( - IAsyncCompletionSession session, + ITrackingSpan applicableToSpan, + CompletionSessionData sessionData, AsyncCompletionSessionDataSnapshot data, RecentItemsManager recentItemsManager, IGlobalOptionService globalOptions) { - _session = session; + _sessionData = sessionData; _data = data; _recentItemsManager = recentItemsManager; - _filterText = _session.ApplicableToSpan.GetText(_data.Snapshot); + _applicableToSpan = applicableToSpan; + _filterText = applicableToSpan.GetText(_data.Snapshot); - if (!_session.Properties.TryGetProperty(CompletionSource.HasSuggestionItemOptions, out bool hasSuggestedItemOptions)) - { - // This is the scenario when the session is created out of Roslyn, in some other provider, e.g. in Debugger. - // For now, the default hasSuggestedItemOptions is false. - hasSuggestedItemOptions = false; - } - - _hasSuggestedItemOptions = hasSuggestedItemOptions || _data.DisplaySuggestionItem; + _hasSuggestedItemOptions = sessionData.HasSuggestionItemOptions || _data.DisplaySuggestionItem; // We prefer using the original snapshot, which should always be available from items provided by Roslyn's CompletionSource. // Only use data.Snapshot in the theoretically possible but rare case when all items we are handling are from some non-Roslyn CompletionSource. @@ -184,7 +179,7 @@ private bool ShouldDismissCompletionListImmediately() // // We'll bring up the completion list here (as VB has completion on ). // If the user then types '3', we don't want to match against Int32. - if (_filterText.Length > 0 && char.IsNumber(_filterText[0]) && !IsAfterDot(_data.Snapshot, _session.ApplicableToSpan)) + if (_filterText.Length > 0 && char.IsNumber(_filterText[0]) && !IsAfterDot(_data.Snapshot, _applicableToSpan)) { // Dismiss the session. return true; @@ -214,7 +209,7 @@ private void AddCompletionItems(List> list, Cancel { // FilterStateHelper is used to decide whether a given item should be included in the list based on the state of filter/expander buttons. var filterHelper = new FilterStateHelper(_data.SelectedFilters); - filterHelper.LogTargetTypeFilterTelemetry(_session); + filterHelper.LogTargetTypeFilterTelemetry(_sessionData); // We want to sort the items by pattern matching results while preserving the original alphabetical order for items with // same pattern match score, but `List.Sort` isn't stable. Therefore we have to add a monotonically increasing integer @@ -738,9 +733,6 @@ private sealed class FilterStateHelper private readonly bool _needToFilter; private readonly bool _needToFilterExpanded; - // For telemetry - private readonly object _targetTypeCompletionFilterChosenMarker = new(); - public FilterStateHelper(ImmutableArray filtersWithState) { // The filter state list contains two kinds of "filters": regular filter and expander. @@ -796,19 +788,19 @@ private bool ShouldBeFilteredOutOfExpandedCompletionList(VSCompletionItem item) return associatedWithUnselectedExpander; } - public void LogTargetTypeFilterTelemetry(IAsyncCompletionSession session) + public void LogTargetTypeFilterTelemetry(CompletionSessionData sessionData) { - if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isExperimentEnabled) && isExperimentEnabled) + if (sessionData.TargetTypeFilterExperimentEnabled) { // Telemetry: Want to know % of sessions with the "Target type matches" filter where that filter is actually enabled if (_needToFilter && - !session.Properties.ContainsProperty(_targetTypeCompletionFilterChosenMarker) && + !sessionData.TargetTypeFilterSelected && _selectedNonExpanderFilters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches)) { AsyncCompletionLogger.LogTargetTypeFilterChosenInSession(); // Make sure we only record one enabling of the filter per session - session.Properties.AddProperty(_targetTypeCompletionFilterChosenMarker, _targetTypeCompletionFilterChosenMarker); + sessionData.TargetTypeFilterSelected = true; } } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs index 0bc14462de196..e7037a066b408 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs @@ -7,12 +7,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using Roslyn.Utilities; using RoslynCompletionItem = Microsoft.CodeAnalysis.Completion.CompletionItem; -using RoslynCompletionList = Microsoft.CodeAnalysis.Completion.CompletionList; using VSCompletionItem = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion @@ -36,8 +34,9 @@ public Task> SortCompletionListAsync( AsyncCompletionSessionInitialDataSnapshot data, CancellationToken cancellationToken) { + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // This method is called exactly once, so use the opportunity to set a baseline for telemetry. - if (session.TextView.Properties.TryGetProperty(CompletionSource.TargetTypeFilterExperimentEnabled, out bool isTargetTypeFilterEnabled) && isTargetTypeFilterEnabled) + if (sessionData.TargetTypeFilterExperimentEnabled) { AsyncCompletionLogger.LogSessionHasTargetTypeFilterEnabled(); if (data.InitialList.Any(i => i.Filters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))) @@ -54,6 +53,8 @@ public Task> SortCompletionListAsync( AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + // As explained in more details in the comments for `CompletionSource.GetCompletionContextAsync`, expanded items might // not be provided upon initial trigger of completion to reduce typing delays, even if they are supposed to be included by default. // While we do not expect to run in to this scenario very often, we'd still want to minimize the impact on user experience of this feature @@ -64,40 +65,43 @@ public Task> SortCompletionListAsync( // There is a `CompletionContext.IsIncomplete` flag, which is only supported in LSP mode at the moment. Therefore we opt to handle the checking // and combining the items in Roslyn until the `IsIncomplete` flag is fully supported in classic mode. - if (session.Properties.TryGetProperty(CombinedSortedList, out ImmutableArray combinedSortedList)) + if (sessionData.CombinedSortedList.HasValue) { // Always use the previously saved combined list if available. - data = new AsyncCompletionSessionDataSnapshot(combinedSortedList, data.Snapshot, data.Trigger, data.InitialTrigger, data.SelectedFilters, + data = new AsyncCompletionSessionDataSnapshot(sessionData.CombinedSortedList.Value, data.Snapshot, data.Trigger, data.InitialTrigger, data.SelectedFilters, data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); } - else if (session.Properties.TryGetProperty(CompletionSource.ExpandedItemsTask, out Task<(CompletionContext, RoslynCompletionList)> task) - && task.Status == TaskStatus.RanToCompletion) + else if (sessionData.ExpandedItemsTask.HasValue) { - // Make sure the task is removed when Adding expanded items, - // so duplicated items won't be added in subsequent list updates. - session.Properties.RemoveProperty(CompletionSource.ExpandedItemsTask); - - var (expandedContext, _) = await task.ConfigureAwait(false); - if (expandedContext.Items.Length > 0) + var task = sessionData.ExpandedItemsTask.Value; + if (task.Status == TaskStatus.RanToCompletion) { - // Here we rely on the implementation detail of `CompletionItem.CompareTo`, which always put expand items after regular ones. - var itemsBuilder = ImmutableArray.CreateBuilder(expandedContext.Items.Length + data.InitialSortedList.Length); - itemsBuilder.AddRange(data.InitialSortedList); - itemsBuilder.AddRange(expandedContext.Items); - var combinedList = itemsBuilder.MoveToImmutable(); - - // Add expanded items into a combined list, and save it to be used for future updates during the same session. - session.Properties[CombinedSortedList] = combinedList; - var combinedFilterStates = FilterSet.CombineFilterStates(expandedContext.Filters, data.SelectedFilters); - - data = new AsyncCompletionSessionDataSnapshot(combinedList, data.Snapshot, data.Trigger, data.InitialTrigger, combinedFilterStates, - data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); + // Make sure the task is removed when Adding expanded items, + // so duplicated items won't be added in subsequent list updates. + sessionData.ExpandedItemsTask = new(); + + var (expandedContext, _) = await task.ConfigureAwait(false); + if (expandedContext.Items.Length > 0) + { + // Here we rely on the implementation detail of `CompletionItem.CompareTo`, which always put expand items after regular ones. + var itemsBuilder = ImmutableArray.CreateBuilder(expandedContext.Items.Length + data.InitialSortedList.Length); + itemsBuilder.AddRange(data.InitialSortedList); + itemsBuilder.AddRange(expandedContext.Items); + var combinedList = itemsBuilder.MoveToImmutable(); + + // Add expanded items into a combined list, and save it to be used for future updates during the same session. + sessionData.CombinedSortedList = new(combinedList); + var combinedFilterStates = FilterSet.CombineFilterStates(expandedContext.Filters, data.SelectedFilters); + + data = new AsyncCompletionSessionDataSnapshot(combinedList, data.Snapshot, data.Trigger, data.InitialTrigger, combinedFilterStates, + data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); + } + + AsyncCompletionLogger.LogSessionWithDelayedImportCompletionIncludedInUpdate(); } - - AsyncCompletionLogger.LogSessionWithDelayedImportCompletionIncludedInUpdate(); } - var updater = new CompletionListUpdater(session, data, _recentItemsManager, _globalOptions); + var updater = new CompletionListUpdater(session.ApplicableToSpan, sessionData, data, _recentItemsManager, _globalOptions); return updater.UpdateCompletionList(cancellationToken); } diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index d591d1bb73273..a7fcb3a55a4d0 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -24,6 +24,7 @@ Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Operations Imports Microsoft.VisualStudio.Text.Projection +Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense <[UseExportProvider]> @@ -9700,8 +9701,10 @@ class C Await state.AssertCompletionItemsDoNotContainAny("TestUnimportedItem") Dim session = Await state.GetCompletionSession() - Dim expandTask As Task = Nothing - Assert.True(session.Properties.TryGetProperty(Of Task)(CompletionSource.ExpandedItemsTask, expandTask)) + Dim sessionData = CompletionSessionData.GetOrCreateSessionData(session) + Dim expandTask = sessionData.ExpandedItemsTask.GetValueOrNull() + + Assert.NotNull(expandTask) Assert.False(expandTask.IsCompleted) ' following up by typing a few more characters each triggers an list update @@ -9757,8 +9760,10 @@ class C Await state.AssertCompletionItemsDoNotContainAny("TestUnimportedItem") Dim session = Await state.GetCompletionSession() - Dim expandTask As Task = Nothing - Assert.True(session.Properties.TryGetProperty(Of Task)(CompletionSource.ExpandedItemsTask, expandTask)) + Dim sessionData = CompletionSessionData.GetOrCreateSessionData(session) + Dim expandTask = sessionData.ExpandedItemsTask.GetValueOrNull() + + Assert.NotNull(expandTask) Assert.False(expandTask.IsCompleted) ' following up by typing more characters each triggers an list update @@ -9815,8 +9820,10 @@ class C Await state.AssertCompletionItemsDoNotContainAny("TestUnimportedItem") Dim session = Await state.GetCompletionSession() - Dim expandTask As Task = Nothing - Assert.True(session.Properties.TryGetProperty(Of Task)(CompletionSource.ExpandedItemsTask, expandTask)) + Dim sessionData = CompletionSessionData.GetOrCreateSessionData(session) + Dim expandTask = sessionData.ExpandedItemsTask.GetValueOrNull() + + Assert.NotNull(expandTask) Assert.False(expandTask.IsCompleted) provider.Checkpoint.Release() From 81c1cb812c7f81aa8d26c502b4db01aa1717ea83 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 16:36:48 -0800 Subject: [PATCH 031/187] Do not remove parentheses from arithmetic ops in 'checked' contexts --- ...veUnnecessaryExpressionParenthesesTests.cs | 38 ++++++++++++ .../RemoveUnnecessaryParenthesesTests.vb | 38 ++++++++---- .../AbstractSemanticFactsService.cs | 3 +- ...ParenthesizedExpressionSyntaxExtensions.cs | 7 +-- .../SemanticFacts/CSharpSemanticFacts.cs | 2 + .../Core/CompilerExtensions.projitems | 2 +- .../Services/SemanticFacts/ISemanticFacts.cs | 2 + ...ParenthesizedExpressionSyntaxExtensions.cs | 59 +++++++++++-------- ...ParenthesizedExpressionSyntaxExtensions.vb | 6 +- .../SemanticFacts/VisualBasicSemanticFacts.vb | 6 ++ .../CSharpSemanticFactsService.cs | 3 +- .../AbstractSemanticFactsService.cs | 2 +- .../VisualBasicSemanticFactsService.vb | 2 +- 13 files changed, 122 insertions(+), 48 deletions(-) rename src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/{Extensions => Services/SemanticFacts}/ParenthesizedExpressionSyntaxExtensions.cs (66%) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs index be8d5e3e93685..15f33951fdf48 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs @@ -2753,5 +2753,43 @@ public void M() } ", offeredWhenRequireForClarityIsEnabled: true); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] + [WorkItem(45100, "https://github.com/dotnet/roslyn/issues/45100")] + public async Task TestArithmeticOverflow1() + { + await TestMissingAsync( +@"class C +{ + void M(int a) + { + checked + { + return a + $$(int.MaxValue + -int.MaxValue); + } + } +}", parameters: new TestParameters(options: RemoveAllUnnecessaryParentheses)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] + [WorkItem(45100, "https://github.com/dotnet/roslyn/issues/45100")] + public async Task TestArithmeticOverflow2() + { + await TestInRegularAndScript1Async( +@"class C +{ + void M(int a) + { + return a + $$(int.MaxValue + -int.MaxValue); + } +}", +@"class C +{ + void M(int a) + { + return a + int.MaxValue + -int.MaxValue; + } +}", parameters: new TestParameters(options: RemoveAllUnnecessaryParentheses)); + } } } diff --git a/src/Analyzers/VisualBasic/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryParenthesesTests.vb b/src/Analyzers/VisualBasic/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryParenthesesTests.vb index aef3c3af53a18..35038621ed8a5 100644 --- a/src/Analyzers/VisualBasic/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryParenthesesTests.vb +++ b/src/Analyzers/VisualBasic/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryParenthesesTests.vb @@ -18,6 +18,9 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryP Partial Public Class RemoveUnnecessaryParenthesesTests Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest + Private Shared ReadOnly CheckOverflow As CompilationOptions = New VisualBasicCompilationOptions(OutputKind.ConsoleApplication, checkOverflow:=True) + Private Shared ReadOnly DoNotCheckOverflow As CompilationOptions = New VisualBasicCompilationOptions(OutputKind.ConsoleApplication, checkOverflow:=False) + Friend Overrides Function CreateDiagnosticProviderAndFixer(Workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) Return (New VisualBasicRemoveUnnecessaryParenthesesDiagnosticAnalyzer(), New VisualBasicRemoveUnnecessaryParenthesesCodeFixProvider()) End Function @@ -32,13 +35,18 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryP Private Shadows Async Function TestAsync(initial As String, expected As String, offeredWhenRequireAllParenthesesForClarityIsEnabled As Boolean, - Optional ByVal index As Integer = 0) As Task - Await TestInRegularAndScriptAsync(initial, expected, options:=RemoveAllUnnecessaryParentheses, index:=index) + Optional index As Integer = 0, + Optional checkOverflow As Boolean = True) As Task + Dim compilationOptions = If(checkOverflow, + RemoveUnnecessaryParenthesesTests.CheckOverflow, + RemoveUnnecessaryParenthesesTests.DoNotCheckOverflow) + + Await TestInRegularAndScriptAsync(initial, expected, options:=RemoveAllUnnecessaryParentheses, index:=index, compilationOptions:=compilationOptions) If (offeredWhenRequireAllParenthesesForClarityIsEnabled) Then - Await TestInRegularAndScriptAsync(initial, expected, options:=MyBase.RequireAllParenthesesForClarity, index:=index) + Await TestInRegularAndScriptAsync(initial, expected, options:=RequireAllParenthesesForClarity, index:=index, compilationOptions:=compilationOptions) Else - Await TestMissingAsync(initial, parameters:=New TestParameters(options:=MyBase.RequireAllParenthesesForClarity)) + Await TestMissingAsync(initial, parameters:=New TestParameters(options:=RequireAllParenthesesForClarity, compilationOptions:=compilationOptions)) End If End Function @@ -114,7 +122,7 @@ end class", parameters:=New TestParameters(options:=RequireOtherBinaryParenthese End Function - Public Async Function TestArithmeticNotRequiredForClarityWhenPrecedenceStaysTheSame1() As Task + Public Async Function TestArithmeticNotRequiredForClarityWhenPrecedenceStaysTheSame1_DoNotCheckOverflow() As Task Await TestAsync( "class C sub M() @@ -125,7 +133,17 @@ end class", sub M() dim x = 1 + 2 + 3 end sub -end class", offeredWhenRequireAllParenthesesForClarityIsEnabled:=True) +end class", offeredWhenRequireAllParenthesesForClarityIsEnabled:=True, checkOverflow:=False) + End Function + + + Public Async Function TestArithmeticNotRequiredForClarityWhenPrecedenceStaysTheSame1_CheckOverflow() As Task + Await TestMissingAsync( +"class C + sub M() + dim x = 1 + $$(2 + 3) + end sub +end class", New TestParameters(options:=RequireArithmeticBinaryParenthesesForClarity)) End Function @@ -587,7 +605,7 @@ end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), firstL - Public Async Function TestUnnecessaryParenthesisDiagnosticInNestedExpression() As Task + Public Async Function TestUnnecessaryParenthesisDiagnosticInNestedExpression_DoNotCheckOverflow() As Task Dim outerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + (2 + 3) + 4)", 2, 16) Dim innerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(2 + 3)", 2, 21) Dim expectedDiagnostics = New DiagnosticDescription() {outerParentheticalExpressionDiagnostic, innerParentheticalExpressionDiagnostic} @@ -596,12 +614,12 @@ end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), firstL sub M() dim x = [|(1 + (2 + 3) + 4)|] end sub -end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), expectedDiagnostics) +end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses, compilationOptions:=DoNotCheckOverflow), expectedDiagnostics) End Function - Public Async Function TestUnnecessaryParenthesisDiagnosticInNestedMultiLineExpression() As Task + Public Async Function TestUnnecessaryParenthesisDiagnosticInNestedMultiLineExpression_DoNotCheckOverflow() As Task Dim outerFirstLineParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + 2 +", 2, 16) Dim innerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(3 + 4)", 3, 12) Dim expectedDiagnostics = New DiagnosticDescription() {outerFirstLineParentheticalExpressionDiagnostic, innerParentheticalExpressionDiagnostic} @@ -612,7 +630,7 @@ end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), expect (3 + 4) + 5 + 6)|] end sub -end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), expectedDiagnostics) +end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses, compilationOptions:=DoNotCheckOverflow), expectedDiagnostics) End Function diff --git a/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/SemanticFacts/AbstractSemanticFactsService.cs b/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/SemanticFacts/AbstractSemanticFactsService.cs index 84e3deba94334..b0714fea6e221 100644 --- a/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/SemanticFacts/AbstractSemanticFactsService.cs +++ b/src/Workspaces/Core/Portable/LanguageServices/SemanticsFactsService/SemanticFacts/AbstractSemanticFactsService.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. -#nullable disable - +using System; using System.Threading; namespace Microsoft.CodeAnalysis.LanguageServices diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 48a0ec4e07548..2657d54997387 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Extensions; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -417,9 +417,8 @@ private static bool RemovalChangesAssociation( node.Expression.Kind() == parentBinaryExpression.Kind() && parentBinaryExpression.Right == node) { - return !node.IsSafeToChangeAssociativity( - node.Expression, parentBinaryExpression.Left, - parentBinaryExpression.Right, semanticModel); + return !CSharpSemanticFacts.Instance.IsSafeToChangeAssociativity( + (BinaryExpressionSyntax)node.Expression, parentBinaryExpression, semanticModel); } // Null-coalescing is right associative; removing parens from the LHS changes the association. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs index 7c30407135668..01ff486109918 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SemanticFacts/CSharpSemanticFacts.cs @@ -30,6 +30,8 @@ private CSharpSemanticFacts() { } + public ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; + public bool SupportsImplicitInterfaceImplementation => true; public bool ExposesAnonymousFunctionParameterNames => false; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 880b5a12e6adc..6a12b077411f7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -253,7 +253,6 @@ - @@ -377,6 +376,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs index 6da22448b8798..08ddd7f64044e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs @@ -13,6 +13,8 @@ namespace Microsoft.CodeAnalysis.LanguageServices { internal partial interface ISemanticFacts { + ISyntaxFacts SyntaxFacts { get; } + /// /// True if this language supports implementing an interface by signature only. If false, /// implementations must specific explicitly which symbol they're implementing. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ParenthesizedExpressionSyntaxExtensions.cs similarity index 66% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ParenthesizedExpressionSyntaxExtensions.cs rename to src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ParenthesizedExpressionSyntaxExtensions.cs index 5017a6949d13d..d996546ccd1dd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ParenthesizedExpressionSyntaxExtensions.cs @@ -3,14 +3,18 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Operations; -namespace Microsoft.CodeAnalysis.Extensions +namespace Microsoft.CodeAnalysis.LanguageServices { - internal static class CommonParenthesizedExpressionSyntaxExtensions + internal static class ISemanticFactsExtensions { - public static bool IsSafeToChangeAssociativity( - this SyntaxNode parenthesizedExpression, SyntaxNode innerExpression, - SyntaxNode parentBinaryLeft, SyntaxNode parentBinaryRight, SemanticModel semanticModel) + public static bool IsSafeToChangeAssociativity( + this ISemanticFacts semanticFacts, + TBinaryExpressionSyntax innerBinary, + TBinaryExpressionSyntax parentBinary, + SemanticModel semanticModel) + where TBinaryExpressionSyntax : SyntaxNode { // Now we'll perform a few semantic checks to determine whether removal // of the parentheses might break semantics. Note that we'll try and be @@ -21,28 +25,24 @@ public static bool IsSafeToChangeAssociativity( // First, does the binary expression result in an operator overload being // called? - var symbolInfo = semanticModel.GetSymbolInfo(innerExpression); + var symbolInfo = semanticModel.GetSymbolInfo(innerBinary); if (AnySymbolIsUserDefinedOperator(symbolInfo)) - { return false; - } // Second, check the type and converted type of the binary expression. // Are they the same? - var innerTypeInfo = semanticModel.GetTypeInfo(innerExpression); + var innerTypeInfo = semanticModel.GetTypeInfo(innerBinary); if (innerTypeInfo.Type != null && innerTypeInfo.ConvertedType != null) { if (!innerTypeInfo.Type.Equals(innerTypeInfo.ConvertedType)) - { return false; - } } // It's not safe to change associativity for dynamic variables as the actual type isn't known. See https://github.com/dotnet/roslyn/issues/47365 if (innerTypeInfo.Type is IDynamicTypeSymbol) - { return false; - } + + semanticFacts.SyntaxFacts.GetPartsOfBinaryExpression(parentBinary, out var parentBinaryLeft, out var parentBinaryRight); // Only allow us to change associativity if all the types are the same. // for example, if we have: int + (int + long) then we don't want to @@ -59,23 +59,34 @@ public static bool IsSafeToChangeAssociativity( return false; } - // Floating point is not safe to change associativity of. For example, - // if the user has "large * (large * small)" then this will become - // "(large * large) * small. And that could easily overflow to Inf (and - // other badness). - var parentBinary = parenthesizedExpression.Parent; - if (parentBinary is null) - { - return false; - } - + // Floating point is not safe to change associativity of. For example, if the user has "large * (large * + // small)" then this will become "(large * large) * small. And that could easily overflow to Inf (and other + // badness). var outerTypeInfo = semanticModel.GetTypeInfo(parentBinary); if (IsFloatingPoint(innerTypeInfo) || IsFloatingPoint(outerTypeInfo)) - { return false; + + if (semanticModel.GetOperation(parentBinary) is IBinaryOperation parentBinaryOp && + semanticModel.GetOperation(innerBinary) is IBinaryOperation innerBinaryOp) + { + if ((parentBinaryOp.IsChecked || innerBinaryOp.IsChecked) && + (IsArithmetic(parentBinaryOp) || IsArithmetic(innerBinaryOp))) + { + // For checked operations, we can't change which type of operator we're performing in a row as that + // could lead to overflow if we end up doing something like an addition prior to a subtraction. + return false; + } } return true; + + static bool IsArithmetic(IBinaryOperation op) + { + return op.OperatorKind is BinaryOperatorKind.Add or + BinaryOperatorKind.Subtract or + BinaryOperatorKind.Multiply or + BinaryOperatorKind.Divide; + } } private static bool AnySymbolIsUserDefinedOperator(SymbolInfo symbolInfo) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb index 062eb6c0b5da6..ca254ca8d8189 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb @@ -5,6 +5,7 @@ Imports System.Runtime.CompilerServices Imports System.Threading Imports Microsoft.CodeAnalysis.Extensions +Imports Microsoft.CodeAnalysis.LanguageServices Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions @@ -397,9 +398,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions If IsAssociative(parentBinaryExpression.Kind) AndAlso expression.Kind = parentExpression.Kind Then - Return node.IsSafeToChangeAssociativity( - node.Expression, parentBinaryExpression.Left, - parentBinaryExpression.Right, semanticModel) + Return VisualBasicSemanticFacts.Instance.IsSafeToChangeAssociativity( + binaryExpression, parentBinaryExpression, semanticModel) End If End If diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index ded95e302ab56..dd0df2b375c6f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -19,6 +19,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Private Sub New() End Sub + Public ReadOnly Property SyntaxFacts As ISyntaxFacts Implements ISemanticFacts.SyntaxFacts + Get + Return VisualBasicSyntaxFacts.Instance + End Get + End Property + Public ReadOnly Property SupportsImplicitInterfaceImplementation As Boolean Implements ISemanticFacts.SupportsImplicitInterfaceImplementation Get Return False diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs index 9e0299091bcf9..8522643d5e697 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.LanguageServices; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Operations; @@ -24,7 +23,7 @@ internal sealed partial class CSharpSemanticFactsService : AbstractSemanticFacts { internal static readonly CSharpSemanticFactsService Instance = new(); - protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; + public override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance; private CSharpSemanticFactsService() diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs index 7f02fc0730b34..9269cc53c7ac0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.LanguageServices { internal abstract partial class AbstractSemanticFactsService : ISemanticFacts { - protected abstract ISyntaxFacts SyntaxFacts { get; } + public abstract ISyntaxFacts SyntaxFacts { get; } protected abstract ISemanticFacts SemanticFacts { get; } protected abstract SyntaxToken ToIdentifierToken(string identifier); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb index 5bee2fee605dc..9a94724fdd35f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb @@ -33,7 +33,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Shared ReadOnly Instance As New VisualBasicSemanticFactsService() - Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance + Public Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance Protected Overrides ReadOnly Property SemanticFacts As ISemanticFacts = VisualBasicSemanticFacts.Instance Private Sub New() From ce16500fb29dec9b1863e16c8aeab27e8efeb49d Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Mon, 14 Feb 2022 18:04:32 -0800 Subject: [PATCH 032/187] Clean up data stored in VS completion item property bag --- .../AsyncCompletion/CommitManager.cs | 22 +++---- .../AsyncCompletion/CompletionItemData.cs | 51 ++++++++++++++++ .../AsyncCompletion/CompletionSessionData.cs | 9 ++- .../AsyncCompletion/CompletionSource.cs | 21 ++----- .../ItemManager.CompletionListUpdater.cs | 60 +++++++++++-------- .../AsyncCompletion/ItemManager.cs | 19 +----- .../Handlers/Completion/CompletionHandler.cs | 4 +- .../CrefCompletionProvider.cs | 4 +- .../Completion/CommonCompletionItem.cs | 10 ++-- .../Providers/SymbolCompletionItem.cs | 4 +- 10 files changed, 121 insertions(+), 83 deletions(-) create mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs index 77c2a604b80fa..e51c5c5a5cb05 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs @@ -106,14 +106,18 @@ public AsyncCompletionData.CommitResult TryCommit( return CommitResultUnhandled; } - if (!item.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem)) + if (!CompletionItemData.TryGetData(item, out var itemData) || !itemData.IsProvidedByRoslynCompletionSource) { // Roslyn should not be called if the item committing was not provided by Roslyn. return CommitResultUnhandled; } + // Need the trigger snapshot to calculate the span when the commit changes to be applied. + // They should always be available from items provided by Roslyn CompletionSource. + Contract.ThrowIfFalse(itemData.TriggerLocation.HasValue); + var filterText = session.ApplicableToSpan.GetText(session.ApplicableToSpan.TextBuffer.CurrentSnapshot) + typeChar; - if (Helpers.IsFilterCharacter(roslynItem, typeChar, filterText)) + if (Helpers.IsFilterCharacter(itemData.RoslynItem, typeChar, filterText)) { // Returning Cancel means we keep the current session and consider the character for further filtering. return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.CancelCommit); @@ -126,21 +130,13 @@ public AsyncCompletionData.CommitResult TryCommit( // We can be called before for ShouldCommitCompletion. However, that call does not provide rules applied for the completion item. // Now we check for the commit character in the context of Rules that could change the list of commit characters. - if (!Helpers.IsStandardCommitCharacter(typeChar) && !IsCommitCharacter(serviceRules, roslynItem, typeChar)) + if (!Helpers.IsStandardCommitCharacter(typeChar) && !IsCommitCharacter(serviceRules, itemData.RoslynItem, typeChar)) { // Returning None means we complete the current session with a void commit. // The Editor then will try to trigger a new completion session for the character. return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None); } - if (!item.Properties.TryGetProperty(CompletionSource.TriggerLocation, out SnapshotPoint triggerLocation)) - { - // Need the trigger snapshot to calculate the span when the commit changes to be applied. - // They should always be available from items provided by Roslyn CompletionSource. - // Just to be defensive, if it's not found here, Roslyn should not make a commit. - return CommitResultUnhandled; - } - if (!sessionData.CompletionListSpan.HasValue) { return CommitResultUnhandled; @@ -148,7 +144,7 @@ public AsyncCompletionData.CommitResult TryCommit( var completionListSpan = sessionData.CompletionListSpan.Value; - var triggerDocument = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); + var triggerDocument = itemData.TriggerLocation.Value.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (triggerDocument == null) { return CommitResultUnhandled; @@ -170,7 +166,7 @@ public AsyncCompletionData.CommitResult TryCommit( var commitChar = typeChar == '\0' ? null : (char?)typeChar; return Commit( session, triggerDocument, completionService, subjectBuffer, - roslynItem, completionListSpan, commitChar, triggerLocation.Snapshot, serviceRules, + itemData.RoslynItem, completionListSpan, commitChar, itemData.TriggerLocation.Value.Snapshot, serviceRules, filterText, cancellationToken); } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs new file mode 100644 index 0000000000000..9fc99271b177d --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.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 Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; +using Microsoft.VisualStudio.Text; +using RoslynCompletionItem = Microsoft.CodeAnalysis.Completion.CompletionItem; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion +{ + internal sealed class CompletionItemData + { + private const string RoslynCompletionItemData = nameof(RoslynCompletionItemData); + + public RoslynCompletionItem RoslynItem { get; } + + public Optional TriggerLocation { get; } + + public bool IsProvidedByRoslynCompletionSource => TriggerLocation.HasValue; + + private CompletionItemData(RoslynCompletionItem roslynItem, SnapshotPoint? triggerLocation) + { + RoslynItem = roslynItem; + TriggerLocation = triggerLocation ?? new(); + } + + public static bool TryGetData(CompletionItem vsCompletionitem, out CompletionItemData data) + => vsCompletionitem.Properties.TryGetProperty(RoslynCompletionItemData, out data); + + public static RoslynCompletionItem GetOrAddDummyRoslynItem(CompletionItem vsItem) + { + if (vsItem.Properties.TryGetProperty(RoslynCompletionItemData, out CompletionItemData data)) + return data.RoslynItem; + + var roslynItem = CreateDummyRoslynItem(vsItem); + AddData(vsItem, roslynItem, triggerLocation: null); + + return roslynItem; + } + + public static void AddData(CompletionItem vsCompletionitem, RoslynCompletionItem roslynItem, SnapshotPoint? triggerLocation) + => vsCompletionitem.Properties[RoslynCompletionItemData] = new CompletionItemData(roslynItem, triggerLocation); + + private static RoslynCompletionItem CreateDummyRoslynItem(CompletionItem vsItem) + => RoslynCompletionItem.Create( + displayText: vsItem.DisplayText, + filterText: vsItem.FilterText, + sortText: vsItem.SortText, + displayTextSuffix: vsItem.Suffix); + } +} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs index d81b28d1cf5b7..3491716bdf5e0 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs @@ -18,8 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncComplet /// internal sealed class CompletionSessionData { - private const string SessionDataName = "RoslynCompletionSessionData"; - + private const string RoslynCompletionSessionData = nameof(RoslynCompletionSessionData); public bool TargetTypeFilterExperimentEnabled { get; set; } public bool TargetTypeFilterSelected { get; set; } public bool HasSuggestionItemOptions { get; set; } @@ -30,7 +29,11 @@ internal sealed class CompletionSessionData public Optional> ExpandedItemsTask { get; set; } public Optional> ExcludedCommitCharacters { get; set; } + private CompletionSessionData() + { + } + public static CompletionSessionData GetOrCreateSessionData(IAsyncCompletionSession session) - => session.Properties.GetOrCreateSingletonProperty(SessionDataName, static () => new CompletionSessionData()); + => session.Properties.GetOrCreateSingletonProperty(RoslynCompletionSessionData, static () => new CompletionSessionData()); } } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 56215f5bb52e7..fcfa305bf39bd 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -39,10 +39,6 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncComplet { internal sealed class CompletionSource : ForegroundThreadAffinitizedObject, IAsyncExpandingCompletionSource { - internal const string RoslynItem = nameof(RoslynItem); - internal const string TriggerLocation = nameof(TriggerLocation); - internal const string InsertionText = nameof(InsertionText); - internal const string Description = nameof(Description); internal const string PotentialCommitCharacters = nameof(PotentialCommitCharacters); internal const string NonBlockingCompletion = nameof(NonBlockingCompletion); @@ -444,7 +440,7 @@ public async Task GetExpandedCompletionContextAsync( var suggestionItemOptions = new AsyncCompletionData.SuggestionItemOptions( completionList.SuggestionModeItem.DisplayText, - completionList.SuggestionModeItem.Properties.TryGetValue(Description, out var description) ? description : string.Empty); + completionList.SuggestionModeItem.Properties.TryGetValue(CommonCompletionItem.Description, out var description) ? description : string.Empty); return (new(items, suggestionItemOptions, selectionHint: AsyncCompletionData.InitialSelectionHint.SoftSelection, filters), completionList); } @@ -490,13 +486,10 @@ private static void UpdateSessionData(CompletionSessionData sessionData, Complet if (item is null) throw new ArgumentNullException(nameof(item)); - if (!item.Properties.TryGetProperty(RoslynItem, out RoslynCompletionItem roslynItem) || - !item.Properties.TryGetProperty(TriggerLocation, out SnapshotPoint triggerLocation)) - { + if (!CompletionItemData.TryGetData(item, out var itemData) && !itemData.IsProvidedByRoslynCompletionSource) return null; - } - var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); + var document = itemData.TriggerLocation.Value.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) return null; @@ -506,7 +499,7 @@ private static void UpdateSessionData(CompletionSessionData sessionData, Complet var completionOptions = _globalOptions.GetCompletionOptions(document.Project.Language); var displayOptions = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); - var description = await service.GetDescriptionAsync(document, roslynItem, completionOptions, displayOptions, cancellationToken).ConfigureAwait(false); + var description = await service.GetDescriptionAsync(document, itemData.RoslynItem, completionOptions, displayOptions, cancellationToken).ConfigureAwait(false); if (description == null) return null; @@ -561,7 +554,7 @@ private VSCompletionItem Convert( // roslynItem generated by providers can contain an insertionText in a property bag. // We will not use it but other providers may need it. // We actually will calculate the insertion text once again when called TryCommit. - if (!roslynItem.Properties.TryGetValue(InsertionText, out var insertionText)) + if (!roslynItem.Properties.TryGetValue(CommonCompletionItem.InsertionText, out var insertionText)) { insertionText = roslynItem.DisplayText; } @@ -597,9 +590,7 @@ private VSCompletionItem Convert( automationText: roslynItem.AutomationText ?? roslynItem.DisplayText, attributeIcons: itemData.AttributeIcons); - item.Properties.AddProperty(RoslynItem, roslynItem); - item.Properties.AddProperty(TriggerLocation, initialTriggerLocation); - + CompletionItemData.AddData(item, roslynItem, initialTriggerLocation); return item; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index ee5229d24710a..3967d663d4584 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; +using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using Microsoft.VisualStudio.Text; using Roslyn.Utilities; @@ -33,7 +34,7 @@ internal partial class ItemManager private sealed class CompletionListUpdater { private readonly CompletionSessionData _sessionData; - private readonly AsyncCompletionSessionDataSnapshot _data; + private readonly AsyncCompletionSessionDataSnapshot _snapshotData; private readonly RecentItemsManager _recentItemsManager; private readonly ITrackingSpan _applicableToSpan; @@ -48,8 +49,8 @@ private sealed class CompletionListUpdater private readonly Func, string, ImmutableArray> _filterMethod; - private CompletionTriggerReason InitialTriggerReason => _data.InitialTrigger.Reason; - private CompletionTriggerReason UpdateTriggerReason => _data.Trigger.Reason; + private CompletionTriggerReason InitialTriggerReason => _snapshotData.InitialTrigger.Reason; + private CompletionTriggerReason UpdateTriggerReason => _snapshotData.Trigger.Reason; // We might need to handle large amount of items with import completion enabled, // so use a dedicated pool to minimize/avoid array allocations (especially in LOH) @@ -61,24 +62,24 @@ private sealed class CompletionListUpdater public CompletionListUpdater( ITrackingSpan applicableToSpan, CompletionSessionData sessionData, - AsyncCompletionSessionDataSnapshot data, + AsyncCompletionSessionDataSnapshot snapshotData, RecentItemsManager recentItemsManager, IGlobalOptionService globalOptions) { _sessionData = sessionData; - _data = data; + _snapshotData = snapshotData; _recentItemsManager = recentItemsManager; _applicableToSpan = applicableToSpan; - _filterText = applicableToSpan.GetText(_data.Snapshot); + _filterText = applicableToSpan.GetText(_snapshotData.Snapshot); - _hasSuggestedItemOptions = sessionData.HasSuggestionItemOptions || _data.DisplaySuggestionItem; + _hasSuggestedItemOptions = sessionData.HasSuggestionItemOptions || _snapshotData.DisplaySuggestionItem; // We prefer using the original snapshot, which should always be available from items provided by Roslyn's CompletionSource. // Only use data.Snapshot in the theoretically possible but rare case when all items we are handling are from some non-Roslyn CompletionSource. - var snapshotForDocument = TryGetInitialTriggerLocation(_data, out var intialTriggerLocation) + var snapshotForDocument = TryGetInitialTriggerLocation(_snapshotData, out var intialTriggerLocation) ? intialTriggerLocation.Snapshot - : _data.Snapshot; + : _snapshotData.Snapshot; _document = snapshotForDocument?.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); if (_document != null) @@ -179,7 +180,7 @@ private bool ShouldDismissCompletionListImmediately() // // We'll bring up the completion list here (as VB has completion on ). // If the user then types '3', we don't want to match against Int32. - if (_filterText.Length > 0 && char.IsNumber(_filterText[0]) && !IsAfterDot(_data.Snapshot, _applicableToSpan)) + if (_filterText.Length > 0 && char.IsNumber(_filterText[0]) && !IsAfterDot(_snapshotData.Snapshot, _applicableToSpan)) { // Dismiss the session. return true; @@ -208,7 +209,7 @@ static bool IsAfterDot(ITextSnapshot snapshot, ITrackingSpan applicableToSpan) private void AddCompletionItems(List> list, CancellationToken cancellationToken) { // FilterStateHelper is used to decide whether a given item should be included in the list based on the state of filter/expander buttons. - var filterHelper = new FilterStateHelper(_data.SelectedFilters); + var filterHelper = new FilterStateHelper(_snapshotData.SelectedFilters); filterHelper.LogTargetTypeFilterTelemetry(_sessionData); // We want to sort the items by pattern matching results while preserving the original alphabetical order for items with @@ -222,20 +223,28 @@ private void AddCompletionItems(List> list, Cancel var roslynFilterReason = Helpers.GetFilterReason(UpdateTriggerReason); // Filter items based on the selected filters and matching. - foreach (var item in _data.InitialSortedList) + foreach (var item in _snapshotData.InitialSortedList) { cancellationToken.ThrowIfCancellationRequested(); if (filterHelper.ShouldBeFilteredOut(item)) continue; - var roslynItem = GetOrAddRoslynCompletionItem(item); - if (CompletionHelper.TryCreateMatchResult(_completionHelper, roslynItem, item, _filterText, + if (CompletionItemData.TryGetData(item, out var itemData)) + { + if (CompletionHelper.TryCreateMatchResult(_completionHelper, itemData.RoslynItem, item, _filterText, roslynInitialTriggerKind, roslynFilterReason, _recentItemsManager.RecentItems, _highlightMatchingPortions, currentIndex, out var matchResult)) + { + list.Add(matchResult); + currentIndex++; + } + } + else { - list.Add(matchResult); - currentIndex++; + // All items passed in should contain a CompletionItemData object in the property bag, + // which is guaranteed in `ItemManager.SortCompletionListAsync`. + throw ExceptionUtilities.Unreachable; } } @@ -309,7 +318,7 @@ private void AddCompletionItems(List> list, Cancel } } - var typedChar = _data.Trigger.Character; + var typedChar = _snapshotData.Trigger.Character; // Check that it is a filter symbol. We can be called for a non-filter symbol. // If inserting a non-filter character (neither IsPotentialFilterCharacter, nor Helpers.IsFilterCharacter), @@ -470,7 +479,7 @@ static Span GetOffsetSpan(TextSpan span, RoslynCompletionItem item) // selection. return new FilteredCompletionModel( items: ImmutableArray.Empty, selectedItemIndex: 0, - filters: _data.SelectedFilters, selectionHint: UpdateSelectionHint.SoftSelected, centerSelection: true, uniqueItem: null); + filters: _snapshotData.SelectedFilters, selectionHint: UpdateSelectionHint.SoftSelected, centerSelection: true, uniqueItem: null); } private ImmutableArray GetUpdatedFilters(IReadOnlyList> items, CancellationToken cancellationToken) @@ -488,7 +497,7 @@ private ImmutableArray GetUpdatedFilters(IReadOnlyLis // When no items are available for a given filter, it becomes unavailable. // Expanders always appear available as long as it's presented. - return _data.SelectedFilters.SelectAsArray(n => n.WithAvailability(n.Filter is CompletionExpander || filters.Contains(n.Filter))); + return _snapshotData.SelectedFilters.SelectAsArray(n => n.WithAvailability(n.Filter is CompletionExpander || filters.Contains(n.Filter))); } /// @@ -548,10 +557,13 @@ static int GetRecentItemIndex(ImmutableArray recentItems, RoslynCompleti private static bool TryGetInitialTriggerLocation(AsyncCompletionSessionDataSnapshot data, out SnapshotPoint intialTriggerLocation) { - var firstItem = data.InitialSortedList.FirstOrDefault(static item => item.Properties.ContainsProperty(CompletionSource.TriggerLocation)); - if (firstItem != null) + foreach (var item in data.InitialSortedList) { - return firstItem.Properties.TryGetProperty(CompletionSource.TriggerLocation, out intialTriggerLocation); + if (CompletionItemData.TryGetData(item, out var itemData) && itemData.IsProvidedByRoslynCompletionSource) + { + intialTriggerLocation = itemData.TriggerLocation.Value; + return true; + } } intialTriggerLocation = default; @@ -651,7 +663,7 @@ private static bool IsPotentialFilterCharacter(char c) private ItemSelection UpdateSelectionBasedOnSuggestedDefaults(IReadOnlyList> items, ItemSelection itemSelection, CancellationToken cancellationToken) { // Editor doesn't provide us a list of "default" items. - if (_data.Defaults.IsDefaultOrEmpty) + if (_snapshotData.Defaults.IsDefaultOrEmpty) return itemSelection; // "Preselect" is only used when we have high confidence with the selection, so don't override it. @@ -708,7 +720,7 @@ private ItemSelection GetDefaultsMatch(IReadOnlyList> SortCompletionListAsync( } // Sort by default comparer of Roslyn CompletionItem - var sortedItems = data.InitialList.OrderBy(GetOrAddRoslynCompletionItem).ToImmutableArray(); + var sortedItems = data.InitialList.OrderBy(CompletionItemData.GetOrAddDummyRoslynItem).ToImmutableArray(); return Task.FromResult(sortedItems); } @@ -104,21 +103,5 @@ public Task> SortCompletionListAsync( var updater = new CompletionListUpdater(session.ApplicableToSpan, sessionData, data, _recentItemsManager, _globalOptions); return updater.UpdateCompletionList(cancellationToken); } - - private static RoslynCompletionItem GetOrAddRoslynCompletionItem(VSCompletionItem vsItem) - { - if (!vsItem.Properties.TryGetProperty(CompletionSource.RoslynItem, out RoslynCompletionItem roslynItem)) - { - roslynItem = RoslynCompletionItem.Create( - displayText: vsItem.DisplayText, - filterText: vsItem.FilterText, - sortText: vsItem.SortText, - displayTextSuffix: vsItem.Suffix); - - vsItem.Properties.AddProperty(CompletionSource.RoslynItem, roslynItem); - } - - return roslynItem; - } } } diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs index 1555f67c15d53..909c9bd1cdc0f 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs @@ -261,7 +261,9 @@ static async Task CreateCompletionItemAsync( // If the feature flag is off, return an InsertText. else { - completionItem.InsertText = item.Properties.ContainsKey("InsertionText") ? item.Properties["InsertionText"] : completeDisplayText; + completionItem.InsertText = item.Properties.ContainsKey(CommonCompletionItem.InsertionText) + ? item.Properties[CommonCompletionItem.InsertionText] + : completeDisplayText; } var commitCharacters = GetCommitCharacters(item, commitCharacterRulesCache, supportsVSExtensions); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs index b8e03bf4936ee..bf8f4dc50a7ae 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs @@ -417,11 +417,9 @@ private static CompletionItemRules GetRules(string displayText) } } - private const string InsertionTextProperty = "insertionText"; - protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) { - if (!selectedItem.Properties.TryGetValue(InsertionTextProperty, out var insertionText)) + if (!selectedItem.Properties.TryGetValue(CommonCompletionItem.InsertionText, out var insertionText)) { insertionText = selectedItem.DisplayText; } diff --git a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs index 0b1442552014b..490c8bc544300 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionItem.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.Linq; using Microsoft.CodeAnalysis.Tags; @@ -12,6 +11,9 @@ namespace Microsoft.CodeAnalysis.Completion { internal static class CommonCompletionItem { + public const string Description = nameof(Description); + public const string InsertionText = nameof(InsertionText); + public static CompletionItem Create( string displayText, string? displayTextSuffix, @@ -43,7 +45,7 @@ public static CompletionItem Create( properties ??= ImmutableDictionary.Empty; if (!description.IsDefault && description.Length > 0) { - properties = properties.Add("Description", EncodeDescription(description.ToTaggedText())); + properties = properties.Add(Description, EncodeDescription(description.ToTaggedText())); } return CompletionItem.Create( @@ -60,11 +62,11 @@ public static CompletionItem Create( } public static bool HasDescription(CompletionItem item) - => item.Properties.ContainsKey("Description"); + => item.Properties.ContainsKey(Description); public static CompletionDescription GetDescription(CompletionItem item) { - if (item.Properties.TryGetValue("Description", out var encodedDescription)) + if (item.Properties.TryGetValue(Description, out var encodedDescription)) { return DecodeDescription(encodedDescription); } diff --git a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs index 3ec0e32e4fcea..6df78671c6185 100644 --- a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs @@ -43,7 +43,7 @@ private static CompletionItem CreateWorker( if (insertionText != null) { - props = props.Add("InsertionText", insertionText); + props = props.Add(CommonCompletionItem.InsertionText, insertionText); } props = props.Add("ContextPosition", contextPosition.ToString()); @@ -254,7 +254,7 @@ public static int GetDescriptionPosition(CompletionItem item) => GetContextPosition(item); public static string GetInsertionText(CompletionItem item) - => item.Properties["InsertionText"]; + => item.Properties[CommonCompletionItem.InsertionText]; // COMPAT OVERLOAD: This is used by IntelliCode. public static CompletionItem CreateWithSymbolId( From 94da14b3215b6d9c96876684329efab6c050454d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 18:19:35 -0800 Subject: [PATCH 033/187] NRT --- .../UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs index 64370ae8d7ec8..fcb56ab24908b 100644 --- a/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs @@ -70,6 +70,9 @@ protected override async Task FixAllAsync( var methodDecl = diagnostic.AdditionalLocations[1].FindNode(cancellationToken); var method = semanticModel.GetDeclaredSymbol(methodDecl, cancellationToken); + if (method == null) + continue; + var methodBlock = declarationService.GetDeclarations(method)[0].GetSyntax(cancellationToken); var (accessesBase, members, _) = analyzer.GetHashedMembers(method, operation); From 20447867e8b842127eab435e246cb6801a6bea23 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 18:40:20 -0800 Subject: [PATCH 034/187] Simplify --- .../Extensions/ParenthesizedExpressionSyntaxExtensions.vb | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb index ca254ca8d8189..f3ba6e1193671 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb @@ -5,7 +5,6 @@ Imports System.Runtime.CompilerServices Imports System.Threading Imports Microsoft.CodeAnalysis.Extensions -Imports Microsoft.CodeAnalysis.LanguageServices Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions From 057735b95c7c6076decad0ebea063a1d6fa3c892 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 18:41:29 -0800 Subject: [PATCH 035/187] Fix --- .../Compiler/Core/CompilerExtensions.projitems | 2 +- ...xpressionSyntaxExtensions.cs => ISemanticFactsExtensions.cs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/{ParenthesizedExpressionSyntaxExtensions.cs => ISemanticFactsExtensions.cs} (100%) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems index 6a12b077411f7..6fb74a6ed131d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems @@ -376,7 +376,7 @@ - + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFactsExtensions.cs similarity index 100% rename from src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ParenthesizedExpressionSyntaxExtensions.cs rename to src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFactsExtensions.cs From 5b93b4d6206f274d613ab5c22eda069c49730c32 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 14 Feb 2022 18:41:53 -0800 Subject: [PATCH 036/187] Fix --- .../Extensions/ParenthesizedExpressionSyntaxExtensions.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb index f3ba6e1193671..cf067ef59aba4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Extensions/ParenthesizedExpressionSyntaxExtensions.vb @@ -4,7 +4,7 @@ Imports System.Runtime.CompilerServices Imports System.Threading -Imports Microsoft.CodeAnalysis.Extensions +Imports Microsoft.CodeAnalysis.LanguageServices Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions From 7f83c7e6558e9423ba2401ca33bea60be5e42ed8 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Tue, 15 Feb 2022 13:15:45 +0000 Subject: [PATCH 037/187] Update dependencies from https://github.com/dotnet/arcade build 20220214.7 Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.Helix.Sdk From Version 7.0.0-beta.22080.1 -> To Version 7.0.0-beta.22114.7 --- eng/Version.Details.xml | 8 ++++---- global.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 84c64000563d3..08e6a33b2ac04 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - ff6cc4e9c3eef575f62a33a642ca80e79d27c9bb + 6d977266bcc193caedb60a27ae44d14d9a11a040 https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - ff6cc4e9c3eef575f62a33a642ca80e79d27c9bb + 6d977266bcc193caedb60a27ae44d14d9a11a040 diff --git a/global.json b/global.json index 21705bd705307..4d865d78f5387 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22111.10", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22111.10" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22114.7", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22114.7" } } From 3efcf4ff1d7d7bbae91338d26e458e09a3c252a8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 15 Feb 2022 10:31:19 -0800 Subject: [PATCH 038/187] Fixup --- ...arpGenerateDefaultConstructorsCodeFixProvider.cs | 4 ++++ ...ractGenerateDefaultConstructorCodeFixProvider.cs | 13 ++++++++----- ...sicGenerateDefaultConstructorsCodeFixProvider.vb | 5 +++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Features/CSharp/Portable/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsCodeFixProvider.cs index ed8cb0ed896a3..3325cec6f6bf4 100644 --- a/src/Features/CSharp/Portable/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/GenerateDefaultConstructors/CSharpGenerateDefaultConstructorsCodeFixProvider.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Composition; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GenerateDefaultConstructors; using Microsoft.CodeAnalysis.Host.Mef; @@ -26,5 +27,8 @@ public CSharpGenerateDefaultConstructorsCodeFixProvider() public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(CS1729, CS7036, CS8983); + + protected override SyntaxToken? TryGetTypeName(SyntaxNode typeDeclaration) + => (typeDeclaration as BaseTypeDeclarationSyntax)?.Identifier; } } diff --git a/src/Features/Core/Portable/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorCodeFixProvider.cs b/src/Features/Core/Portable/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorCodeFixProvider.cs index f18cffa501219..7fcb341ee1666 100644 --- a/src/Features/Core/Portable/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorCodeFixProvider.cs +++ b/src/Features/Core/Portable/GenerateDefaultConstructors/AbstractGenerateDefaultConstructorCodeFixProvider.cs @@ -15,6 +15,8 @@ internal abstract class AbstractGenerateDefaultConstructorCodeFixProvider : Code { public override FixAllProvider? GetFixAllProvider() => null; + protected abstract SyntaxToken? TryGetTypeName(SyntaxNode typeDeclaration); + public override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; @@ -23,17 +25,18 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) if (diagnostic == null) return; - var syntaxFacts = document.GetRequiredLanguageService(); - var headerFacts = document.GetRequiredLanguageService(); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var headerFacts = document.GetRequiredLanguageService(); if (!headerFacts.IsOnTypeHeader(root, diagnostic.Location.SourceSpan.Start, fullHeader: true, out var typeDecl)) return; - var typeName = syntaxFacts.GetIdentifierOfTypeDeclaration(typeDecl); + var typeName = TryGetTypeName(typeDecl); + if (typeName == null) + return; + var service = document.GetRequiredLanguageService(); var actions = await service.GenerateDefaultConstructorsAsync( - document, new TextSpan(typeName.Span.Start, 0), forRefactoring: false, cancellationToken).ConfigureAwait(false); + document, new TextSpan(typeName.Value.Span.Start, 0), forRefactoring: false, cancellationToken).ConfigureAwait(false); context.RegisterFixes(actions, diagnostic); } } diff --git a/src/Features/VisualBasic/Portable/GenerateDefaultConstructors/VisualBasicGenerateDefaultConstructorsCodeFixProvider.vb b/src/Features/VisualBasic/Portable/GenerateDefaultConstructors/VisualBasicGenerateDefaultConstructorsCodeFixProvider.vb index 62c7fcb5574ee..ae87499be8307 100644 --- a/src/Features/VisualBasic/Portable/GenerateDefaultConstructors/VisualBasicGenerateDefaultConstructorsCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/GenerateDefaultConstructors/VisualBasicGenerateDefaultConstructorsCodeFixProvider.vb @@ -7,6 +7,7 @@ Imports System.Composition Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.GenerateDefaultConstructors Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateDefaultConstructors @@ -23,5 +24,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateDefaultConstructors Public Overrides ReadOnly Property FixableDiagnosticIds As Immutable.ImmutableArray(Of String) = ImmutableArray.Create(BC30387, BC40056) + + Protected Overrides Function TryGetTypeName(typeDeclaration As SyntaxNode) As SyntaxToken? + Return TryCast(typeDeclaration, TypeBlockSyntax)?.BlockStatement.Identifier + End Function End Class End Namespace From 84a59ed2ab2c0b14bed62d50a5b53ceaf230bdb1 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 15 Feb 2022 10:52:24 -0800 Subject: [PATCH 039/187] Add test --- ...veUnnecessaryExpressionParenthesesTests.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs index 15f33951fdf48..36c6d019fa0b7 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryParentheses/RemoveUnnecessaryExpressionParenthesesTests.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. -#nullable disable - using System; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryParentheses; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; @@ -2771,6 +2770,22 @@ void M(int a) }", parameters: new TestParameters(options: RemoveAllUnnecessaryParentheses)); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] + [WorkItem(45100, "https://github.com/dotnet/roslyn/issues/45100")] + public async Task TestArithmeticOverflow1_CompilationOption() + { + await TestMissingAsync( +@"class C +{ + void M(int a) + { + return a + $$(int.MaxValue + -int.MaxValue); + } +}", parameters: new TestParameters( + options: RemoveAllUnnecessaryParentheses, + compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, checkOverflow: true))); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] [WorkItem(45100, "https://github.com/dotnet/roslyn/issues/45100")] public async Task TestArithmeticOverflow2() From 950b8c1998f4916c9adcf1bcc20d8fe3b6a3a38e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 15 Feb 2022 10:53:40 -0800 Subject: [PATCH 040/187] Simplify --- .../Extensions/ParenthesizedExpressionSyntaxExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index 2657d54997387..a9c212b8901e4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -414,11 +414,11 @@ private static bool RemovalChangesAssociation( // 3) for logical operators the result will always be the same (there are // additional conditions that are checked for non-logical operators). if (IsAssociative(parentBinaryExpression.Kind()) && - node.Expression.Kind() == parentBinaryExpression.Kind() && - parentBinaryExpression.Right == node) + parentBinaryExpression.Right == node && + node.Expression.IsKind(parentBinaryExpression.Kind(), out BinaryExpressionSyntax? nodeBinary)) { return !CSharpSemanticFacts.Instance.IsSafeToChangeAssociativity( - (BinaryExpressionSyntax)node.Expression, parentBinaryExpression, semanticModel); + nodeBinary, parentBinaryExpression, semanticModel); } // Null-coalescing is right associative; removing parens from the LHS changes the association. From aaef347144b7e221dbe1bc41db1abf4a7772e112 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 15 Feb 2022 11:14:07 -0800 Subject: [PATCH 041/187] Fix test --- .../AbstractCodeGenerationTests.cs | 20 +++++++++---------- .../ExpressionGenerationTests.cs | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/Test/CodeGeneration/AbstractCodeGenerationTests.cs b/src/EditorFeatures/Test/CodeGeneration/AbstractCodeGenerationTests.cs index 3d4cfb7b8e9fc..45ff92a6ef52f 100644 --- a/src/EditorFeatures/Test/CodeGeneration/AbstractCodeGenerationTests.cs +++ b/src/EditorFeatures/Test/CodeGeneration/AbstractCodeGenerationTests.cs @@ -2,19 +2,16 @@ // 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 Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.VisualBasic.Syntax; -using Microsoft.VisualStudio.Composition; -using Microsoft.VisualStudio.LanguageServices; using Roslyn.Test.Utilities; using Xunit; @@ -31,18 +28,19 @@ private static SyntaxNode Simplify( var projectId = ProjectId.CreateNewId(); var project = workspace.CurrentSolution - .AddProject(projectId, languageName, $"{languageName}.dll", languageName).GetProject(projectId); + .AddProject(projectId, languageName, $"{languageName}.dll", languageName).GetRequiredProject(projectId); var normalizedSyntax = syntaxNode.NormalizeWhitespace().ToFullString(); var document = project.AddMetadataReference(TestMetadata.Net451.mscorlib) .AddDocument("Fake Document", SourceText.From(normalizedSyntax)); + var root = document.GetRequiredSyntaxRootAsync(default).AsTask().Result; var annotatedDocument = document.WithSyntaxRoot( - document.GetSyntaxRootAsync().Result.WithAdditionalAnnotations(Simplification.Simplifier.Annotation)); + root.WithAdditionalAnnotations(Simplifier.Annotation)); - var simplifiedDocument = Simplification.Simplifier.ReduceAsync(annotatedDocument).Result; + var simplifiedDocument = Simplifier.ReduceAsync(annotatedDocument).Result; - var rootNode = simplifiedDocument.GetSyntaxRootAsync().Result; + var rootNode = simplifiedDocument.GetRequiredSyntaxRootAsync(default).AsTask().Result; return rootNode; } @@ -78,7 +76,7 @@ internal static void Test( if (cs != null || csSimple != null) { - var codeDefFactory = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(); + var codeDefFactory = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetRequiredService(); var node = nodeCreator(codeDefFactory); node = node.NormalizeWhitespace(); @@ -99,7 +97,7 @@ internal static void Test( if (vb != null || vbSimple != null) { - var codeDefFactory = workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetService(); + var codeDefFactory = workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetRequiredService(); var node = nodeCreator(codeDefFactory); node = node.NormalizeWhitespace(); diff --git a/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs b/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs index 6bdc4a7d09052..e66fa8db8dc54 100644 --- a/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs +++ b/src/EditorFeatures/Test/CodeGeneration/ExpressionGenerationTests.cs @@ -181,7 +181,7 @@ public void TestAddExpression2() cs: "(1) + ((2) + (3))", csSimple: "1 + 2 + 3", vb: "(1) + ((2) + (3))", - vbSimple: "1 + 2 + 3"); + vbSimple: "1 + (2 + 3)"); } [Fact] @@ -224,7 +224,7 @@ public void TestMultiplyExpression2() cs: "(1) * ((2) * (3))", csSimple: "1 * 2 * 3", vb: "(1) * ((2) * (3))", - vbSimple: "1 * 2 * 3"); + vbSimple: "1 * (2 * 3)"); } [Fact] From f767c709cf686e1abe7f4fbfae1f30c7d43bd528 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 15 Feb 2022 11:16:46 -0800 Subject: [PATCH 042/187] Fix warning on conversion of anon func with implicit parameters (#59522) --- .../Binder/Binder.QueryUnboundLambdaState.cs | 1 - .../CSharp/Portable/Binder/Binder_Lambda.cs | 2 +- .../Portable/BoundTree/UnboundLambda.cs | 2 -- .../Portable/FlowAnalysis/NullableWalker.cs | 12 +++++++---- .../Semantics/NullableReferenceTypesTests.cs | 21 +++++++++++++++++++ 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs index a65b7103def98..4f5f4e34a4531 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.QueryUnboundLambdaState.cs @@ -34,7 +34,6 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap, public override bool ParameterIsDiscard(int index) { return false; } public override bool ParameterIsNullChecked(int index) { return false; } public override SyntaxList ParameterAttributes(int index) => default; - public override bool HasNames { get { return true; } } public override bool HasSignature { get { return true; } } public override bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 68b5133be2f19..83e729bc1f0e8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -335,7 +335,7 @@ private UnboundLambda BindAnonymousFunction(AnonymousFunctionExpressionSyntax sy // are reported now. ModifierUtils.ToDeclarationModifiers(syntax.Modifiers, diagnostics.DiagnosticBag ?? new DiagnosticBag()); - if (data.HasNames) + if (data.HasSignature) { var binder = new LocalScopeBinder(this); bool allowShadowingNames = binder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions); diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 76baf682e82d8..fce66b2e44abf 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -526,7 +526,6 @@ internal UnboundLambdaState WithCaching(bool includeCache) public abstract bool HasExplicitlyTypedParameterList { get; } public abstract int ParameterCount { get; } public abstract bool IsAsync { get; } - public abstract bool HasNames { get; } public abstract bool IsStatic { get; } public abstract Location ParameterLocation(int index); public abstract TypeWithAnnotations ParameterTypeWithAnnotations(int index); @@ -1357,7 +1356,6 @@ internal PlainUnboundLambdaState( _isStatic = isStatic; } - public override bool HasNames { get { return !_parameterNames.IsDefault; } } public override bool HasSignature { get { return !_parameterNames.IsDefault; } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 6ff752fbfba82..d052ecd9e89cf 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7570,10 +7570,14 @@ void reportBadDelegateReturn(BindingDiagnosticBag bag, MethodSymbol targetInvoke void reportBadDelegateParameter(BindingDiagnosticBag bag, MethodSymbol sourceInvokeMethod, MethodSymbol targetInvokeMethod, ParameterSymbol parameterSymbol, bool topLevel, Location location) { - ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, location, - unboundLambda.ParameterName(parameterSymbol.Ordinal), - unboundLambda.MessageID.Localize(), - delegateType); + // For anonymous functions with implicit parameters, no need to report this since the parameters can't be referenced + if (unboundLambda.HasSignature) + { + ReportDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, location, + unboundLambda.ParameterName(parameterSymbol.Ordinal), + unboundLambda.MessageID.Localize(), + delegateType); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 6456143bb87a9..01f4c859d344b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -155949,5 +155949,26 @@ void M() Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "d2").WithArguments("ref readonly string? D.Invoke()", "D").WithLocation(9, 28) ); } + + [Fact, WorkItem(59336, "https://github.com/dotnet/roslyn/issues/59336")] + public void DelegateConversionWithImplicitParameters() + { + var source = @" +#nullable enable + +public delegate void MyDelegate([System.Diagnostics.CodeAnalysis.MaybeNull] object first, [System.Diagnostics.CodeAnalysis.MaybeNull] object second); + +class C +{ + public event MyDelegate? MyEvent; + void M() + { + MyEvent += delegate { }; + MyEvent?.Invoke(new object(), new object()); + } +}"; + var comp = CreateCompilation(new[] { source, MaybeNullAttributeDefinition }); + comp.VerifyDiagnostics(); + } } } From b47ceb1a23681d085343821a1af05a74daf3a6db Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 15 Feb 2022 11:59:02 -0800 Subject: [PATCH 043/187] Enable analysis of source generated files for test --- .../Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index 43ed55aa90686..6b7479e36a830 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -860,7 +860,9 @@ void M() sourceGeneratedFiles = Array.Empty(); } - using var workspace = TestWorkspace.CreateCSharp(files, sourceGeneratedFiles, composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService))); + using var workspace = TestWorkspace.CreateCSharp(files, sourceGeneratedFiles, composition: s_editorFeaturesCompositionWithMockDiagnosticUpdateSourceRegistrationService.AddParts(typeof(TestDocumentTrackingService), typeof(TestSyntaxTreeConfigurationService))); + var syntaxTreeConfigurationService = workspace.GetService(); + syntaxTreeConfigurationService.EnableOpeningSourceGeneratedFilesInWorkspace = true; var options = workspace.Options.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope); workspace.SetOptions(options); From 3294cefa6ed212369759695ce16945909f2ac5fe Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 15 Feb 2022 13:16:23 -0800 Subject: [PATCH 044/187] AbstractFlowPass.VisitIsPatternExpression - add handling for list-pattern specific patterns (#59285) --- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 10 +++ .../PatternMatchingTests_ListPatterns.cs | 77 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 82b3f200ad694..edf875988ef6a 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -917,6 +917,12 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node { Debug.Assert(!IsConditionalState); + // Local functions below need to handle all patterns that come through here + Debug.Assert(node.Pattern is + BoundTypePattern or BoundRecursivePattern or BoundITuplePattern or BoundRelationalPattern or + BoundDeclarationPattern or BoundConstantPattern or BoundNegatedPattern or BoundBinaryPattern or + BoundDeclarationPattern or BoundDiscardPattern or BoundListPattern or BoundSlicePattern); + bool negated = node.Pattern.IsNegated(out var pattern); Debug.Assert(negated == node.IsNegated); @@ -976,6 +982,8 @@ static bool patternMatchesNull(BoundPattern pattern) case BoundRelationalPattern: case BoundDeclarationPattern { IsVar: false }: case BoundConstantPattern { ConstantValue: { IsNull: false } }: + case BoundListPattern: + case BoundSlicePattern: // Only occurs in error cases return false; case BoundConstantPattern { ConstantValue: { IsNull: true } }: return true; @@ -1035,6 +1043,8 @@ static bool patternMatchesNull(BoundPattern pattern) case BoundITuplePattern: case BoundRelationalPattern: case BoundDeclarationPattern: + case BoundListPattern: + case BoundSlicePattern: // Only occurs in error cases return null; default: throw ExceptionUtilities.UnexpectedValue(pattern.Kind); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index d151cbd684a47..9daac7b509bb6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -8380,4 +8380,81 @@ public void ListPattern_NullTestOnSlice() Diagnostic(ErrorCode.ERR_SwitchCaseSubsumed, "[1,2,3]").WithLocation(15, 10) ); } + + [Fact, WorkItem(58738, "https://github.com/dotnet/roslyn/issues/58738")] + public void ListPattern_AbstractFlowPass_isBoolTest() + { + var source = @" +var a = new[] { 1 }; + +if (a is [var x] and x is [1]) +{ +} + +if ((a is [var y] and y) is .. 1) +{ +} + +var b = new[] { true }; +if ((b is [var z] and z) is [true]) +{ +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(new[] { source, TestSources.GetSubArray }); + comp.VerifyEmitDiagnostics( + // (4,22): error CS0029: Cannot implicitly convert type 'int' to 'int[]' + // if (a is [var x] and x is [1]) + Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("int", "int[]").WithLocation(4, 22), + // (4,27): error CS0021: Cannot apply indexing with [] to an expression of type 'bool' + // if (a is [var x] and x is [1]) + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[1]").WithArguments("bool").WithLocation(4, 27), + // (8,23): error CS0029: Cannot implicitly convert type 'int' to 'int[]' + // if ((a is [var y] and y) is .. 1) + Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("int", "int[]").WithLocation(8, 23), + // (8,29): error CS0021: Cannot apply indexing with [] to an expression of type 'bool' + // if ((a is [var y] and y) is .. 1) + Diagnostic(ErrorCode.ERR_BadIndexLHS, ".. 1").WithArguments("bool").WithLocation(8, 29), + // (13,23): error CS0029: Cannot implicitly convert type 'bool' to 'bool[]' + // if ((b is [var z] and z) is [true]) + Diagnostic(ErrorCode.ERR_NoImplicitConv, "z").WithArguments("bool", "bool[]").WithLocation(13, 23), + // (13,29): error CS0021: Cannot apply indexing with [] to an expression of type 'bool' + // if ((b is [var z] and z) is [true]) + Diagnostic(ErrorCode.ERR_BadIndexLHS, "[true]").WithArguments("bool").WithLocation(13, 29) + ); + } + + [Fact, WorkItem(58738, "https://github.com/dotnet/roslyn/issues/58738")] + public void ListPattern_AbstractFlowPass_patternMatchesNull() + { + var source = @" +var a = new[] { 1 }; + +if (a?.M(out var i) is [1]) + i.ToString(); +else + i.ToString(); // 1 + +if (a?.M(out var j) is .. 1) // 2, 3 + j.ToString(); +else + j.ToString(); + +public static class Extension +{ + public static T M(this T t, out int i) => throw null; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(new[] { source, TestSources.GetSubArray }); + comp.VerifyEmitDiagnostics( + // (7,5): error CS0165: Use of unassigned local variable 'i' + // i.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "i").WithArguments("i").WithLocation(7, 5), + // (9,24): error CS8980: Slice patterns may only be used once and directly inside a list pattern. + // if (a?.M(out var j) is .. 1) // 2, 3 + Diagnostic(ErrorCode.ERR_MisplacedSlicePattern, ".. 1").WithLocation(9, 24), + // (9,27): error CS0029: Cannot implicitly convert type 'int' to 'int[]' + // if (a?.M(out var j) is .. 1) // 2, 3 + Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "int[]").WithLocation(9, 27) + ); + } } From 10a9d97e32f2ded7b7ea0cfbe4bc3ba546528af9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 15 Feb 2022 13:52:58 -0800 Subject: [PATCH 045/187] Fix PythiaOptions (#59565) --- .../Core/Portable/ExternalAccess/Pythia/Api/PythiaOptions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaOptions.cs b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaOptions.cs index 290030d5a684b..d8fc85227e522 100644 --- a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaOptions.cs +++ b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaOptions.cs @@ -17,11 +17,11 @@ internal static class PythiaOptions { public const string LocalRegistryPath = @"Roslyn\Internal\OnOff\Features\"; - public static readonly Option2 ShowDebugInfo = new( + public static readonly Option ShowDebugInfo = new( "InternalFeatureOnOffOptions", nameof(ShowDebugInfo), defaultValue: false, storageLocation: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(ShowDebugInfo))); - public static readonly Option2 RemoveRecommendationLimit = new( + public static readonly Option RemoveRecommendationLimit = new( "InternalFeatureOnOffOptions", nameof(RemoveRecommendationLimit), defaultValue: false, storageLocation: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(RemoveRecommendationLimit))); } From 281cafdbd8c27e4c32c4b999b1b187a58ed61919 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Tue, 15 Feb 2022 14:13:25 -0800 Subject: [PATCH 046/187] Fix --- .../AsyncCompletion/CompletionItemData.cs | 12 +----------- .../IntelliSense/AsyncCompletion/CompletionSource.cs | 2 +- .../TestUtilities2/Intellisense/TestState.vb | 12 ++++++++++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs index 9fc99271b177d..d2edab010c587 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs @@ -8,22 +8,12 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion { - internal sealed class CompletionItemData + internal sealed record class CompletionItemData(RoslynCompletionItem RoslynItem, SnapshotPoint? TriggerLocation) { private const string RoslynCompletionItemData = nameof(RoslynCompletionItemData); - public RoslynCompletionItem RoslynItem { get; } - - public Optional TriggerLocation { get; } - public bool IsProvidedByRoslynCompletionSource => TriggerLocation.HasValue; - private CompletionItemData(RoslynCompletionItem roslynItem, SnapshotPoint? triggerLocation) - { - RoslynItem = roslynItem; - TriggerLocation = triggerLocation ?? new(); - } - public static bool TryGetData(CompletionItem vsCompletionitem, out CompletionItemData data) => vsCompletionitem.Properties.TryGetProperty(RoslynCompletionItemData, out data); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index fcfa305bf39bd..af095b76d8b54 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -460,7 +460,7 @@ private static void UpdateSessionData(CompletionSessionData sessionData, Complet var excludedCommitCharacters = GetExcludedCommitCharacters(completionList.Items); if (excludedCommitCharacters.Length > 0) { - if (sessionData.CompletionListSpan.HasValue) + if (sessionData.ExcludedCommitCharacters.HasValue) { excludedCommitCharacters = excludedCommitCharacters.Union(sessionData.ExcludedCommitCharacters.Value).ToImmutableArray(); } diff --git a/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb b/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb index f7e9b9e8dde1d..81bf48edb6700 100644 --- a/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb +++ b/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.Completion Imports Microsoft.CodeAnalysis.Editor.CommandHandlers Imports Microsoft.CodeAnalysis.Editor.CSharp.CompleteStatement Imports Microsoft.CodeAnalysis.Editor.Implementation.Formatting +Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities Imports Microsoft.CodeAnalysis.LanguageServices @@ -26,7 +27,6 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Inherits AbstractCommandHandlerTestState Private Const timeoutMs = 60000 - Friend Const RoslynItem = "RoslynItem" Friend ReadOnly EditorCompletionCommandHandler As ICommandHandler Friend ReadOnly CompletionPresenterProvider As ICompletionPresenterProvider @@ -502,7 +502,15 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense End Function Private Shared Function GetRoslynCompletionItem(item As Data.CompletionItem) As CompletionItem - Return If(item IsNot Nothing, DirectCast(item.Properties(RoslynItem), CompletionItem), Nothing) + If (item Is Nothing) Then + Return Nothing + End If + + Dim roslynItemData As CompletionItemData = Nothing + If (CompletionItemData.TryGetData(item, roslynItemData) = False) Then + Return Nothing + End If + Return roslynItemData.RoslynItem End Function Public Sub RaiseFiltersChanged(args As ImmutableArray(Of Data.CompletionFilterWithState)) From bbf4733de7cba9f8a297c2bbc0d38ab68b12f17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 15 Feb 2022 15:05:21 -0800 Subject: [PATCH 047/187] Add missing project references to Core deployment (#59566) --- ...oft.CodeAnalysis.Remote.ServiceHub.CoreComponents.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Remote/ServiceHub.CoreComponents/Microsoft.CodeAnalysis.Remote.ServiceHub.CoreComponents.csproj b/src/Workspaces/Remote/ServiceHub.CoreComponents/Microsoft.CodeAnalysis.Remote.ServiceHub.CoreComponents.csproj index 9cf3249154d0f..5b8952ce8de4d 100644 --- a/src/Workspaces/Remote/ServiceHub.CoreComponents/Microsoft.CodeAnalysis.Remote.ServiceHub.CoreComponents.csproj +++ b/src/Workspaces/Remote/ServiceHub.CoreComponents/Microsoft.CodeAnalysis.Remote.ServiceHub.CoreComponents.csproj @@ -4,11 +4,15 @@ Exe net6.0 - + false + + From 0098764330d1e22c43ead6065e4ed5f8aae09195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 15 Feb 2022 15:06:32 -0800 Subject: [PATCH 048/187] Revert "Revert "Make solution options global (#59168)" (#59537)" (#59567) This reverts commit 4de93eaaa560aaec7e86eb1623b9d7acb4da2d68. --- .../CSharp/GoToBase/CSharpGoToBaseService.cs | 6 +- .../CSharpGoToDefinitionService.cs | 1 + .../GoToDefinition/CSharpGoToSymbolService.cs | 3 +- .../CopyPasteAndPrintingClassifierTests.cs | 9 +- .../DiagnosticAnalyzerQuickInfoSourceTests.cs | 3 +- .../SyntacticQuickInfoSourceTests.cs | 9 +- .../FindBaseSymbolsCommandHandler.cs | 8 +- .../FindDerivedSymbolsCommandHandler.cs | 8 +- .../FindExtensionMethodsCommandHandler.cs | 8 +- .../FindImplementingMembersCommandHandler.cs | 8 +- .../FindMemberOverloadsCommandHandler.cs | 7 +- .../Core.Wpf/InlineHints/InlineHintsTag.cs | 7 +- .../FindUsages/FindUsagesOptionsStorage.cs | 6 +- .../AbstractGoToDefinitionService.cs | 1 + .../AbstractGoToSymbolService.cs | 1 + .../GoToDefinition/GoToDefinitionHelpers.cs | 1 + ...assificationBufferTaggerProvider.Tagger.cs | 27 +-- ...ntingClassificationBufferTaggerProvider.cs | 8 +- .../SemanticClassificationUtilities.cs | 3 +- ...emanticClassificationViewTaggerProvider.cs | 5 +- .../Formatting/FormattingOptionsMetadata.cs | 17 +- .../AsyncCompletion/CompletionSource.cs | 11 +- .../QuickInfo/IntellisenseQuickInfoBuilder.cs | 15 +- .../IntellisenseQuickInfoBuilderContext.cs | 4 + ...QuickInfoSourceProvider.QuickInfoSource.cs | 15 +- .../QuickInfo/QuickInfoSourceProvider.cs | 8 +- .../Completion/CompletionResolveHandler.cs | 6 +- .../Handlers/Hover/HoverHandler.cs | 21 +- .../References/FindUsagesLSPContext.cs | 221 +++++++++--------- .../InlineHintsDataTaggerProvider.cs | 26 +-- .../InlineHints/InlineHintsOptionsStorage.cs | 114 +++++++++ .../Core/QuickInfo/QuickInfoOptionsStorage.cs | 26 +++ .../SymbolDescriptionOptionsStorage.cs | 18 ++ .../GoToDefinition/GoToDefinitionTestsBase.vb | 25 +- ...stractIntellisenseQuickInfoBuilderTests.vb | 5 +- .../SymbolDescriptionServiceTests.vb | 2 +- .../Classification/AbstractClassifierTests.cs | 2 +- .../AbstractCompletionProviderTests.cs | 8 +- .../TestUtilities2/Intellisense/TestState.vb | 3 +- .../GoToBase/VisualBasicGoToBaseService.vb | 2 + .../VisualBasicGoToDefinitionService.vb | 1 + .../VisualBasicGoToSymbolService.vb | 3 +- .../ClassificationOptionsStorage.cs | 30 +++ ...stractFindUsagesService.ProgressAdapter.cs | 9 +- ...ctFindUsagesService_FindImplementations.cs | 2 +- .../IDefinitionsAndReferencesFactory.cs | 169 +++++++------- .../GoToBase/AbstractGoToBaseService.cs | 13 +- .../InheritanceMarginServiceHelpers.cs | 16 +- .../InlineHints/InlineHintsOptions.cs | 19 +- .../InlineParameterHintsOptions.cs | 109 ++------- .../InlineHints/InlineTypeHintsOptions.cs | 69 +----- .../SymbolDescriptionOptions.cs | 9 - .../Portable/QuickInfo/QuickInfoOptions.cs | 49 +--- .../StackTraceExplorerService.cs | 1 + .../StackTraceExplorerUtilities.cs | 13 +- .../SemanticTokens/SemanticTokensHelpers.cs | 3 +- .../SemanticTokensRangeHandler.cs | 12 +- .../SemanticTokensRangeTests.cs | 16 +- .../Lsif/Generator/CompilerInvocation.cs | 12 +- src/Features/Lsif/Generator/Generator.cs | 26 ++- .../Lsif/Generator/GeneratorOptions.cs | 18 ++ ...LanguageServerIndexFormat.Generator.csproj | 4 + src/Features/Lsif/Generator/Program.cs | 4 +- .../GeneratorTest/Utilities/TestLsifOutput.vb | 2 +- .../OmniSharpInlineHintsService.cs | 50 ---- .../Razor/IRazorDocumentExcerptService.cs | 6 - .../RazorDocumentExcerptServiceWrapper.cs | 28 +-- .../RazorDocumentServiceProviderWrapper.cs | 14 +- .../RegexClassifierBenchmarks.cs | 3 +- .../ClassificationBenchmarks.cs | 3 +- .../Options/AdvancedOptionPageControl.xaml.cs | 44 ++-- .../RemoteCodeLensReferencesService.cs | 14 +- ...bstractTableDataSourceFindUsagesContext.cs | 4 +- .../Entries/DocumentSpanEntry.cs | 3 +- ...ntainedDocument.DocumentServiceProvider.cs | 7 +- .../Implementation/Venus/ContainedLanguage.cs | 31 +-- .../Venus/ContainedLanguage`2.cs | 71 ------ .../SyntaxTreeConfigurationOptions.cs | 39 ---- ...ualStudioSyntaxTreeConfigurationService.cs | 19 +- .../ValueTrackedTreeItemViewModel.cs | 10 +- .../ValueTrackingCommandHandler.cs | 10 +- .../Core/Test/Venus/DocumentServiceTests.vb | 9 +- .../Venus/DocumentService_IntegrationTests.vb | 4 +- .../Options/AdvancedOptionPageControl.xaml.vb | 30 +-- .../Extensions/SymbolExtensions.cs | 3 +- .../Completion/CompletionResolveHandler.cs | 10 +- .../Handler/Hover/HoverHandler.cs | 12 +- .../Classification/ClassificationOptions.cs | 55 +---- .../Portable/Classification/Classifier.cs | 10 +- .../IDocumentExcerptService.cs | 6 +- 90 files changed, 804 insertions(+), 978 deletions(-) create mode 100644 src/EditorFeatures/Core/InlineHints/InlineHintsOptionsStorage.cs create mode 100644 src/EditorFeatures/Core/QuickInfo/QuickInfoOptionsStorage.cs create mode 100644 src/EditorFeatures/Core/SymbolDisplay/SymbolDescriptionOptionsStorage.cs create mode 100644 src/Features/Core/Portable/Completion/ClassificationOptionsStorage.cs create mode 100644 src/Features/Lsif/Generator/GeneratorOptions.cs delete mode 100644 src/Tools/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsService.cs delete mode 100644 src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage`2.cs delete mode 100644 src/VisualStudio/Core/Def/Telemetry/SyntaxTreeConfigurationOptions.cs diff --git a/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs b/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs index 2a27d699a995b..bb742d54361d7 100644 --- a/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs +++ b/src/EditorFeatures/CSharp/GoToBase/CSharpGoToBaseService.cs @@ -2,21 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Composition; using Microsoft.CodeAnalysis.GoToBase; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.GoToBase { [ExportLanguageService(typeof(IGoToBaseService), LanguageNames.CSharp), Shared] - internal class CSharpGoToBaseService : AbstractGoToBaseService + internal sealed class CSharpGoToBaseService : AbstractGoToBaseService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpGoToBaseService() + : base() { } } diff --git a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs index 80294aad2ccbd..ea9f2776eaf37 100644 --- a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs +++ b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToDefinitionService.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition { diff --git a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs index cb7c9793e0b98..d8dacdc07cb5b 100644 --- a/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs +++ b/src/EditorFeatures/CSharp/GoToDefinition/CSharpGoToSymbolService.cs @@ -7,11 +7,12 @@ using Microsoft.CodeAnalysis.Editor.GoToDefinition; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition { [ExportLanguageService(typeof(IGoToSymbolService), LanguageNames.CSharp), Shared] - internal class CSharpGoToSymbolService : AbstractGoToSymbolService + internal sealed class CSharpGoToSymbolService : AbstractGoToSymbolService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs index 62457ed87a60a..739373c2994e8 100644 --- a/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/CopyPasteAndPrintingClassifierTests.cs @@ -32,12 +32,13 @@ public async Task TestGetTagsOnBufferTagger() using var workspace = TestWorkspace.CreateCSharp("class C { C c; }"); var document = workspace.Documents.First(); - var listenerProvider = workspace.ExportProvider.GetExportedValue(); + var listenerProvider = workspace.GetService(); var provider = new CopyPasteAndPrintingClassificationBufferTaggerProvider( - workspace.ExportProvider.GetExportedValue(), - workspace.ExportProvider.GetExportedValue(), - listenerProvider); + workspace.GetService(), + workspace.GetService(), + listenerProvider, + workspace.GlobalOptions); var tagger = provider.CreateTagger(document.GetTextBuffer())!; using var disposable = (IDisposable)tagger; diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs index 2ab0a601f225d..59ee0d46f5c76 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs @@ -193,8 +193,7 @@ private static async Task GetQuickinfo(TestWorkspace workspace, D { var diagnosticAnalyzerService = workspace.ExportProvider.GetExportedValue(); var provider = new CSharpDiagnosticAnalyzerQuickInfoProvider(diagnosticAnalyzerService); - var options = SymbolDescriptionOptions.From(document.Project); - var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, options, CancellationToken.None)); + var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, SymbolDescriptionOptions.Default, CancellationToken.None)); return info; } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs index d7e1c836f5b25..0bb441b549c46 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SyntacticQuickInfoSourceTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.QuickInfo; using Microsoft.CodeAnalysis.Editor.Host; @@ -568,8 +569,7 @@ protected override async Task AssertNoContentAsync( int position) { var provider = CreateProvider(); - var options = SymbolDescriptionOptions.From(document.Project); - Assert.Null(await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, options, CancellationToken.None))); + Assert.Null(await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, SymbolDescriptionOptions.Default, CancellationToken.None))); } protected override async Task AssertContentIsAsync( @@ -580,8 +580,7 @@ protected override async Task AssertContentIsAsync( string expectedDocumentationComment = null) { var provider = CreateProvider(); - var options = SymbolDescriptionOptions.From(document.Project); - var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, options, CancellationToken.None)); + var info = await provider.GetQuickInfoAsync(new QuickInfoContext(document, position, SymbolDescriptionOptions.Default, CancellationToken.None)); Assert.NotNull(info); Assert.NotEqual(0, info.RelatedSpans.Length); @@ -591,7 +590,7 @@ protected override async Task AssertContentIsAsync( var streamingPresenter = workspace.ExportProvider.GetExport(); var quickInfoItem = await IntellisenseQuickInfoBuilder.BuildItemAsync( trackingSpan.Object, info, document, - threadingContext, operationExecutor, + ClassificationOptions.Default, threadingContext, operationExecutor, AsynchronousOperationListenerProvider.NullListener, streamingPresenter, CancellationToken.None); var containerElement = quickInfoItem.Item as ContainerElement; diff --git a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindBaseSymbolsCommandHandler.cs b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindBaseSymbolsCommandHandler.cs index 5e633f238c4c6..43a1e8789ef3c 100644 --- a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindBaseSymbolsCommandHandler.cs +++ b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindBaseSymbolsCommandHandler.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; @@ -28,6 +29,7 @@ internal sealed class FindBaseSymbolsCommandHandler : AbstractNavigationCommandHandler { private readonly IAsynchronousOperationListener _asyncListener; + private readonly IGlobalOptionService _globalOptions; public override string DisplayName => nameof(FindBaseSymbolsCommandHandler); @@ -35,12 +37,14 @@ internal sealed class FindBaseSymbolsCommandHandler : [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public FindBaseSymbolsCommandHandler( [ImportMany] IEnumerable> streamingPresenters, - IAsynchronousOperationListenerProvider listenerProvider) + IAsynchronousOperationListenerProvider listenerProvider, + IGlobalOptionService globalOptions) : base(streamingPresenters) { Contract.ThrowIfNull(listenerProvider); _asyncListener = listenerProvider.GetListener(FeatureAttribute.FindReferences); + _globalOptions = globalOptions; } protected override bool TryExecuteCommand(int caretPosition, Document document, CommandExecutionContext context) @@ -83,7 +87,7 @@ private async Task StreamingFindBaseSymbolsAsync( return; } - var definitionItem = overriddenSymbol.ToNonClassifiedDefinitionItem(document.Project.Solution, true); + var definitionItem = overriddenSymbol.ToNonClassifiedDefinitionItem(document.Project.Solution, includeHiddenLocations: true); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); // try getting the next one diff --git a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindDerivedSymbolsCommandHandler.cs b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindDerivedSymbolsCommandHandler.cs index a44978f3fc2ab..7076795e0c28e 100644 --- a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindDerivedSymbolsCommandHandler.cs +++ b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindDerivedSymbolsCommandHandler.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Linq; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Threading; @@ -11,15 +10,16 @@ using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands.Navigation; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; using VSCommanding = Microsoft.VisualStudio.Commanding; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.FindUsages; namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigationCommandHandlers { @@ -105,7 +105,7 @@ private async Task FindDerivedSymbolsAsync( foreach (var candidate in candidates) { - var definitionItem = candidate.ToNonClassifiedDefinitionItem(document.Project.Solution, true); + var definitionItem = candidate.ToNonClassifiedDefinitionItem(document.Project.Solution, includeHiddenLocations: true); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindExtensionMethodsCommandHandler.cs b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindExtensionMethodsCommandHandler.cs index 1a8ec8d3d0750..c56709e12897c 100644 --- a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindExtensionMethodsCommandHandler.cs +++ b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindExtensionMethodsCommandHandler.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; @@ -32,6 +33,7 @@ internal sealed class FindExtensionMethodsCommandHandler : AbstractNavigationCommandHandler { private readonly IAsynchronousOperationListener _asyncListener; + private readonly IGlobalOptionService _globalOptions; public override string DisplayName => nameof(FindExtensionMethodsCommandHandler); @@ -39,12 +41,14 @@ internal sealed class FindExtensionMethodsCommandHandler : [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public FindExtensionMethodsCommandHandler( [ImportMany] IEnumerable> streamingPresenters, - IAsynchronousOperationListenerProvider listenerProvider) + IAsynchronousOperationListenerProvider listenerProvider, + IGlobalOptionService globalOptions) : base(streamingPresenters) { Contract.ThrowIfNull(listenerProvider); _asyncListener = listenerProvider.GetListener(FeatureAttribute.FindReferences); + _globalOptions = globalOptions; } protected override bool TryExecuteCommand(int caretPosition, Document document, CommandExecutionContext context) @@ -109,7 +113,7 @@ private async Task FindExtensionMethodsAsync( { var originatingProject = solution.GetProject(sourceDefinition.ContainingAssembly, cancellationToken); - var definitionItem = reducedMethod.ToNonClassifiedDefinitionItem(solution, true); + var definitionItem = reducedMethod.ToNonClassifiedDefinitionItem(solution, includeHiddenLocations: true); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); } diff --git a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindImplementingMembersCommandHandler.cs b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindImplementingMembersCommandHandler.cs index daa41f3df2dc6..52d5ed0de7671 100644 --- a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindImplementingMembersCommandHandler.cs +++ b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindImplementingMembersCommandHandler.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands.Navigation; @@ -30,6 +31,7 @@ internal sealed class FindImplementingMembersCommandHandler : AbstractNavigationCommandHandler { private readonly IAsynchronousOperationListener _asyncListener; + private readonly IGlobalOptionService _globalOptions; public override string DisplayName => nameof(FindImplementingMembersCommandHandler); @@ -37,12 +39,14 @@ internal sealed class FindImplementingMembersCommandHandler : [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public FindImplementingMembersCommandHandler( [ImportMany] IEnumerable> streamingPresenters, - IAsynchronousOperationListenerProvider listenerProvider) + IAsynchronousOperationListenerProvider listenerProvider, + IGlobalOptionService globalOptions) : base(streamingPresenters) { Contract.ThrowIfNull(listenerProvider); _asyncListener = listenerProvider.GetListener(FeatureAttribute.FindReferences); + _globalOptions = globalOptions; } protected override bool TryExecuteCommand(int caretPosition, Document document, CommandExecutionContext context) @@ -142,7 +146,7 @@ private static async Task InspectInterfaceAsync( if (impl == null) continue; - var definitionItem = impl.ToNonClassifiedDefinitionItem(project.Solution, true); + var definitionItem = impl.ToNonClassifiedDefinitionItem(project.Solution, includeHiddenLocations: true); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindMemberOverloadsCommandHandler.cs b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindMemberOverloadsCommandHandler.cs index 79a8ce4b9885b..a8a782ea2a5bd 100644 --- a/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindMemberOverloadsCommandHandler.cs +++ b/src/EditorFeatures/Core.Cocoa/NavigationCommandHandlers/FindMemberOverloadsCommandHandler.cs @@ -3,12 +3,14 @@ // See the LICENSE file in the project root for more information. using System; -using System.Linq; using System.Collections.Generic; using System.ComponentModel.Composition; +using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Commanding; @@ -16,9 +18,6 @@ using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; using VSCommanding = Microsoft.VisualStudio.Commanding; -using Microsoft.CodeAnalysis.Host.Mef; -using System.Threading; -using Microsoft.CodeAnalysis.FindUsages; namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigationCommandHandlers { diff --git a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs index 08d1d55eed9bb..a1cac5cc9174f 100644 --- a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs +++ b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTag.cs @@ -13,16 +13,14 @@ using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; -using Microsoft.CodeAnalysis.Editor.Host; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Elfie.Diagnostics; using Microsoft.CodeAnalysis.InlineHints; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Classification; @@ -35,7 +33,7 @@ namespace Microsoft.CodeAnalysis.Editor.InlineHints /// This is the tag which implements the IntraTextAdornmentTag and is meant to create the UIElements that get shown /// in the editor /// - internal class InlineHintsTag : IntraTextAdornmentTag + internal sealed class InlineHintsTag : IntraTextAdornmentTag { public const string TagId = "inline hints"; @@ -101,6 +99,7 @@ public async Task> CreateDescriptionAsync(Cancellati { var context = new IntellisenseQuickInfoBuilderContext( document, + _taggerProvider.GlobalOptions.GetClassificationOptions(document.Project.Language), _taggerProvider.ThreadingContext, _taggerProvider.OperationExecutor, _taggerProvider.AsynchronousOperationListener, diff --git a/src/EditorFeatures/Core/FindUsages/FindUsagesOptionsStorage.cs b/src/EditorFeatures/Core/FindUsages/FindUsagesOptionsStorage.cs index ee400a4316629..da4c02aa5f3df 100644 --- a/src/EditorFeatures/Core/FindUsages/FindUsagesOptionsStorage.cs +++ b/src/EditorFeatures/Core/FindUsages/FindUsagesOptionsStorage.cs @@ -3,15 +3,13 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Options; -using System; +using Microsoft.CodeAnalysis.Classification; namespace Microsoft.CodeAnalysis.FindUsages { internal static class FindUsagesOptionsStorage { -#pragma warning disable IDE0060 // Remove unused parameter -- TODO public static FindUsagesOptions GetFindUsagesOptions(this IGlobalOptionService globalOptions, string language) - => throw new NotImplementedException(); -#pragma warning restore IDE0060 // Remove unused parameter + => new(ClassificationOptions: globalOptions.GetClassificationOptions(language)); } } diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs index bbd428b45b0aa..fe6e15bc65e30 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToDefinitionService.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.GoToDefinition; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToSymbolService.cs b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToSymbolService.cs index cacd8843db3c7..37a8ade70b4b9 100644 --- a/src/EditorFeatures/Core/GoToDefinition/AbstractGoToSymbolService.cs +++ b/src/EditorFeatures/Core/GoToDefinition/AbstractGoToSymbolService.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.GoToDefinition; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Editor.GoToDefinition { diff --git a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs index f560bae5c506d..17439faba4a3f 100644 --- a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs +++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.GoToDefinition; using Microsoft.CodeAnalysis.Navigation; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs index 00d2db2548ed0..0bb1a0b91404f 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.Tagger.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Tagging; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; @@ -23,11 +24,12 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Classification { internal partial class CopyPasteAndPrintingClassificationBufferTaggerProvider { - private class Tagger : ForegroundThreadAffinitizedObject, IAccurateTagger, IDisposable + private sealed class Tagger : ForegroundThreadAffinitizedObject, IAccurateTagger, IDisposable { private readonly CopyPasteAndPrintingClassificationBufferTaggerProvider _owner; private readonly ITextBuffer _subjectBuffer; private readonly ITaggerEventSource _eventSource; + private readonly IGlobalOptionService _globalOptions; // State for the tagger. Can be accessed from any thread. Access should be protected by _gate. @@ -38,11 +40,13 @@ private class Tagger : ForegroundThreadAffinitizedObject, IAccurateTagger> GetAllTags(NormalizedSnapshotSp if (document == null) return Array.Empty>(); + var classificationService = document.GetLanguageService(); + if (classificationService == null) + return Array.Empty>(); + // We want to classify from the start of the first requested span to the end of the // last requested span. var spanToTag = new SnapshotSpan(snapshot, Span.FromBounds(spans.First().Start, spans.Last().End)); @@ -125,8 +133,10 @@ public IEnumerable> GetAllTags(NormalizedSnapshotSp { // Our cache is not there, or is out of date. We need to compute the up to date results. var context = new TaggerContext(document, snapshot); - this.ThreadingContext.JoinableTaskFactory.Run( - () => ProduceTagsAsync(context, new DocumentSnapshotSpan(document, spanToTag), _owner._typeMap, cancellationToken)); + var options = _globalOptions.GetClassificationOptions(document.Project.Language); + + ThreadingContext.JoinableTaskFactory.Run( + () => SemanticClassificationUtilities.ProduceTagsAsync(context, new DocumentSnapshotSpan(document, spanToTag), classificationService, _owner._typeMap, options, cancellationToken)); cachedTaggedSpan = spanToTag; cachedTags = new TagSpanIntervalTree(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive, context.tagSpans); @@ -151,15 +161,6 @@ private void GetCachedInfo(out SnapshotSpan? cachedTaggedSpan, out TagSpanInterv cachedTags = _cachedTags; } } - - private static Task ProduceTagsAsync( - TaggerContext context, DocumentSnapshotSpan documentSpan, ClassificationTypeMap typeMap, CancellationToken cancellationToken) - { - var classificationService = documentSpan.Document.GetLanguageService(); - return classificationService != null - ? SemanticClassificationUtilities.ProduceTagsAsync(context, documentSpan, classificationService, typeMap, cancellationToken) - : Task.CompletedTask; - } } } } diff --git a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs index a6118f3c23c73..6b209997194fb 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/CopyPasteAndPrintingClassificationBufferTaggerProvider.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; @@ -30,17 +31,20 @@ internal partial class CopyPasteAndPrintingClassificationBufferTaggerProvider : { private readonly IAsynchronousOperationListener _asyncListener; private readonly ClassificationTypeMap _typeMap; + private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] public CopyPasteAndPrintingClassificationBufferTaggerProvider( IThreadingContext threadingContext, ClassificationTypeMap typeMap, - IAsynchronousOperationListenerProvider listenerProvider) + IAsynchronousOperationListenerProvider listenerProvider, + IGlobalOptionService globalOptions) : base(threadingContext) { _typeMap = typeMap; _asyncListener = listenerProvider.GetListener(FeatureAttribute.Classification); + _globalOptions = globalOptions; } public IAccurateTagger? CreateTagger(ITextBuffer buffer) where T : ITag @@ -54,7 +58,7 @@ public CopyPasteAndPrintingClassificationBufferTaggerProvider( return null; } - return new Tagger(this, buffer, _asyncListener) as IAccurateTagger; + return new Tagger(this, buffer, _asyncListener, _globalOptions) as IAccurateTagger; } ITagger? ITaggerProvider.CreateTagger(ITextBuffer buffer) diff --git a/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationUtilities.cs b/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationUtilities.cs index 5033212ed5266..3302968e9374f 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationUtilities.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationUtilities.cs @@ -33,14 +33,13 @@ public static async Task ProduceTagsAsync( DocumentSnapshotSpan spanToTag, IClassificationService classificationService, ClassificationTypeMap typeMap, + ClassificationOptions options, CancellationToken cancellationToken) { var document = spanToTag.Document; if (document == null) return; - var options = ClassificationOptions.From(document.Project); - // Don't block getting classifications on building the full compilation. This may take a significant amount // of time and can cause a very latency sensitive operation (copying) to block the user while we wait on this // work to happen. diff --git a/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationViewTaggerProvider.cs index 85b5fb1f4e261..7756afd80d4f4 100644 --- a/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Classification/SemanticClassificationViewTaggerProvider.cs @@ -76,7 +76,7 @@ protected override ITaggerEventSource CreateEventSource(ITextView textView, ITex TaggerEventSources.OnViewSpanChanged(ThreadingContext, textView), TaggerEventSources.OnWorkspaceChanged(subjectBuffer, this.AsyncListener), TaggerEventSources.OnDocumentActiveContextChanged(subjectBuffer), - TaggerEventSources.OnOptionChanged(subjectBuffer, ClassificationOptions.Metadata.ClassifyReassignedVariables)); + TaggerEventSources.OnOptionChanged(subjectBuffer, ClassificationOptionsStorage.ClassifyReassignedVariables)); } protected override IEnumerable GetSpansToTag(ITextView textView, ITextBuffer subjectBuffer) @@ -126,8 +126,9 @@ protected override Task ProduceTagsAsync( return Task.CompletedTask; } + var classificationOptions = _globalOptions.GetClassificationOptions(document.Project.Language); return SemanticClassificationUtilities.ProduceTagsAsync( - context, spanToTag, classificationService, _typeMap, cancellationToken); + context, spanToTag, classificationService, _typeMap, classificationOptions, cancellationToken); } } } diff --git a/src/EditorFeatures/Core/Implementation/Formatting/FormattingOptionsMetadata.cs b/src/EditorFeatures/Core/Implementation/Formatting/FormattingOptionsMetadata.cs index 9c607691ff476..7f06fe036fb76 100644 --- a/src/EditorFeatures/Core/Implementation/Formatting/FormattingOptionsMetadata.cs +++ b/src/EditorFeatures/Core/Implementation/Formatting/FormattingOptionsMetadata.cs @@ -2,27 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Immutable; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; namespace Microsoft.CodeAnalysis.Editor.Implementation.Formatting { - [ExportSolutionOptionProvider, Shared] - internal sealed class FormattingOptionsMetadata : IOptionProvider + internal sealed class FormattingOptionsMetadata { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FormattingOptionsMetadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - FormatOnPaste); - private const string FeatureName = "FormattingOptions"; public static readonly PerLanguageOption2 FormatOnPaste = diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 7aaeee93c280d..9244dd9679f20 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Completion.Providers; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; @@ -506,14 +507,16 @@ private static void AddPropertiesToSession(IAsyncCompletionSession session, Comp if (service == null) return null; - var options = _globalOptions.GetCompletionOptions(document.Project.Language); - var displayOptions = SymbolDescriptionOptions.From(document.Project); - var description = await service.GetDescriptionAsync(document, roslynItem, options, displayOptions, cancellationToken).ConfigureAwait(false); + var completionOptions = _globalOptions.GetCompletionOptions(document.Project.Language); + var displayOptions = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); + var description = await service.GetDescriptionAsync(document, roslynItem, completionOptions, displayOptions, cancellationToken).ConfigureAwait(false); if (description == null) return null; + var classificationOptions = _globalOptions.GetClassificationOptions(document.Project.Language); + var context = new IntellisenseQuickInfoBuilderContext( - document, ThreadingContext, _operationExecutor, _asyncListener, _streamingPresenter); + document, classificationOptions, ThreadingContext, _operationExecutor, _asyncListener, _streamingPresenter); var elements = IntelliSense.Helpers.BuildInteractiveTextElements(description.TaggedParts, context).ToArray(); if (elements.Length == 0) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs index 28055ba350ec6..6a4ae8f5c86ed 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs @@ -13,9 +13,9 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Storage; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Utilities; @@ -26,7 +26,8 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo { internal static class IntellisenseQuickInfoBuilder { - private static async Task BuildInteractiveContentAsync(CodeAnalysisQuickInfoItem quickInfoItem, + private static async Task BuildInteractiveContentAsync( + CodeAnalysisQuickInfoItem quickInfoItem, IntellisenseQuickInfoBuilderContext? context, CancellationToken cancellationToken) { @@ -99,15 +100,14 @@ private static async Task BuildInteractiveContentAsync(CodeAna .SelectMany(s => Helpers.BuildInteractiveTextElements(s.TaggedParts, context))); // build text for RelatedSpan - if (quickInfoItem.RelatedSpans.Any() && context?.Document is Document document) + if (quickInfoItem.RelatedSpans.Any() && context != null) { - var classificationOptions = ClassificationOptions.From(document.Project); - + var document = context.Document; var textRuns = new List(); var spanSeparatorNeededBefore = false; foreach (var span in quickInfoItem.RelatedSpans) { - var classifiedSpans = await ClassifierHelper.GetClassifiedSpansAsync(document, span, classificationOptions, cancellationToken).ConfigureAwait(false); + var classifiedSpans = await ClassifierHelper.GetClassifiedSpansAsync(document, span, context.ClassificationOptions, cancellationToken).ConfigureAwait(false); var tabSize = document.Project.Solution.Options.GetOption(FormattingOptions.TabSize, document.Project.Language); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -140,13 +140,14 @@ internal static async Task BuildItemAsync( ITrackingSpan trackingSpan, CodeAnalysisQuickInfoItem quickInfoItem, Document document, + ClassificationOptions classificationOptions, IThreadingContext threadingContext, IUIThreadOperationExecutor operationExecutor, IAsynchronousOperationListener asyncListener, Lazy streamingPresenter, CancellationToken cancellationToken) { - var context = new IntellisenseQuickInfoBuilderContext(document, threadingContext, operationExecutor, asyncListener, streamingPresenter); + var context = new IntellisenseQuickInfoBuilderContext(document, classificationOptions, threadingContext, operationExecutor, asyncListener, streamingPresenter); var content = await BuildInteractiveContentAsync(quickInfoItem, context, cancellationToken).ConfigureAwait(false); return new IntellisenseQuickInfoItem(trackingSpan, content); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilderContext.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilderContext.cs index 1ea0c1ebecc2d..f83ba2239c952 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilderContext.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilderContext.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Classification; using Microsoft.VisualStudio.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo @@ -17,12 +18,14 @@ internal sealed class IntellisenseQuickInfoBuilderContext { public IntellisenseQuickInfoBuilderContext( Document document, + ClassificationOptions classificationOptions, IThreadingContext? threadingContext, IUIThreadOperationExecutor? operationExecutor, IAsynchronousOperationListener? asynchronousOperationListener, Lazy? streamingPresenter) { Document = document; + ClassificationOptions = classificationOptions; ThreadingContext = threadingContext; OperationExecutor = operationExecutor; StreamingPresenter = streamingPresenter; @@ -30,6 +33,7 @@ public IntellisenseQuickInfoBuilderContext( } public Document Document { get; } + public ClassificationOptions ClassificationOptions { get; } public IThreadingContext? ThreadingContext { get; } public IUIThreadOperationExecutor? OperationExecutor { get; } public IAsynchronousOperationListener? AsynchronousOperationListener { get; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs index 6988656fe0bbc..acad4c42dbb00 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs @@ -7,6 +7,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; @@ -14,6 +15,7 @@ using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Language.Intellisense; @@ -27,26 +29,29 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo { internal partial class QuickInfoSourceProvider { - private class QuickInfoSource : IAsyncQuickInfoSource + private sealed class QuickInfoSource : IAsyncQuickInfoSource { private readonly ITextBuffer _subjectBuffer; private readonly IThreadingContext _threadingContext; private readonly IUIThreadOperationExecutor _operationExecutor; private readonly IAsynchronousOperationListener _asyncListener; private readonly Lazy _streamingPresenter; + private readonly IGlobalOptionService _globalOptions; public QuickInfoSource( ITextBuffer subjectBuffer, IThreadingContext threadingContext, IUIThreadOperationExecutor operationExecutor, IAsynchronousOperationListener asyncListener, - Lazy streamingPresenter) + Lazy streamingPresenter, + IGlobalOptionService globalOptions) { _subjectBuffer = subjectBuffer; _threadingContext = threadingContext; _operationExecutor = operationExecutor; _asyncListener = asyncListener; _streamingPresenter = streamingPresenter; + _globalOptions = globalOptions; } public async Task GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken) @@ -70,14 +75,16 @@ public async Task GetQuickInfoItemAsync(IAsyncQuickIn { cancellationToken.ThrowIfCancellationRequested(); - var options = SymbolDescriptionOptions.From(document.Project); + var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); var item = await service.GetQuickInfoAsync(document, triggerPoint.Value, options, cancellationToken).ConfigureAwait(false); if (item != null) { var textVersion = snapshot.Version; var trackingSpan = textVersion.CreateTrackingSpan(item.Span.ToSpan(), SpanTrackingMode.EdgeInclusive); + var classificationOptions = _globalOptions.GetClassificationOptions(document.Project.Language); + return await IntellisenseQuickInfoBuilder.BuildItemAsync( - trackingSpan, item, document, + trackingSpan, item, document, classificationOptions, _threadingContext, _operationExecutor, _asyncListener, _streamingPresenter, cancellationToken).ConfigureAwait(false); } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.cs index f77a9a333b724..cd9681af7641f 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/QuickInfoSourceProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; @@ -26,6 +27,7 @@ internal partial class QuickInfoSourceProvider : IAsyncQuickInfoSourceProvider private readonly IUIThreadOperationExecutor _operationExecutor; private readonly Lazy _streamingPresenter; private readonly IAsynchronousOperationListener _listener; + private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -33,12 +35,14 @@ public QuickInfoSourceProvider( IThreadingContext threadingContext, IUIThreadOperationExecutor operationExecutor, IAsynchronousOperationListenerProvider listenerProvider, - Lazy streamingPresenter) + Lazy streamingPresenter, + IGlobalOptionService globalOptions) { _threadingContext = threadingContext; _operationExecutor = operationExecutor; _streamingPresenter = streamingPresenter; _listener = listenerProvider.GetListener(FeatureAttribute.QuickInfo); + _globalOptions = globalOptions; } public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) @@ -47,7 +51,7 @@ public IAsyncQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) return null; return new QuickInfoSource( - textBuffer, _threadingContext, _operationExecutor, _listener, _streamingPresenter); + textBuffer, _threadingContext, _operationExecutor, _listener, _streamingPresenter, _globalOptions); } } } diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs index c37774864dc6f..5918dc976d3a4 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs @@ -67,9 +67,9 @@ public CompletionResolveHandler(IGlobalOptionService globalOptions, CompletionLi return completionItem; } - var options = _globalOptions.GetCompletionOptions(document.Project.Language); - var displayOptions = SymbolDescriptionOptions.From(document.Project); - var description = await completionService.GetDescriptionAsync(document, selectedItem, options, displayOptions, cancellationToken).ConfigureAwait(false)!; + var completionOptions = _globalOptions.GetCompletionOptions(document.Project.Language); + var displayOptions = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); + var description = await completionService.GetDescriptionAsync(document, selectedItem, completionOptions, displayOptions, cancellationToken).ConfigureAwait(false)!; if (description != null) { var supportsVSExtensions = context.ClientCapabilities.HasVisualStudioLspCapability(); diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs index 8af9a9e84fd97..388a2f00d07a3 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs @@ -9,11 +9,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -28,12 +29,15 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler ///
[ExportRoslynLanguagesLspRequestHandlerProvider, Shared] [ProvidesMethod(Methods.TextDocumentHoverName)] - internal class HoverHandler : AbstractStatelessRequestHandler + internal sealed class HoverHandler : AbstractStatelessRequestHandler { + private readonly IGlobalOptionService _globalOptions; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public HoverHandler() + public HoverHandler(IGlobalOptionService globalOptions) { + _globalOptions = globalOptions; } public override string Method => Methods.TextDocumentHoverName; @@ -50,15 +54,16 @@ public HoverHandler() var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var quickInfoService = document.Project.LanguageServices.GetRequiredService(); - var options = SymbolDescriptionOptions.From(document.Project); + var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); var info = await quickInfoService.GetQuickInfoAsync(document, position, options, cancellationToken).ConfigureAwait(false); if (info == null) { return null; } + var classificationOptions = _globalOptions.GetClassificationOptions(document.Project.Language); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - return await GetHoverAsync(info, text, document.Project.Language, document, context.ClientCapabilities, cancellationToken).ConfigureAwait(false); + return await GetHoverAsync(info, text, document.Project.Language, document, classificationOptions, context.ClientCapabilities, cancellationToken).ConfigureAwait(false); } internal static async Task GetHoverAsync( @@ -80,7 +85,7 @@ public HoverHandler() } var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); - return await GetHoverAsync(info, text, semanticModel.Language, document: null, clientCapabilities: null, cancellationToken).ConfigureAwait(false); + return await GetHoverAsync(info, text, semanticModel.Language, document: null, classificationOptions: null, clientCapabilities: null, cancellationToken).ConfigureAwait(false); } private static async Task GetHoverAsync( @@ -88,9 +93,12 @@ private static async Task GetHoverAsync( SourceText text, string language, Document? document, + ClassificationOptions? classificationOptions, ClientCapabilities? clientCapabilities, CancellationToken cancellationToken) { + Contract.ThrowIfFalse(document is null == (classificationOptions == null)); + var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability(); if (supportsVSExtensions) @@ -99,6 +107,7 @@ private static async Task GetHoverAsync( ? null : new IntellisenseQuickInfoBuilderContext( document, + classificationOptions!.Value, threadingContext: null, operationExecutor: null, asynchronousOperationListener: null, diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindUsagesLSPContext.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindUsagesLSPContext.cs index 3da4dac625de0..ba63a8e002012 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindUsagesLSPContext.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindUsagesLSPContext.cs @@ -111,8 +111,8 @@ public override async ValueTask OnDefinitionFoundAsync(DefinitionItem definition // Creating a new VSReferenceItem for the definition var definitionItem = await GenerateVSReferenceItemAsync( - _id, definitionId: _id, _document, _position, definition.SourceSpans.FirstOrNull(), - definition.DisplayableProperties, _metadataAsSourceFileService, definition.GetClassifiedText(), + definitionId: _id, definition.SourceSpans.FirstOrNull(), + definition.DisplayableProperties, definition.GetClassifiedText(), definition.Tags.GetFirstGlyph(), symbolUsageInfo: null, isWrittenTo: false, cancellationToken).ConfigureAwait(false); if (definitionItem != null) @@ -159,8 +159,8 @@ public override async ValueTask OnReferenceFoundAsync(SourceReferenceItem refere // Creating a new VSReferenceItem for the reference var referenceItem = await GenerateVSReferenceItemAsync( - _id, definitionId, _document, _position, reference.SourceSpan, - reference.AdditionalProperties, _metadataAsSourceFileService, definitionText: null, + definitionId, reference.SourceSpan, + reference.AdditionalProperties, definitionText: null, definitionGlyph: Glyph.None, reference.SymbolUsageInfo, reference.IsWrittenTo, cancellationToken).ConfigureAwait(false); if (referenceItem != null) @@ -170,25 +170,21 @@ public override async ValueTask OnReferenceFoundAsync(SourceReferenceItem refere } } - private static async Task GenerateVSReferenceItemAsync( - int id, + private async Task GenerateVSReferenceItemAsync( int? definitionId, - Document document, - int position, DocumentSpan? documentSpan, ImmutableDictionary properties, - IMetadataAsSourceFileService metadataAsSourceFileService, ClassifiedTextElement? definitionText, Glyph definitionGlyph, SymbolUsageInfo? symbolUsageInfo, bool isWrittenTo, CancellationToken cancellationToken) { - var location = await ComputeLocationAsync(document, position, documentSpan, metadataAsSourceFileService, cancellationToken).ConfigureAwait(false); + var location = await ComputeLocationAsync(documentSpan, cancellationToken).ConfigureAwait(false); // Getting the text for the Text property. If we somehow can't compute the text, that means we're probably dealing with a metadata // reference, and those don't show up in the results list in Roslyn FAR anyway. - var text = await ComputeTextAsync(id, definitionId, documentSpan, definitionText, isWrittenTo, cancellationToken).ConfigureAwait(false); + var text = await ComputeTextAsync(definitionId, documentSpan, definitionText, isWrittenTo, cancellationToken).ConfigureAwait(false); if (text == null) { return null; @@ -202,7 +198,7 @@ public override async ValueTask OnReferenceFoundAsync(SourceReferenceItem refere DefinitionText = definitionText, // Only definitions should have a non-null DefinitionText DefinitionIcon = definitionGlyph.GetImageElement(), DisplayPath = location?.Uri.LocalPath, - Id = id, + Id = _id, Kind = symbolUsageInfo.HasValue ? ProtocolConversions.SymbolUsageInfoToReferenceKinds(symbolUsageInfo.Value) : Array.Empty(), ResolutionStatus = VSInternalResolutionStatusKind.ConfirmedAsReference, Text = text, @@ -227,130 +223,125 @@ public override async ValueTask OnReferenceFoundAsync(SourceReferenceItem refere result.ContainingType = referenceContainingType; return result; + } - // Local functions - static async Task ComputeLocationAsync( - Document document, - int position, - DocumentSpan? documentSpan, - IMetadataAsSourceFileService metadataAsSourceFileService, - CancellationToken cancellationToken) + private async Task ComputeLocationAsync(DocumentSpan? documentSpan, CancellationToken cancellationToken) + { + // If we have no document span, our location may be in metadata. + if (documentSpan != null) { - // If we have no document span, our location may be in metadata. - if (documentSpan != null) - { - // We do have a document span, so compute location normally. - return await ProtocolConversions.DocumentSpanToLocationAsync(documentSpan.Value, cancellationToken).ConfigureAwait(false); - } + // We do have a document span, so compute location normally. + return await ProtocolConversions.DocumentSpanToLocationAsync(documentSpan.Value, cancellationToken).ConfigureAwait(false); + } - // If we have no document span, our location may be in metadata or may be a namespace. - var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); - if (symbol == null || symbol.Locations.IsEmpty || symbol.Kind == SymbolKind.Namespace) - { - // Either: - // (1) We couldn't find the location in metadata and it's not in any of our known documents. - // (2) The symbol is a namespace (and therefore has no location). - return null; - } + // If we have no document span, our location may be in metadata or may be a namespace. + var symbol = await SymbolFinder.FindSymbolAtPositionAsync(_document, _position, cancellationToken).ConfigureAwait(false); + if (symbol == null || symbol.Locations.IsEmpty || symbol.Kind == SymbolKind.Namespace) + { + // Either: + // (1) We couldn't find the location in metadata and it's not in any of our known documents. + // (2) The symbol is a namespace (and therefore has no location). + return null; + } - var declarationFile = await metadataAsSourceFileService.GetGeneratedFileAsync( - document.Project, symbol, signaturesOnly: true, allowDecompilation: false, cancellationToken).ConfigureAwait(false); + var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync( + _document.Project, symbol, signaturesOnly: true, allowDecompilation: false, cancellationToken).ConfigureAwait(false); - var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; + var linePosSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; - if (string.IsNullOrEmpty(declarationFile.FilePath)) - { - return null; - } + if (string.IsNullOrEmpty(declarationFile.FilePath)) + { + return null; + } - try - { - return new LSP.Location - { - Uri = ProtocolConversions.GetUriFromFilePath(declarationFile.FilePath), - Range = ProtocolConversions.LinePositionToRange(linePosSpan), - }; - } - catch (UriFormatException e) when (FatalError.ReportAndCatch(e)) + try + { + return new LSP.Location { - // We might reach this point if the file path is formatted incorrectly. - return null; - } + Uri = ProtocolConversions.GetUriFromFilePath(declarationFile.FilePath), + Range = ProtocolConversions.LinePositionToRange(linePosSpan), + }; } + catch (UriFormatException e) when (FatalError.ReportAndCatch(e)) + { + // We might reach this point if the file path is formatted incorrectly. + return null; + } + } - static async Task ComputeTextAsync( - int id, int? definitionId, - DocumentSpan? documentSpan, - ClassifiedTextElement? definitionText, - bool isWrittenTo, - CancellationToken cancellationToken) + private async Task ComputeTextAsync( + int? definitionId, + DocumentSpan? documentSpan, + ClassifiedTextElement? definitionText, + bool isWrittenTo, + CancellationToken cancellationToken) + { + // General case + if (documentSpan != null) { - // General case - if (documentSpan != null) - { - var document = documentSpan.Value.Document; - var classificationOptions = ClassificationOptions.From(document.Project); + var document = documentSpan.Value.Document; + var options = await GetOptionsAsync(document.Project.Language, cancellationToken).ConfigureAwait(false); - var classifiedSpansAndHighlightSpan = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync( - documentSpan.Value, classificationOptions, cancellationToken).ConfigureAwait(false); + var classifiedSpansAndHighlightSpan = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync( + documentSpan.Value, options.ClassificationOptions, cancellationToken).ConfigureAwait(false); - var classifiedSpans = classifiedSpansAndHighlightSpan.ClassifiedSpans; - var docText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var classifiedTextRuns = GetClassifiedTextRuns(id, definitionId, documentSpan.Value, isWrittenTo, classifiedSpans, docText); + var classifiedSpans = classifiedSpansAndHighlightSpan.ClassifiedSpans; + var docText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var classifiedTextRuns = GetClassifiedTextRuns(_id, definitionId, documentSpan.Value, isWrittenTo, classifiedSpans, docText); - return new ClassifiedTextElement(classifiedTextRuns.ToArray()); - } - // Certain definitions may not have a DocumentSpan, such as namespace and metadata definitions - else if (id == definitionId) - { - return definitionText; - } + return new ClassifiedTextElement(classifiedTextRuns.ToArray()); + } - return null; + // Certain definitions may not have a DocumentSpan, such as namespace and metadata definitions + if (_id == definitionId) + { + return definitionText; + } + + return null; + } + + private static ClassifiedTextRun[] GetClassifiedTextRuns( + int id, + int? definitionId, + DocumentSpan documentSpan, + bool isWrittenTo, + ImmutableArray classifiedSpans, + SourceText docText) + { + using var _ = ArrayBuilder.GetInstance(out var classifiedTextRuns); + foreach (var span in classifiedSpans) + { + // Default case: Don't highlight. For example, if the user invokes FAR on 'x' in 'var x = 1', then 'var', + // '=', and '1' should not be highlighted. + string? markerTagType = null; - // Nested local functions - static ClassifiedTextRun[] GetClassifiedTextRuns( - int id, int? definitionId, - DocumentSpan documentSpan, - bool isWrittenTo, - ImmutableArray classifiedSpans, - SourceText docText) + // Case 1: Highlight this span of text. For example, if the user invokes FAR on 'x' in 'var x = 1', + // then 'x' should be highlighted. + if (span.TextSpan == documentSpan.SourceSpan) { - using var _ = ArrayBuilder.GetInstance(out var classifiedTextRuns); - foreach (var span in classifiedSpans) + // Case 1a: Highlight a definition + if (id == definitionId) { - // Default case: Don't highlight. For example, if the user invokes FAR on 'x' in 'var x = 1', then 'var', - // '=', and '1' should not be highlighted. - string? markerTagType = null; - - // Case 1: Highlight this span of text. For example, if the user invokes FAR on 'x' in 'var x = 1', - // then 'x' should be highlighted. - if (span.TextSpan == documentSpan.SourceSpan) - { - // Case 1a: Highlight a definition - if (id == definitionId) - { - markerTagType = DefinitionHighlightTag.TagId; - } - // Case 1b: Highlight a written reference - else if (isWrittenTo) - { - markerTagType = WrittenReferenceHighlightTag.TagId; - } - // Case 1c: Highlight a read reference - else - { - markerTagType = ReferenceHighlightTag.TagId; - } - } - - classifiedTextRuns.Add(new ClassifiedTextRun( - span.ClassificationType, docText.ToString(span.TextSpan), ClassifiedTextRunStyle.Plain, markerTagType)); + markerTagType = DefinitionHighlightTag.TagId; + } + // Case 1b: Highlight a written reference + else if (isWrittenTo) + { + markerTagType = WrittenReferenceHighlightTag.TagId; + } + // Case 1c: Highlight a read reference + else + { + markerTagType = ReferenceHighlightTag.TagId; } - - return classifiedTextRuns.ToArray(); } + + classifiedTextRuns.Add(new ClassifiedTextRun( + span.ClassificationType, docText.ToString(span.TextSpan), ClassifiedTextRunStyle.Plain, markerTagType)); } + + return classifiedTextRuns.ToArray(); } private ValueTask ReportReferencesAsync(ImmutableArray referencesToReport, CancellationToken cancellationToken) diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs index e812f239b9a99..dc77a8beef900 100644 --- a/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs +++ b/src/EditorFeatures/Core/InlineHints/InlineHintsDataTaggerProvider.cs @@ -66,18 +66,18 @@ protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, I TaggerEventSources.OnViewSpanChanged(ThreadingContext, textViewOpt), TaggerEventSources.OnWorkspaceChanged(subjectBuffer, _listener), TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsGlobalStateOption.DisplayAllOverride), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.EnabledForParameters), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.ForLiteralParameters), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.ForIndexerParameters), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.ForObjectCreationParameters), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.ForOtherParameters), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.SuppressForParametersThatMatchMethodIntent), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.SuppressForParametersThatDifferOnlyBySuffix), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineParameterHintsOptions.Metadata.SuppressForParametersThatMatchArgumentName), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineTypeHintsOptions.Metadata.EnabledForTypes), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineTypeHintsOptions.Metadata.ForImplicitVariableTypes), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineTypeHintsOptions.Metadata.ForLambdaParameterTypes), - TaggerEventSources.OnOptionChanged(subjectBuffer, InlineTypeHintsOptions.Metadata.ForImplicitObjectCreation)); + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.EnabledForParameters), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForLiteralParameters), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForIndexerParameters), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForObjectCreationParameters), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForOtherParameters), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.SuppressForParametersThatMatchMethodIntent), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.SuppressForParametersThatDifferOnlyBySuffix), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.SuppressForParametersThatMatchArgumentName), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.EnabledForTypes), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForImplicitVariableTypes), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForLambdaParameterTypes), + TaggerEventSources.OnOptionChanged(subjectBuffer, InlineHintsOptionsStorage.ForImplicitObjectCreation)); } protected override IEnumerable GetSpansToTag(ITextView textView, ITextBuffer subjectBuffer) @@ -107,7 +107,7 @@ protected override async Task ProduceTagsAsync( if (service == null) return; - var options = InlineHintsOptions.From(document.Project); + var options = GlobalOptions.GetInlineHintsOptions(document.Project.Language); var snapshotSpan = documentSnapshotSpan.SnapshotSpan; var hints = await service.GetInlineHintsAsync(document, snapshotSpan.Span.ToTextSpan(), options, cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/InlineHints/InlineHintsOptionsStorage.cs b/src/EditorFeatures/Core/InlineHints/InlineHintsOptionsStorage.cs new file mode 100644 index 0000000000000..0563859fdef8c --- /dev/null +++ b/src/EditorFeatures/Core/InlineHints/InlineHintsOptionsStorage.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.InlineHints +{ + internal static class InlineHintsOptionsStorage + { + public static InlineHintsOptions GetInlineHintsOptions(this IGlobalOptionService globalOptions, string language) + => new( + ParameterOptions: globalOptions.GetInlineParameterHintsOptions(language), + TypeOptions: globalOptions.GetInlineTypeHintsOptions(language), + DisplayOptions: globalOptions.GetSymbolDescriptionOptions(language)); + + public static InlineParameterHintsOptions GetInlineParameterHintsOptions(this IGlobalOptionService globalOptions, string language) + => new( + EnabledForParameters: globalOptions.GetOption(EnabledForParameters, language), + ForLiteralParameters: globalOptions.GetOption(ForLiteralParameters, language), + ForIndexerParameters: globalOptions.GetOption(ForIndexerParameters, language), + ForObjectCreationParameters: globalOptions.GetOption(ForObjectCreationParameters, language), + ForOtherParameters: globalOptions.GetOption(ForOtherParameters, language), + SuppressForParametersThatDifferOnlyBySuffix: globalOptions.GetOption(SuppressForParametersThatDifferOnlyBySuffix, language), + SuppressForParametersThatMatchMethodIntent: globalOptions.GetOption(SuppressForParametersThatMatchMethodIntent, language), + SuppressForParametersThatMatchArgumentName: globalOptions.GetOption(SuppressForParametersThatMatchArgumentName, language)); + + public static InlineTypeHintsOptions GetInlineTypeHintsOptions(this IGlobalOptionService globalOptions, string language) + => new( + EnabledForTypes: globalOptions.GetOption(EnabledForTypes, language), + ForImplicitVariableTypes: globalOptions.GetOption(ForImplicitVariableTypes, language), + ForLambdaParameterTypes: globalOptions.GetOption(ForLambdaParameterTypes, language), + ForImplicitObjectCreation: globalOptions.GetOption(ForImplicitObjectCreation, language)); + + private const string FeatureName = "InlineHintsOptions"; + + // Parameter hints + + public static readonly PerLanguageOption2 EnabledForParameters = + new(FeatureName, + nameof(EnabledForParameters), + InlineParameterHintsOptions.Default.EnabledForParameters, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints")); + + public static readonly PerLanguageOption2 ForLiteralParameters = + new(FeatureName, + nameof(ForLiteralParameters), + InlineParameterHintsOptions.Default.ForLiteralParameters, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForLiteralParameters")); + + public static readonly PerLanguageOption2 ForIndexerParameters = + new(FeatureName, + nameof(ForIndexerParameters), + InlineParameterHintsOptions.Default.ForIndexerParameters, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForArrayIndexers")); + + public static readonly PerLanguageOption2 ForObjectCreationParameters = + new(FeatureName, + nameof(ForObjectCreationParameters), + InlineParameterHintsOptions.Default.ForObjectCreationParameters, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForObjectCreationParameters")); + + public static readonly PerLanguageOption2 ForOtherParameters = + new(FeatureName, + nameof(ForOtherParameters), + InlineParameterHintsOptions.Default.ForOtherParameters, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForOtherParameters")); + + public static readonly PerLanguageOption2 SuppressForParametersThatDifferOnlyBySuffix = + new(FeatureName, + nameof(SuppressForParametersThatDifferOnlyBySuffix), + InlineParameterHintsOptions.Default.SuppressForParametersThatDifferOnlyBySuffix, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatDifferOnlyBySuffix")); + + public static readonly PerLanguageOption2 SuppressForParametersThatMatchMethodIntent = + new(FeatureName, + nameof(SuppressForParametersThatMatchMethodIntent), + InlineParameterHintsOptions.Default.SuppressForParametersThatMatchMethodIntent, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchMethodIntent")); + + public static readonly PerLanguageOption2 SuppressForParametersThatMatchArgumentName = + new(FeatureName, + nameof(SuppressForParametersThatMatchArgumentName), + InlineParameterHintsOptions.Default.SuppressForParametersThatMatchArgumentName, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchArgumentName")); + + // Type Hints + + public static readonly PerLanguageOption2 EnabledForTypes = + new(FeatureName, + nameof(EnabledForTypes), + defaultValue: InlineTypeHintsOptions.Default.EnabledForTypes, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints")); + + public static readonly PerLanguageOption2 ForImplicitVariableTypes = + new(FeatureName, + nameof(ForImplicitVariableTypes), + defaultValue: InlineTypeHintsOptions.Default.ForImplicitVariableTypes, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitVariableTypes")); + + public static readonly PerLanguageOption2 ForLambdaParameterTypes = + new(FeatureName, + nameof(ForLambdaParameterTypes), + defaultValue: InlineTypeHintsOptions.Default.ForLambdaParameterTypes, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForLambdaParameterTypes")); + + public static readonly PerLanguageOption2 ForImplicitObjectCreation = + new(FeatureName, + nameof(ForImplicitObjectCreation), + defaultValue: InlineTypeHintsOptions.Default.ForImplicitObjectCreation, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitObjectCreation")); + } +} diff --git a/src/EditorFeatures/Core/QuickInfo/QuickInfoOptionsStorage.cs b/src/EditorFeatures/Core/QuickInfo/QuickInfoOptionsStorage.cs new file mode 100644 index 0000000000000..7eefba5b325fb --- /dev/null +++ b/src/EditorFeatures/Core/QuickInfo/QuickInfoOptionsStorage.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + internal static class QuickInfoOptionsStorage + { + public static QuickInfoOptions GetQuickInfoOptions(this IGlobalOptionService globalOptions, string? language) + => new( + ShowRemarksInQuickInfo: globalOptions.GetOption(ShowRemarksInQuickInfo, language), + IncludeNavigationHintsInQuickInfo: globalOptions.GetOption(IncludeNavigationHintsInQuickInfo)); + + private const string FeatureName = "QuickInfoOptions"; + + public static readonly PerLanguageOption2 ShowRemarksInQuickInfo = new( + FeatureName, "ShowRemarksInQuickInfo", QuickInfoOptions.Default.ShowRemarksInQuickInfo, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowRemarks")); + + public static readonly Option2 IncludeNavigationHintsInQuickInfo = new( + FeatureName, "IncludeNavigationHintsInQuickInfo", QuickInfoOptions.Default.IncludeNavigationHintsInQuickInfo, + storageLocation: new RoamingProfileStorageLocation("TextEditor.Specific.IncludeNavigationHintsInQuickInfo")); + } +} diff --git a/src/EditorFeatures/Core/SymbolDisplay/SymbolDescriptionOptionsStorage.cs b/src/EditorFeatures/Core/SymbolDisplay/SymbolDescriptionOptionsStorage.cs new file mode 100644 index 0000000000000..559eeb744fb98 --- /dev/null +++ b/src/EditorFeatures/Core/SymbolDisplay/SymbolDescriptionOptionsStorage.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.QuickInfo; + +namespace Microsoft.CodeAnalysis.LanguageServices +{ + internal static class SymbolDescriptionOptionsStorage + { + public static SymbolDescriptionOptions GetSymbolDescriptionOptions(this IGlobalOptionService globalOptions, string language) + => new( + QuickInfoOptions: globalOptions.GetQuickInfoOptions(language), + ClassificationOptions: globalOptions.GetClassificationOptions(language)); + } +} diff --git a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb index b2478a1aa7f65..7261387825995 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionTestsBase.vb @@ -10,14 +10,14 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition Imports Microsoft.CodeAnalysis.Navigation +Imports Microsoft.CodeAnalysis.Options Imports Microsoft.VisualStudio.Text Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition Public Class GoToDefinitionTestsBase - Private Shared Sub Test( + Public Shared Sub Test( workspaceDefinition As XElement, - expectedResult As Boolean, - executeOnDocument As Func(Of Document, Integer, IThreadingContext, IStreamingFindUsagesPresenter, Boolean)) + Optional expectedResult As Boolean = True) Using workspace = TestWorkspace.Create(workspaceDefinition, composition:=GoToTestHelpers.Composition) Dim solution = workspace.CurrentSolution Dim cursorDocument = workspace.Documents.First(Function(d) d.CursorPosition.HasValue) @@ -41,7 +41,12 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition Dim presenterCalled As Boolean = False Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext)() Dim presenter = New MockStreamingFindUsagesPresenter(workspace.GlobalOptions, Sub() presenterCalled = True) - Dim actualResult = executeOnDocument(document, cursorPosition, threadingContext, presenter) + + Dim goToDefService = If(document.Project.Language = LanguageNames.CSharp, + DirectCast(New CSharpGoToDefinitionService(threadingContext, presenter), IGoToDefinitionService), + New VisualBasicGoToDefinitionService(threadingContext, presenter)) + + Dim actualResult = goToDefService.TryGoToDefinition(document, cursorPosition, CancellationToken.None) Assert.Equal(expectedResult, actualResult) @@ -110,17 +115,5 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition End Using End Sub - - Friend Shared Sub Test(workspaceDefinition As XElement, Optional expectedResult As Boolean = True) - Test(workspaceDefinition, expectedResult, - Function(document, cursorPosition, threadingContext, presenter) - Dim goToDefService = If(document.Project.Language = LanguageNames.CSharp, - DirectCast(New CSharpGoToDefinitionService(threadingContext, presenter), IGoToDefinitionService), - New VisualBasicGoToDefinitionService(threadingContext, presenter)) - - Return goToDefService.TryGoToDefinition(document, cursorPosition, CancellationToken.None) - End Function) - End Sub - End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/AbstractIntellisenseQuickInfoBuilderTests.vb b/src/EditorFeatures/Test2/IntelliSense/AbstractIntellisenseQuickInfoBuilderTests.vb index b569aa20e4b83..8ab07d30a0f9d 100644 --- a/src/EditorFeatures/Test2/IntelliSense/AbstractIntellisenseQuickInfoBuilderTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/AbstractIntellisenseQuickInfoBuilderTests.vb @@ -50,7 +50,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim streamingPresenter = workspace.ExportProvider.GetExport(Of IStreamingFindUsagesPresenter)() Return Await IntellisenseQuickInfoBuilder.BuildItemAsync( trackingSpan.Object, quickInfoItem, document, - threadingContext, operationExecutor, + ClassificationOptions.Default, threadingContext, operationExecutor, AsynchronousOperationListenerProvider.NullListener, streamingPresenter, CancellationToken.None) End Using @@ -77,9 +77,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim threadingContext = workspace.ExportProvider.GetExportedValue(Of IThreadingContext)() Dim operationExecutor = workspace.ExportProvider.GetExportedValue(Of IUIThreadOperationExecutor)() Dim streamingPresenter = workspace.ExportProvider.GetExport(Of IStreamingFindUsagesPresenter)() + Dim classificationOptions = workspace.GlobalOptions.GetClassificationOptions(document.Project.Language) Return Await IntellisenseQuickInfoBuilder.BuildItemAsync( trackingSpan.Object, codeAnalysisQuickInfoItem, document, - threadingContext, operationExecutor, + classificationOptions, threadingContext, operationExecutor, AsynchronousOperationListenerProvider.NullListener, streamingPresenter, CancellationToken.None) End Using diff --git a/src/EditorFeatures/Test2/Workspaces/SymbolDescriptionServiceTests.vb b/src/EditorFeatures/Test2/Workspaces/SymbolDescriptionServiceTests.vb index 71821914edf27..4fb6a0cbb13ef 100644 --- a/src/EditorFeatures/Test2/Workspaces/SymbolDescriptionServiceTests.vb +++ b/src/EditorFeatures/Test2/Workspaces/SymbolDescriptionServiceTests.vb @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Dim symbolDescriptionService = languageServiceProvider.GetService(Of ISymbolDisplayService)() - Dim options = SymbolDescriptionOptions.From(document.Project) + Dim options = SymbolDescriptionOptions.Default Dim actualDescription = Await symbolDescriptionService.ToDescriptionStringAsync(semanticModel, cursorPosition, symbol, options) Assert.Equal(expectedDescription, actualDescription) diff --git a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs index 1001e756a9598..47fea4a45c791 100644 --- a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs +++ b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs @@ -263,7 +263,7 @@ protected async Task TestInNamespaceAsync( protected static async Task> GetSemanticClassificationsAsync(Document document, TextSpan span) { var service = document.GetRequiredLanguageService(); - var options = ClassificationOptions.From(document.Project); + var options = ClassificationOptions.Default; using var _ = ArrayBuilder.GetInstance(out var result); await service.AddSemanticClassificationsAsync(document, span, options, result, CancellationToken.None); diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 446013410a669..45a2fbeac3e1c 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -156,7 +156,7 @@ private protected async Task CheckResultsAsync( } var options = GetCompletionOptions(); - var displayOptions = SymbolDescriptionOptions.From(document.Project); + var displayOptions = SymbolDescriptionOptions.Default; var completionService = GetCompletionService(document.Project); var completionList = await GetCompletionListAsync(completionService, document, position, trigger, options); var items = completionList.Items; @@ -800,7 +800,7 @@ private async Task VerifyItemWithReferenceWorkerAsync( var document = solution.GetDocument(documentId); var options = GetCompletionOptions(); - var displayOptions = SymbolDescriptionOptions.From(document.Project); + var displayOptions = SymbolDescriptionOptions.Default; var triggerInfo = RoslynCompletion.CompletionTrigger.Invoke; var completionService = GetCompletionService(document.Project); @@ -856,7 +856,7 @@ private async Task VerifyItemWithMscorlib45WorkerAsync( var solution = testWorkspace.CurrentSolution; var documentId = testWorkspace.Documents.Single(d => d.Name == "SourceDocument").Id; var document = solution.GetDocument(documentId); - var displayOptions = SymbolDescriptionOptions.From(document.Project); + var displayOptions = SymbolDescriptionOptions.Default; var triggerInfo = RoslynCompletion.CompletionTrigger.Invoke; var completionService = GetCompletionService(document.Project); @@ -888,7 +888,7 @@ protected async Task VerifyItemInLinkedFilesAsync(string xmlString, string expec var textContainer = testWorkspace.Documents.First().GetTextBuffer().AsTextContainer(); var currentContextDocumentId = testWorkspace.GetDocumentIdInCurrentContext(textContainer); var document = solution.GetDocument(currentContextDocumentId); - var displayOptions = SymbolDescriptionOptions.From(document.Project); + var displayOptions = SymbolDescriptionOptions.Default; var triggerInfo = RoslynCompletion.CompletionTrigger.Invoke; var completionService = GetCompletionService(document.Project); diff --git a/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb b/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb index 6e421c9689479..f7e9b9e8dde1d 100644 --- a/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb +++ b/src/EditorFeatures/TestUtilities2/Intellisense/TestState.vb @@ -451,8 +451,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense Dim service = CompletionService.GetService(document) Dim roslynItem = GetSelectedItem() Dim options = CompletionOptions.Default - Dim displayOptions = SymbolDescriptionOptions.From(document.Project) - Return Await service.GetDescriptionAsync(document, roslynItem, options, displayOptions) + Return Await service.GetDescriptionAsync(document, roslynItem, options, SymbolDescriptionOptions.Default) End Function Public Sub AssertCompletionItemExpander(isAvailable As Boolean, isSelected As Boolean) diff --git a/src/EditorFeatures/VisualBasic/GoToBase/VisualBasicGoToBaseService.vb b/src/EditorFeatures/VisualBasic/GoToBase/VisualBasicGoToBaseService.vb index c69c2b91d5cbc..19ca552b051f8 100644 --- a/src/EditorFeatures/VisualBasic/GoToBase/VisualBasicGoToBaseService.vb +++ b/src/EditorFeatures/VisualBasic/GoToBase/VisualBasicGoToBaseService.vb @@ -5,6 +5,7 @@ Imports System.Composition Imports Microsoft.CodeAnalysis.GoToBase Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.Options Namespace Microsoft.CodeAnalysis.VisualBasic.GoToBase @@ -14,6 +15,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GoToBase Public Sub New() + MyBase.New() End Sub End Class End Namespace diff --git a/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb b/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb index 0a25dae688ae7..ce029770b6ef3 100644 --- a/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb +++ b/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToDefinitionService.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.Editor.GoToDefinition Imports Microsoft.CodeAnalysis.Editor.Host Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.Options Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition diff --git a/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToSymbolService.vb b/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToSymbolService.vb index 3e659db1a51c2..6caf05540eb9f 100644 --- a/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToSymbolService.vb +++ b/src/EditorFeatures/VisualBasic/GoToDefinition/VisualBasicGoToSymbolService.vb @@ -6,10 +6,11 @@ Imports System.Composition Imports Microsoft.CodeAnalysis.Editor.GoToDefinition Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.Options Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition - Friend Class VisualBasicGoToSymbolService + Friend NotInheritable Class VisualBasicGoToSymbolService Inherits AbstractGoToSymbolService diff --git a/src/Features/Core/Portable/Completion/ClassificationOptionsStorage.cs b/src/Features/Core/Portable/Completion/ClassificationOptionsStorage.cs new file mode 100644 index 0000000000000..9420003e28fb9 --- /dev/null +++ b/src/Features/Core/Portable/Completion/ClassificationOptionsStorage.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.Classification +{ + // TODO: Move to EditorFeatures https://github.com/dotnet/roslyn/issues/59184 + internal static class ClassificationOptionsStorage + { + public static ClassificationOptions GetClassificationOptions(this IGlobalOptionService globalOptions, string language) + => new( + ClassifyReassignedVariables: globalOptions.GetOption(ClassifyReassignedVariables, language), + ColorizeRegexPatterns: globalOptions.GetOption(ColorizeRegexPatterns, language), + ColorizeJsonPatterns: globalOptions.GetOption(ColorizeJsonPatterns, language)); + + public static PerLanguageOption2 ClassifyReassignedVariables = + new("ClassificationOptions", "ClassifyReassignedVariables", ClassificationOptions.Default.ClassifyReassignedVariables, + storageLocation: new RoamingProfileStorageLocation($"TextEditor.%LANGUAGE%.Specific.ClassificationOptions.ClassifyReassignedVariables")); + + public static PerLanguageOption2 ColorizeRegexPatterns = + new("RegularExpressionsOptions", "ColorizeRegexPatterns", ClassificationOptions.Default.ColorizeRegexPatterns, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ColorizeRegexPatterns")); + + public static PerLanguageOption2 ColorizeJsonPatterns = + new("JsonFeatureOptions", "ColorizeJsonPatterns", ClassificationOptions.Default.ColorizeJsonPatterns, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ColorizeJsonPatterns")); + } +} diff --git a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs index d5e481db581db..36f4b2c530487 100644 --- a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs +++ b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs @@ -41,12 +41,10 @@ public FindLiteralsProgressAdapter( public async ValueTask OnReferenceFoundAsync(Document document, TextSpan span, CancellationToken cancellationToken) { - // TODO: - // var options = await _context.GetOptionsAsync(document.Project.Language, cancellationToken).ConfigureAwait(false); - var classificationOptions = ClassificationOptions.From(document.Project); + var options = await _context.GetOptionsAsync(document.Project.Language, cancellationToken).ConfigureAwait(false); var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync( - document, span, classificationOptions, cancellationToken).ConfigureAwait(false); + document, span, options.ClassificationOptions, cancellationToken).ConfigureAwait(false); await _context.OnReferenceFoundAsync( new SourceReferenceItem(_definition, documentSpan, SymbolUsageInfo.None), cancellationToken).ConfigureAwait(false); @@ -105,10 +103,11 @@ private async ValueTask GetDefinitionItemAsync(SymbolGroup group if (!_definitionToItem.TryGetValue(group, out var definitionItem)) { definitionItem = await group.ToClassifiedDefinitionItemAsync( + _context, _solution, + _options, isPrimary: _definitionToItem.Count == 0, includeHiddenLocations: false, - _options, cancellationToken).ConfigureAwait(false); _definitionToItem[group] = definitionItem; diff --git a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs index 45a50f3e08c94..736573cc2a9ef 100644 --- a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs +++ b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs @@ -84,7 +84,7 @@ await context.SetSearchTitleAsync( foreach (var implementation in implementations) { var definitionItem = await implementation.ToClassifiedDefinitionItemAsync( - solution, isPrimary: true, includeHiddenLocations: false, FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false); + context, solution, FindReferencesSearchOptions.Default, isPrimary: true, includeHiddenLocations: false, cancellationToken).ConfigureAwait(false); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); } diff --git a/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs b/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs index 3241ee68cbfac..9cb9e83ca5d2a 100644 --- a/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs +++ b/src/Features/Core/Portable/FindUsages/IDefinitionsAndReferencesFactory.cs @@ -15,9 +15,9 @@ using Microsoft.CodeAnalysis.FindSymbols.Finders; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindUsages @@ -59,64 +59,58 @@ public static DefinitionItem ToNonClassifiedDefinitionItem( this ISymbol definition, Solution solution, bool includeHiddenLocations) - { - // Because we're passing in 'false' for 'includeClassifiedSpans', this won't ever have - // to actually do async work. This is because the only asynchrony is when we are trying - // to compute the classified spans for the locations of the definition. So it's totally - // fine to pass in CancellationToken.None and block on the result. - return ToDefinitionItemAsync( - definition, solution, isPrimary: false, includeHiddenLocations, includeClassifiedSpans: false, - options: FindReferencesSearchOptions.Default, cancellationToken: CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); - } + => ToNonClassifiedDefinitionItem(definition, solution, FindReferencesSearchOptions.Default, includeHiddenLocations); - public static Task ToNonClassifiedDefinitionItemAsync( + public static DefinitionItem ToNonClassifiedDefinitionItem( this ISymbol definition, Solution solution, - bool includeHiddenLocations, - CancellationToken cancellationToken) - { - return ToDefinitionItemAsync( - definition, solution, isPrimary: false, includeHiddenLocations, includeClassifiedSpans: false, - options: FindReferencesSearchOptions.Default with { UnidirectionalHierarchyCascade = true }, cancellationToken); - } + FindReferencesSearchOptions options, + bool includeHiddenLocations) + => ToNonClassifiedDefinitionItem(definition, definition.Locations, solution, options, isPrimary: false, includeHiddenLocations); - public static Task ToClassifiedDefinitionItemAsync( + private static DefinitionItem ToNonClassifiedDefinitionItem( + ISymbol definition, + ImmutableArray locations, + Solution solution, + FindReferencesSearchOptions options, + bool isPrimary, + bool includeHiddenLocations) + => ToDefinitionItem(definition, TryGetSourceLocations(definition, solution, locations, includeHiddenLocations), solution, options, isPrimary); + + public static async ValueTask ToClassifiedDefinitionItemAsync( this ISymbol definition, + IFindUsagesContext context, Solution solution, + FindReferencesSearchOptions options, bool isPrimary, bool includeHiddenLocations, - FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return ToDefinitionItemAsync( - definition, solution, isPrimary, - includeHiddenLocations, includeClassifiedSpans: true, - options, cancellationToken); + var unclassifiedSpans = TryGetSourceLocations(definition, solution, definition.Locations, includeHiddenLocations); + var classifiedSpans = unclassifiedSpans.IsDefault ? default : await ClassifyDocumentSpansAsync(context, unclassifiedSpans, cancellationToken).ConfigureAwait(false); + + return ToDefinitionItem(definition, classifiedSpans, solution, options, isPrimary); } - public static Task ToClassifiedDefinitionItemAsync( - this SymbolGroup group, Solution solution, bool isPrimary, bool includeHiddenLocations, FindReferencesSearchOptions options, CancellationToken cancellationToken) + public static async ValueTask ToClassifiedDefinitionItemAsync( + this SymbolGroup group, IFindUsagesContext context, Solution solution, FindReferencesSearchOptions options, bool isPrimary, bool includeHiddenLocations, CancellationToken cancellationToken) { // Make a single definition item that knows about all the locations of all the symbols in the group. + var definition = group.Symbols.First(); + var allLocations = group.Symbols.SelectMany(s => s.Locations).ToImmutableArray(); - return ToDefinitionItemAsync(group.Symbols.First(), allLocations, solution, isPrimary, includeHiddenLocations, includeClassifiedSpans: true, options, cancellationToken); - } + var unclassifiedSpans = TryGetSourceLocations(definition, solution, allLocations, includeHiddenLocations); + var classifiedSpans = unclassifiedSpans.IsDefault ? default : await ClassifyDocumentSpansAsync(context, unclassifiedSpans, cancellationToken).ConfigureAwait(false); - private static Task ToDefinitionItemAsync( - ISymbol definition, Solution solution, bool isPrimary, bool includeHiddenLocations, bool includeClassifiedSpans, FindReferencesSearchOptions options, CancellationToken cancellationToken) - { - return ToDefinitionItemAsync(definition, definition.Locations, solution, isPrimary, includeHiddenLocations, includeClassifiedSpans, options, cancellationToken); + return ToDefinitionItem(definition, classifiedSpans, solution, options, isPrimary); } - private static async Task ToDefinitionItemAsync( + private static DefinitionItem ToDefinitionItem( ISymbol definition, - ImmutableArray locations, + ImmutableArray sourceLocations, Solution solution, - bool isPrimary, - bool includeHiddenLocations, - bool includeClassifiedSpans, FindReferencesSearchOptions options, - CancellationToken cancellationToken) + bool isPrimary) { // Ensure we're working with the original definition for the symbol. I.e. When we're // creating definition items, we want to create them for types like Dictionary @@ -140,45 +134,14 @@ private static async Task ToDefinitionItemAsync( var properties = GetProperties(definition, isPrimary); - // If it's a namespace, don't create any normal location. Namespaces - // come from many different sources, but we'll only show a single - // root definition node for it. That node won't be navigable. - using var sourceLocations = TemporaryArray.Empty; - if (definition.Kind != SymbolKind.Namespace) + if (sourceLocations.IsDefault) { - foreach (var location in locations) - { - if (location.IsInMetadata) - { - return DefinitionItem.CreateMetadataDefinition( - tags, displayParts, nameDisplayParts, solution, - definition, properties, displayIfNoReferences); - } - else if (location.IsInSource) - { - if (!location.IsVisibleSourceLocation() && - !includeHiddenLocations) - { - continue; - } - - var document = solution.GetDocument(location.SourceTree); - if (document != null) - { - var classificationOptions = ClassificationOptions.From(document.Project); - - var documentLocation = !includeClassifiedSpans - ? new DocumentSpan(document, location.SourceSpan) - : await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync( - document, location.SourceSpan, classificationOptions, cancellationToken).ConfigureAwait(false); - - sourceLocations.Add(documentLocation); - } - } - } + return DefinitionItem.CreateMetadataDefinition( + tags, displayParts, nameDisplayParts, solution, + definition, properties, displayIfNoReferences); } - if (sourceLocations.Count == 0) + if (sourceLocations.IsEmpty) { // If we got no definition locations, then create a sentinel one // that we can display but which will not allow navigation. @@ -191,10 +154,58 @@ private static async Task ToDefinitionItemAsync( var displayableProperties = AbstractReferenceFinder.GetAdditionalFindUsagesProperties(definition); return DefinitionItem.Create( - tags, displayParts, sourceLocations.ToImmutableAndClear(), + tags, displayParts, sourceLocations, nameDisplayParts, properties, displayableProperties, displayIfNoReferences); } + private static ImmutableArray TryGetSourceLocations(ISymbol definition, Solution solution, ImmutableArray locations, bool includeHiddenLocations) + { + // If it's a namespace, don't create any normal location. Namespaces + // come from many different sources, but we'll only show a single + // root definition node for it. That node won't be navigable. + if (definition.Kind == SymbolKind.Namespace) + { + return ImmutableArray.Empty; + } + + // If it's a namespace, don't create any normal location. Namespaces + // come from many different sources, but we'll only show a single + // root definition node for it. That node won't be navigable. + using var sourceLocations = TemporaryArray.Empty; + + foreach (var location in locations) + { + if (location.IsInMetadata) + { + return default; + } + + if (location.IsInSource) + { + if (!location.IsVisibleSourceLocation() && + !includeHiddenLocations) + { + continue; + } + + var document = solution.GetDocument(location.SourceTree); + if (document != null) + { + sourceLocations.Add(new DocumentSpan(document, location.SourceSpan)); + } + } + } + + return sourceLocations.ToImmutableAndClear(); + } + + private static ValueTask> ClassifyDocumentSpansAsync(IFindUsagesContext context, ImmutableArray unclassifiedSpans, CancellationToken cancellationToken) + => unclassifiedSpans.SelectAsArrayAsync(async (documentSpan, context, cancellationToken) => + { + var options = await context.GetOptionsAsync(documentSpan.Document.Project.Language, cancellationToken).ConfigureAwait(false); + return await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(documentSpan.Document, documentSpan.SourceSpan, options.ClassificationOptions, cancellationToken).ConfigureAwait(false); + }, context, cancellationToken); + private static ImmutableDictionary GetProperties(ISymbol definition, bool isPrimary) { var properties = ImmutableDictionary.Empty; @@ -226,9 +237,7 @@ private static ImmutableDictionary GetProperties(ISymbol definit public static async Task TryCreateSourceReferenceItemAsync( this ReferenceLocation referenceLocation, -#pragma warning disable IDE0060 // Remove unused parameter TODO IFindUsagesContext context, -#pragma warning restore IDE0060 // Remove unused parameter DefinitionItem definitionItem, bool includeHiddenLocations, CancellationToken cancellationToken) @@ -245,12 +254,10 @@ private static ImmutableDictionary GetProperties(ISymbol definit var document = referenceLocation.Document; var sourceSpan = location.SourceSpan; - // TODO: - // var options = await context.GetOptionsAsync(document.Project.Language, cancellationToken).ConfigureAwait(false); - var classificationOptions = ClassificationOptions.From(document.Project); + var options = await context.GetOptionsAsync(document.Project.Language, cancellationToken).ConfigureAwait(false); var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync( - document, sourceSpan, classificationOptions, cancellationToken).ConfigureAwait(false); + document, sourceSpan, options.ClassificationOptions, cancellationToken).ConfigureAwait(false); return new SourceReferenceItem(definitionItem, documentSpan, referenceLocation.SymbolUsageInfo, referenceLocation.AdditionalProperties); } diff --git a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs index e54e30b177fdb..bb6c43ca0113d 100644 --- a/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs +++ b/src/Features/Core/Portable/GoToBase/AbstractGoToBaseService.cs @@ -2,18 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.GoToBase { - internal abstract partial class AbstractGoToBaseService : IGoToBaseService + internal abstract class AbstractGoToBaseService : IGoToBaseService { + protected AbstractGoToBaseService() + { + } + public async Task FindBasesAsync(IFindUsagesContext context, Document document, int position, CancellationToken cancellationToken) { var symbolAndProjectOpt = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( @@ -48,7 +51,7 @@ await context.SetSearchTitleAsync( if (sourceDefinition != null) { var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync( - solution, isPrimary: true, includeHiddenLocations: false, FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false); + context, solution, FindReferencesSearchOptions.Default, isPrimary: true, includeHiddenLocations: false, cancellationToken: cancellationToken).ConfigureAwait(false); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); found = true; @@ -56,7 +59,7 @@ await context.SetSearchTitleAsync( else if (baseSymbol.Locations.Any(l => l.IsInMetadata)) { var definitionItem = baseSymbol.ToNonClassifiedDefinitionItem( - solution, includeHiddenLocations: true); + solution, FindReferencesSearchOptions.Default, includeHiddenLocations: true); await context.OnDefinitionFoundAsync(definitionItem, cancellationToken).ConfigureAwait(false); found = true; } diff --git a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginServiceHelpers.cs b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginServiceHelpers.cs index 499d0d7f2c881..fa720ed555fe8 100644 --- a/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginServiceHelpers.cs +++ b/src/Features/Core/Portable/InheritanceMargin/InheritanceMarginServiceHelpers.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols.FindReferences; using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -39,7 +40,7 @@ public static async ValueTask> ImmutableArray<(SymbolKey symbolKey, int lineNumber)> symbolKeyAndLineNumbers, CancellationToken cancellationToken) { - var remoteClient = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false); + var remoteClient = await RemoteHostClient.TryGetClientAsync(solution.Workspace.Services, cancellationToken).ConfigureAwait(false); if (remoteClient != null) { // Here the line number is also passed to the remote process. It is done in this way because @@ -362,10 +363,7 @@ private static async ValueTask CreateInherita targetSymbol = symbolInSource ?? targetSymbol; // Right now the targets are not shown in a classified way. - var definition = await ToSlimDefinitionItemAsync( - targetSymbol, - solution, - cancellationToken: cancellationToken).ConfigureAwait(false); + var definition = ToSlimDefinitionItem(targetSymbol, solution); var displayName = targetSymbol.ToDisplayString(s_displayFormat); @@ -503,16 +501,16 @@ private static async Task> GetDerivedTypesAndIm /// Otherwise, create the full non-classified DefinitionItem. Because in such case we want to display all the locations to the user /// by reusing the FAR window. ///
- private static async Task ToSlimDefinitionItemAsync(ISymbol symbol, Solution solution, CancellationToken cancellationToken) + private static DefinitionItem ToSlimDefinitionItem(ISymbol symbol, Solution solution) { RoslynDebug.Assert(IsNavigableSymbol(symbol)); var locations = symbol.Locations; if (locations.Length > 1) { - return await symbol.ToNonClassifiedDefinitionItemAsync( + return symbol.ToNonClassifiedDefinitionItem( solution, - includeHiddenLocations: false, - cancellationToken: cancellationToken).ConfigureAwait(false); + FindReferencesSearchOptions.Default with { UnidirectionalHierarchyCascade = true }, + includeHiddenLocations: false); } if (locations.Length == 1) diff --git a/src/Features/Core/Portable/InlineHints/InlineHintsOptions.cs b/src/Features/Core/Portable/InlineHints/InlineHintsOptions.cs index a2a2396a324d0..6989aae1efb36 100644 --- a/src/Features/Core/Portable/InlineHints/InlineHintsOptions.cs +++ b/src/Features/Core/Portable/InlineHints/InlineHintsOptions.cs @@ -2,23 +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.Runtime.Serialization; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.InlineHints { + [DataContract] internal readonly record struct InlineHintsOptions( - InlineParameterHintsOptions ParameterOptions, - InlineTypeHintsOptions TypeOptions, - SymbolDescriptionOptions DisplayOptions) - { - public static InlineHintsOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static InlineHintsOptions From(OptionSet options, string language) - => new( - ParameterOptions: InlineParameterHintsOptions.From(options, language), - TypeOptions: InlineTypeHintsOptions.From(options, language), - DisplayOptions: SymbolDescriptionOptions.From(options, language)); - } + [property: DataMember(Order = 0)] InlineParameterHintsOptions ParameterOptions, + [property: DataMember(Order = 1)] InlineTypeHintsOptions TypeOptions, + [property: DataMember(Order = 2)] SymbolDescriptionOptions DisplayOptions); } diff --git a/src/Features/Core/Portable/InlineHints/InlineParameterHintsOptions.cs b/src/Features/Core/Portable/InlineHints/InlineParameterHintsOptions.cs index 5eec066d24f12..77f3a006ef6b0 100644 --- a/src/Features/Core/Portable/InlineHints/InlineParameterHintsOptions.cs +++ b/src/Features/Core/Portable/InlineHints/InlineParameterHintsOptions.cs @@ -2,107 +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. -using System; -using System.Collections.Immutable; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.InlineHints { + [DataContract] internal readonly record struct InlineParameterHintsOptions( - bool EnabledForParameters, - bool ForLiteralParameters, - bool ForIndexerParameters, - bool ForObjectCreationParameters, - bool ForOtherParameters, - bool SuppressForParametersThatDifferOnlyBySuffix, - bool SuppressForParametersThatMatchMethodIntent, - bool SuppressForParametersThatMatchArgumentName) + [property: DataMember(Order = 0)] bool EnabledForParameters = false, + [property: DataMember(Order = 1)] bool ForLiteralParameters = true, + [property: DataMember(Order = 2)] bool ForIndexerParameters = true, + [property: DataMember(Order = 3)] bool ForObjectCreationParameters = true, + [property: DataMember(Order = 4)] bool ForOtherParameters = false, + [property: DataMember(Order = 5)] bool SuppressForParametersThatDifferOnlyBySuffix = true, + [property: DataMember(Order = 6)] bool SuppressForParametersThatMatchMethodIntent = true, + [property: DataMember(Order = 7)] bool SuppressForParametersThatMatchArgumentName = true) { - public static InlineParameterHintsOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static InlineParameterHintsOptions From(OptionSet options, string language) - => new( - EnabledForParameters: options.GetOption(Metadata.EnabledForParameters, language), - ForLiteralParameters: options.GetOption(Metadata.ForLiteralParameters, language), - ForIndexerParameters: options.GetOption(Metadata.ForIndexerParameters, language), - ForObjectCreationParameters: options.GetOption(Metadata.ForObjectCreationParameters, language), - ForOtherParameters: options.GetOption(Metadata.ForOtherParameters, language), - SuppressForParametersThatDifferOnlyBySuffix: options.GetOption(Metadata.SuppressForParametersThatDifferOnlyBySuffix, language), - SuppressForParametersThatMatchMethodIntent: options.GetOption(Metadata.SuppressForParametersThatMatchMethodIntent, language), - SuppressForParametersThatMatchArgumentName: options.GetOption(Metadata.SuppressForParametersThatMatchArgumentName, language)); - - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider + public InlineParameterHintsOptions() + : this(EnabledForParameters: false) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - EnabledForParameters, - ForLiteralParameters, - ForIndexerParameters, - ForObjectCreationParameters, - ForOtherParameters, - SuppressForParametersThatDifferOnlyBySuffix, - SuppressForParametersThatMatchMethodIntent, - SuppressForParametersThatMatchArgumentName); - - private const string FeatureName = "InlineHintsOptions"; - - public static readonly PerLanguageOption2 EnabledForParameters = - new(FeatureName, - nameof(EnabledForParameters), - defaultValue: false, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints")); - - public static readonly PerLanguageOption2 ForLiteralParameters = - new(FeatureName, - nameof(ForLiteralParameters), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForLiteralParameters")); - - public static readonly PerLanguageOption2 ForObjectCreationParameters = - new(FeatureName, - nameof(ForObjectCreationParameters), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForObjectCreationParameters")); - - public static readonly PerLanguageOption2 ForOtherParameters = - new(FeatureName, - nameof(ForOtherParameters), - defaultValue: false, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForOtherParameters")); - - public static readonly PerLanguageOption2 ForIndexerParameters = - new(FeatureName, - nameof(ForIndexerParameters), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.ForArrayIndexers")); - - public static readonly PerLanguageOption2 SuppressForParametersThatDifferOnlyBySuffix = - new(FeatureName, - nameof(SuppressForParametersThatDifferOnlyBySuffix), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatDifferOnlyBySuffix")); - - public static readonly PerLanguageOption2 SuppressForParametersThatMatchMethodIntent = - new(FeatureName, - nameof(SuppressForParametersThatMatchMethodIntent), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchMethodIntent")); - - public static readonly PerLanguageOption2 SuppressForParametersThatMatchArgumentName = - new(FeatureName, - nameof(SuppressForParametersThatMatchArgumentName), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineParameterNameHints.SuppressForParametersThatMatchArgumentName")); } + + public static readonly InlineParameterHintsOptions Default = new(); } } diff --git a/src/Features/Core/Portable/InlineHints/InlineTypeHintsOptions.cs b/src/Features/Core/Portable/InlineHints/InlineTypeHintsOptions.cs index a3fab02d85102..5043536f63a77 100644 --- a/src/Features/Core/Portable/InlineHints/InlineTypeHintsOptions.cs +++ b/src/Features/Core/Portable/InlineHints/InlineTypeHintsOptions.cs @@ -2,71 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Immutable; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.InlineHints { + [DataContract] internal readonly record struct InlineTypeHintsOptions( - bool EnabledForTypes, - bool ForImplicitVariableTypes, - bool ForLambdaParameterTypes, - bool ForImplicitObjectCreation) + [property: DataMember(Order = 0)] bool EnabledForTypes = false, + [property: DataMember(Order = 1)] bool ForImplicitVariableTypes = true, + [property: DataMember(Order = 2)] bool ForLambdaParameterTypes = true, + [property: DataMember(Order = 3)] bool ForImplicitObjectCreation = true) { - public static InlineTypeHintsOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static InlineTypeHintsOptions From(OptionSet options, string language) - => new( - EnabledForTypes: options.GetOption(Metadata.EnabledForTypes, language), - ForImplicitVariableTypes: options.GetOption(Metadata.ForImplicitVariableTypes, language), - ForLambdaParameterTypes: options.GetOption(Metadata.ForLambdaParameterTypes, language), - ForImplicitObjectCreation: options.GetOption(Metadata.ForImplicitObjectCreation, language)); - - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider + public InlineTypeHintsOptions() + : this(EnabledForTypes: false) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - EnabledForTypes, - ForImplicitVariableTypes, - ForLambdaParameterTypes, - ForImplicitObjectCreation); - - private const string FeatureName = "InlineHintsOptions"; - - public static readonly PerLanguageOption2 EnabledForTypes = - new(FeatureName, - nameof(EnabledForTypes), - defaultValue: false, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints")); - - public static readonly PerLanguageOption2 ForImplicitVariableTypes = - new(FeatureName, - nameof(ForImplicitVariableTypes), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitVariableTypes")); - - public static readonly PerLanguageOption2 ForLambdaParameterTypes = - new(FeatureName, - nameof(ForLambdaParameterTypes), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForLambdaParameterTypes")); - - public static readonly PerLanguageOption2 ForImplicitObjectCreation = - new(FeatureName, - nameof(ForImplicitObjectCreation), - defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.InlineTypeHints.ForImplicitObjectCreation")); } + + public static readonly InlineTypeHintsOptions Default = new(); } } diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionOptions.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionOptions.cs index 83bc38eccad52..3530c39a61606 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionOptions.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionOptions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.QuickInfo; namespace Microsoft.CodeAnalysis.LanguageServices @@ -16,13 +15,5 @@ public static readonly SymbolDescriptionOptions Default = new( QuickInfoOptions: QuickInfoOptions.Default, ClassificationOptions: ClassificationOptions.Default); - - public static SymbolDescriptionOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static SymbolDescriptionOptions From(OptionSet options, string language) - => new( - QuickInfoOptions: QuickInfoOptions.From(options, language), - ClassificationOptions: ClassificationOptions.From(options, language)); } } diff --git a/src/Features/Core/Portable/QuickInfo/QuickInfoOptions.cs b/src/Features/Core/Portable/QuickInfo/QuickInfoOptions.cs index 96bf53f33c547..9542c46d2ea0e 100644 --- a/src/Features/Core/Portable/QuickInfo/QuickInfoOptions.cs +++ b/src/Features/Core/Portable/QuickInfo/QuickInfoOptions.cs @@ -2,55 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Immutable; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; +using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.QuickInfo { + [DataContract] internal readonly record struct QuickInfoOptions( - bool ShowRemarksInQuickInfo, - bool IncludeNavigationHintsInQuickInfo) + [property: DataMember(Order = 0)] bool ShowRemarksInQuickInfo = true, + [property: DataMember(Order = 1)] bool IncludeNavigationHintsInQuickInfo = true) { - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider + public QuickInfoOptions() + : this(ShowRemarksInQuickInfo: true) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - ShowRemarksInQuickInfo, - IncludeNavigationHintsInQuickInfo); - - private const string FeatureName = "QuickInfoOptions"; - - public static readonly PerLanguageOption2 ShowRemarksInQuickInfo = new( - FeatureName, "ShowRemarksInQuickInfo", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowRemarks")); - - public static readonly Option2 IncludeNavigationHintsInQuickInfo = new( - FeatureName, "IncludeNavigationHintsInQuickInfo", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.Specific.IncludeNavigationHintsInQuickInfo")); } - public static readonly QuickInfoOptions Default - = new( - ShowRemarksInQuickInfo: Metadata.ShowRemarksInQuickInfo.DefaultValue, - IncludeNavigationHintsInQuickInfo: Metadata.IncludeNavigationHintsInQuickInfo.DefaultValue); - - public static QuickInfoOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static QuickInfoOptions From(OptionSet options, string? language) - => new( - ShowRemarksInQuickInfo: options.GetOption(Metadata.ShowRemarksInQuickInfo, language), - IncludeNavigationHintsInQuickInfo: options.GetOption(Metadata.IncludeNavigationHintsInQuickInfo)); - + public static readonly QuickInfoOptions Default = new(); } } diff --git a/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs b/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs index 559595051f36a..719ba0d1bc6af 100644 --- a/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs +++ b/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.EmbeddedLanguages.StackFrame; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Roslyn.Utilities; diff --git a/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerUtilities.cs b/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerUtilities.cs index b1dd486a7cee2..b21e94b754e50 100644 --- a/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerUtilities.cs +++ b/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerUtilities.cs @@ -5,7 +5,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.EmbeddedLanguages.StackFrame; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -61,7 +63,7 @@ internal static class StackTraceExplorerUtilities var method = await TryGetBestMatchAsync(project, fullyQualifiedTypeName, methodNode, methodArguments, methodTypeArguments, cancellationToken).ConfigureAwait(false); if (method is not null) { - return await GetDefinitionAsync(method).ConfigureAwait(false); + return GetDefinition(method); } } else @@ -79,7 +81,7 @@ internal static class StackTraceExplorerUtilities var method = await TryGetBestMatchAsync(project, fullyQualifiedTypeName, methodNode, methodArguments, methodTypeArguments, cancellationToken).ConfigureAwait(false); if (method is not null) { - return await GetDefinitionAsync(method).ConfigureAwait(false); + return GetDefinition(method); } } @@ -89,7 +91,7 @@ internal static class StackTraceExplorerUtilities // Local Functions // - Task GetDefinitionAsync(IMethodSymbol method) + DefinitionItem GetDefinition(IMethodSymbol method) { ISymbol symbol = method; if (symbolPart == StackFrameSymbolPart.ContainingType) @@ -97,7 +99,10 @@ Task GetDefinitionAsync(IMethodSymbol method) symbol = method.ContainingType; } - return symbol.ToNonClassifiedDefinitionItemAsync(solution, includeHiddenLocations: true, cancellationToken); + return symbol.ToNonClassifiedDefinitionItem( + solution, + FindReferencesSearchOptions.Default with { UnidirectionalHierarchyCascade = true }, + includeHiddenLocations: true); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs index 0a0382b980f8b..ab28f71a1f81c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensHelpers.cs @@ -134,6 +134,7 @@ static SemanticTokensHelpers() Document document, Dictionary tokenTypesToIndex, LSP.Range? range, + ClassificationOptions options, bool includeSyntacticClassifications, CancellationToken cancellationToken) { @@ -151,8 +152,6 @@ static SemanticTokensHelpers() var isFinalized = document.Project.TryGetCompilation(out var compilation) && compilation == semanticModel.Compilation; document = frozenDocument; - var options = ClassificationOptions.From(document.Project); - var classifiedSpans = await GetClassifiedSpansForDocumentAsync( document, textSpan, options, includeSyntacticClassifications, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs index d2ff7b10ab2ae..2f9513c825157 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs @@ -6,7 +6,9 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -18,8 +20,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens ///
[ExportRoslynLanguagesLspRequestHandlerProvider, Shared] [ProvidesMethod(Methods.TextDocumentSemanticTokensRangeName)] - internal class SemanticTokensRangeHandler : AbstractStatelessRequestHandler + internal sealed class SemanticTokensRangeHandler : AbstractStatelessRequestHandler { + private readonly IGlobalOptionService _globalOptions; + public override string Method => LSP.Methods.TextDocumentSemanticTokensRangeName; public override bool MutatesSolutionState => false; @@ -27,8 +31,9 @@ internal class SemanticTokensRangeHandler : AbstractStatelessRequestHandler CreateFromJsonAsync(string jsonCont analyzerReferences: parsedCommandLine.AnalyzerReferences.Select(r => new AnalyzerFileReference(r.FilePath, analyzerLoader))) .WithAnalyzerConfigDocuments(parsedCommandLine.AnalyzerConfigPaths.Select(CreateDocumentInfo)); - workspace.AddProject(projectInfo); + var solution = workspace.CurrentSolution.AddProject(projectInfo); + var compilation = await solution.GetRequiredProject(projectId).GetRequiredCompilationAsync(CancellationToken.None); + var options = GeneratorOptions.Default; - var compilation = await workspace.CurrentSolution.GetProject(projectId)!.GetRequiredCompilationAsync(CancellationToken.None); - - return new CompilerInvocation(compilation, languageServices, invocationInfo.ProjectFilePath, workspace.CurrentSolution.Options); + return new CompilerInvocation(compilation, languageServices, invocationInfo.ProjectFilePath, options); // Local methods: DocumentInfo CreateDocumentInfo(string unmappedPath) diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 7a7274f736a6f..310d6729103db 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -14,9 +14,6 @@ using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.ResultSetTracking; using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.QuickInfo; -using Microsoft.CodeAnalysis.Structure; using Roslyn.Utilities; using Methods = Microsoft.VisualStudio.LanguageServer.Protocol.Methods; @@ -52,7 +49,7 @@ public static Generator CreateAndWriteCapabilitiesVertex(ILsifJsonWriter lsifJso return generator; } - public async Task GenerateForCompilationAsync(Compilation compilation, string projectPath, HostLanguageServices languageServices, OptionSet options) + public async Task GenerateForCompilationAsync(Compilation compilation, string projectPath, HostLanguageServices languageServices, GeneratorOptions options) { var projectVertex = new Graph.LsifProject(kind: GetLanguageKind(compilation.Language), new Uri(projectPath), _idFactory); _lsifJsonWriter.Write(projectVertex); @@ -68,9 +65,16 @@ public async Task GenerateForCompilationAsync(Compilation compilation, string pr // Disable navigation hints in quick info as computing them both takes too long, and they're never // even emitted in the final lsif hover information. - var workspace = languageServices.WorkspaceServices.Workspace; - workspace.SetOptions(workspace.Options.WithChangedOption( - QuickInfoOptions.Metadata.IncludeNavigationHintsInQuickInfo, false)); + options = options with + { + SymbolDescriptionOptions = options.SymbolDescriptionOptions with + { + QuickInfoOptions = options.SymbolDescriptionOptions.QuickInfoOptions with + { + IncludeNavigationHintsInQuickInfo = false + } + } + }; var tasks = new List(); foreach (var syntaxTree in compilation.SyntaxTrees) @@ -115,7 +119,7 @@ public async Task GenerateForCompilationAsync(Compilation compilation, string pr private static async Task> GenerateForDocumentAsync( SemanticModel semanticModel, HostLanguageServices languageServices, - OptionSet options, + GeneratorOptions options, IResultSetTracker topLevelSymbolsResultSetTracker, ILsifJsonWriter lsifJsonWriter, IdFactory idFactory) @@ -235,8 +239,7 @@ SymbolKind.RangeVariable or // See https://github.com/Microsoft/language-server-protocol/blob/main/indexFormat/specification.md#resultset for an example. if (symbolResultsTracker.ResultSetNeedsInformationalEdgeAdded(symbolForLinkedResultSet, Methods.TextDocumentHoverName)) { - var displayOptions = SymbolDescriptionOptions.From(options, languageServices.Language); - var hover = await HoverHandler.GetHoverAsync(semanticModel, syntaxToken.SpanStart, displayOptions, languageServices, CancellationToken.None); + var hover = await HoverHandler.GetHoverAsync(semanticModel, syntaxToken.SpanStart, options.SymbolDescriptionOptions, languageServices, CancellationToken.None); if (hover != null) { var hoverResult = new HoverResult(hover, idFactory); @@ -250,8 +253,7 @@ SymbolKind.RangeVariable or lsifJsonWriter.Write(Edge.Create("contains", documentVertex.GetId(), rangeVertices, idFactory)); // Write the folding ranges for the document. - var blockStructureOptions = BlockStructureOptions.From(options, languageServices.Language, isMetadataAsSource: false); - var foldingRanges = FoldingRangesHandler.GetFoldingRanges(syntaxTree, languageServices, blockStructureOptions, CancellationToken.None); + var foldingRanges = FoldingRangesHandler.GetFoldingRanges(syntaxTree, languageServices, options.BlockStructureOptions, CancellationToken.None); var foldingRangeResult = new FoldingRangeResult(foldingRanges, idFactory); lsifJsonWriter.Write(foldingRangeResult); lsifJsonWriter.Write(Edge.Create(Methods.TextDocumentFoldingRangeName, documentVertex.GetId(), foldingRangeResult.GetId(), idFactory)); diff --git a/src/Features/Lsif/Generator/GeneratorOptions.cs b/src/Features/Lsif/Generator/GeneratorOptions.cs new file mode 100644 index 0000000000000..4f008e6c6abe5 --- /dev/null +++ b/src/Features/Lsif/Generator/GeneratorOptions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Structure; + +namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator +{ + internal readonly record struct GeneratorOptions( + SymbolDescriptionOptions SymbolDescriptionOptions, + BlockStructureOptions BlockStructureOptions) + { + public static readonly GeneratorOptions Default = + new(SymbolDescriptionOptions.Default, + BlockStructureOptions.Default); + } +} diff --git a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj index fac7fa750c327..cd51006b9d436 100644 --- a/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj +++ b/src/Features/Lsif/Generator/Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.csproj @@ -41,6 +41,10 @@ + + + + diff --git a/src/Features/Lsif/Generator/Program.cs b/src/Features/Lsif/Generator/Program.cs index 6a87fa727d830..2b98744299bb5 100644 --- a/src/Features/Lsif/Generator/Program.cs +++ b/src/Features/Lsif/Generator/Program.cs @@ -135,6 +135,8 @@ private static async Task GenerateWithMSBuildLocatedAsync( var solution = await openAsync(msbuildWorkspace); + var options = GeneratorOptions.Default; + await logFile.WriteLineAsync($"Load completed in {solutionLoadStopwatch.Elapsed.ToDisplayString()}."); var lsifGenerator = Generator.CreateAndWriteCapabilitiesVertex(lsifWriter); @@ -151,7 +153,7 @@ private static async Task GenerateWithMSBuildLocatedAsync( await logFile.WriteLineAsync($"Fetch of compilation for {project.FilePath} completed in {compilationCreationStopwatch.Elapsed.ToDisplayString()}."); var generationForProjectStopwatch = Stopwatch.StartNew(); - await lsifGenerator.GenerateForCompilationAsync(compilation, project.FilePath, project.LanguageServices, project.Solution.Options); + await lsifGenerator.GenerateForCompilationAsync(compilation, project.FilePath, project.LanguageServices, options); generationForProjectStopwatch.Stop(); totalTimeInGenerationPhase += generationForProjectStopwatch.Elapsed; diff --git a/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb b/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb index 8a10939d1cf69..643b46cdf3c05 100644 --- a/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb +++ b/src/Features/Lsif/GeneratorTest/Utilities/TestLsifOutput.vb @@ -35,7 +35,7 @@ Namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.UnitTests.U ' Assert we don't have any errors to prevent any typos in the tests Assert.Empty(compilation.GetDiagnostics().Where(Function(d) d.Severity = DiagnosticSeverity.Error)) - Await lsifGenerator.GenerateForCompilationAsync(compilation, project.FilePath, project.LanguageServices, project.Solution.Options) + Await lsifGenerator.GenerateForCompilationAsync(compilation, project.FilePath, project.LanguageServices, GeneratorOptions.Default) Next End Function diff --git a/src/Tools/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsService.cs b/src/Tools/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsService.cs deleted file mode 100644 index 01c26617fcc5b..0000000000000 --- a/src/Tools/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsService.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.InlineHints; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.InlineHints -{ - internal static class OmniSharpInlineHintsService - { - public static async Task> GetInlineHintsAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken) - { - var service = document.GetRequiredLanguageService(); - var options = InlineHintsOptions.From(document.Project); - - var hints = await service.GetInlineHintsAsync(document, textSpan, options, cancellationToken).ConfigureAwait(false); - return hints.SelectAsArray(static h => new OmniSharpInlineHint( - h.Span, - h.DisplayParts, - (document, cancellationToken) => h.GetDescriptionAsync(document, cancellationToken))); - } - } - - internal readonly struct OmniSharpInlineHint - { - private readonly Func>> _getDescriptionAsync; - - public OmniSharpInlineHint( - TextSpan span, - ImmutableArray displayParts, - Func>> getDescriptionAsync) - { - Span = span; - DisplayParts = displayParts; - _getDescriptionAsync = getDescriptionAsync; - } - - public readonly TextSpan Span { get; } - public readonly ImmutableArray DisplayParts { get; } - - public Task> GetDescrptionAsync(Document document, CancellationToken cancellationToken) - => _getDescriptionAsync.Invoke(document, cancellationToken); - } -} diff --git a/src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs b/src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs index fed026d507d1c..8f108e50f09d8 100644 --- a/src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs +++ b/src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs @@ -9,12 +9,6 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { - [Obsolete("Use IRazorDocumentExcerptServiceImplementation instead")] - internal interface IRazorDocumentExcerptService - { - Task TryExcerptAsync(Document document, TextSpan span, RazorExcerptMode mode, CancellationToken cancellationToken); - } - internal interface IRazorDocumentExcerptServiceImplementation { Task TryExcerptAsync(Document document, TextSpan span, RazorExcerptMode mode, RazorClassificationOptionsWrapper options, CancellationToken cancellationToken); diff --git a/src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs index 10dccf7614aa3..ff0cdfc1b8e6b 100644 --- a/src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.ComponentModel; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; @@ -15,19 +13,12 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { internal sealed class RazorDocumentExcerptServiceWrapper : IDocumentExcerptService { - [Obsolete] - private readonly IRazorDocumentExcerptService? _legacyRazorDocumentExcerptService; - - private readonly IRazorDocumentExcerptServiceImplementation? _impl; - - [Obsolete] - public RazorDocumentExcerptServiceWrapper(IRazorDocumentExcerptService razorDocumentExcerptService) - => _legacyRazorDocumentExcerptService = razorDocumentExcerptService; + private readonly IRazorDocumentExcerptServiceImplementation _impl; public RazorDocumentExcerptServiceWrapper(IRazorDocumentExcerptServiceImplementation impl) => _impl = impl; - public async Task TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, CancellationToken cancellationToken) + public async Task TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, ClassificationOptions classificationOptions, CancellationToken cancellationToken) { var razorMode = mode switch { @@ -36,20 +27,7 @@ public RazorDocumentExcerptServiceWrapper(IRazorDocumentExcerptServiceImplementa _ => throw ExceptionUtilities.UnexpectedValue(mode), }; - RazorExcerptResult? result; - if (_impl != null) - { - var options = ClassificationOptions.From(document.Project); - result = await _impl.TryExcerptAsync(document, span, razorMode, new RazorClassificationOptionsWrapper(options), cancellationToken).ConfigureAwait(false); - } - else - { -#pragma warning disable CS0612 // Type or member is obsolete - Contract.ThrowIfNull(_legacyRazorDocumentExcerptService); - result = await _legacyRazorDocumentExcerptService.TryExcerptAsync(document, span, razorMode, cancellationToken).ConfigureAwait(false); -#pragma warning restore - } - + var result = await _impl.TryExcerptAsync(document, span, razorMode, new RazorClassificationOptionsWrapper(classificationOptions), cancellationToken).ConfigureAwait(false); var razorExcerpt = result.Value; return new ExcerptResult(razorExcerpt.Content, razorExcerpt.MappedSpan, razorExcerpt.ClassifiedSpans, razorExcerpt.Document, razorExcerpt.Span); } diff --git a/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs index b490d3b5c7089..d2817e421d4d0 100644 --- a/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs @@ -51,19 +51,7 @@ public RazorDocumentServiceProviderWrapper(IRazorDocumentServiceProvider innerDo static documentServiceProvider => { var impl = documentServiceProvider.GetService(); - if (impl != null) - { - return new RazorDocumentExcerptServiceWrapper(impl); - } - -#pragma warning disable CS0612, CS0618 // Type or member is obsolete - var legacyImpl = documentServiceProvider.GetService(); - if (legacyImpl != null) - { - return new RazorDocumentExcerptServiceWrapper(legacyImpl); - } -#pragma warning restore - return null; + return (impl != null) ? new RazorDocumentExcerptServiceWrapper(impl) : null; }, _innerDocumentServiceProvider); diff --git a/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs b/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs index a544e61846fa2..c05367103a6ae 100644 --- a/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs +++ b/src/Tools/IdeBenchmarks/RegexClassifierBenchmarks.cs @@ -84,12 +84,11 @@ protected static async Task> GetSemanticClassific var extensionManager = document.Project.Solution.Workspace.Services.GetService(); var results = ArrayBuilder.GetInstance(); - var options = ClassificationOptions.From(document.Project); await service.AddSemanticClassificationsAsync( document, span, - options, + ClassificationOptions.Default, extensionManager.CreateNodeExtensionGetter(classifiers, c => c.SyntaxNodeTypes), extensionManager.CreateTokenExtensionGetter(classifiers, c => c.SyntaxTokenKinds), results, diff --git a/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs b/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs index 7b84beec18987..2f7788914422c 100644 --- a/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs +++ b/src/Tools/IdeCoreBenchmarks/ClassificationBenchmarks.cs @@ -99,9 +99,8 @@ private async Task LoadSolutionAsync() protected static async Task> GetSemanticClassificationsAsync(Document document, TextSpan span) { var service = document.GetRequiredLanguageService(); - var options = ClassificationOptions.From(document.Project); using var _ = ArrayBuilder.GetInstance(out var result); - await service.AddSemanticClassificationsAsync(document, span, options, result, CancellationToken.None); + await service.AddSemanticClassificationsAsync(document, span, ClassificationOptions.Default, result, CancellationToken.None); return result.ToImmutable(); } diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index 016306b63d84c..ccd55129ca133 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -93,7 +93,7 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(InsertSlashSlashAtTheStartOfNewLinesWhenWritingSingleLineComments, SplitStringLiteralOptions.Enabled, LanguageNames.CSharp); BindToOption(InsertAsteriskAtTheStartOfNewLinesWhenWritingBlockComments, FeatureOnOffOptions.AutoInsertBlockCommentStartString, LanguageNames.CSharp); - BindToOption(ShowRemarksInQuickInfo, QuickInfoOptions.Metadata.ShowRemarksInQuickInfo, LanguageNames.CSharp); + BindToOption(ShowRemarksInQuickInfo, QuickInfoOptionsStorage.ShowRemarksInQuickInfo, LanguageNames.CSharp); BindToOption(DisplayLineSeparators, FeatureOnOffOptions.LineSeparator, LanguageNames.CSharp); // Quick Actions @@ -108,7 +108,7 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(EnableHighlightKeywords, FeatureOnOffOptions.KeywordHighlighting, LanguageNames.CSharp); BindToOption(RenameTrackingPreview, FeatureOnOffOptions.RenameTrackingPreview, LanguageNames.CSharp); - BindToOption(Underline_reassigned_variables, ClassificationOptions.Metadata.ClassifyReassignedVariables, LanguageNames.CSharp); + BindToOption(Underline_reassigned_variables, ClassificationOptionsStorage.ClassifyReassignedVariables, LanguageNames.CSharp); BindToOption(Enable_all_features_in_opened_files_from_source_generators, SourceGeneratedFileManager.Options.EnableOpeningInWorkspace, () => { // If the option has not been set by the user, check if the option is enabled from experimentation. @@ -126,12 +126,12 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.CSharp); - BindToOption(Colorize_regular_expressions, ClassificationOptions.Metadata.ColorizeRegexPatterns, LanguageNames.CSharp); + BindToOption(Colorize_regular_expressions, ClassificationOptionsStorage.ColorizeRegexPatterns, LanguageNames.CSharp); BindToOption(Report_invalid_regular_expressions, RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp); BindToOption(Highlight_related_regular_expression_components_under_cursor, RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, LanguageNames.CSharp); BindToOption(Show_completion_list, CompletionOptionsStorage.ProvideRegexCompletions, LanguageNames.CSharp); - BindToOption(Colorize_JSON_strings, ClassificationOptions.Metadata.ColorizeJsonPatterns, LanguageNames.CSharp); + BindToOption(Colorize_JSON_strings, ClassificationOptionsStorage.ColorizeJsonPatterns, LanguageNames.CSharp); BindToOption(Report_invalid_JSON_strings, JsonFeatureOptions.ReportInvalidJsonPatterns, LanguageNames.CSharp); BindToOption(Highlight_related_JSON_components_under_cursor, JsonFeatureOptions.HighlightRelatedJsonComponentsUnderCursor, LanguageNames.CSharp); @@ -140,19 +140,19 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(DisplayAllHintsWhilePressingAltF1, InlineHintsViewOptions.DisplayAllHintsWhilePressingAltF1); BindToOption(ColorHints, InlineHintsViewOptions.ColorHints, LanguageNames.CSharp); - BindToOption(DisplayInlineParameterNameHints, InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.CSharp); - BindToOption(ShowHintsForLiterals, InlineParameterHintsOptions.Metadata.ForLiteralParameters, LanguageNames.CSharp); - BindToOption(ShowHintsForNewExpressions, InlineParameterHintsOptions.Metadata.ForObjectCreationParameters, LanguageNames.CSharp); - BindToOption(ShowHintsForEverythingElse, InlineParameterHintsOptions.Metadata.ForOtherParameters, LanguageNames.CSharp); - BindToOption(ShowHintsForIndexers, InlineParameterHintsOptions.Metadata.ForIndexerParameters, LanguageNames.CSharp); - BindToOption(SuppressHintsWhenParameterNameMatchesTheMethodsIntent, InlineParameterHintsOptions.Metadata.SuppressForParametersThatMatchMethodIntent, LanguageNames.CSharp); - BindToOption(SuppressHintsWhenParameterNamesDifferOnlyBySuffix, InlineParameterHintsOptions.Metadata.SuppressForParametersThatDifferOnlyBySuffix, LanguageNames.CSharp); - BindToOption(SuppressHintsWhenParameterNamesMatchArgumentNames, InlineParameterHintsOptions.Metadata.SuppressForParametersThatMatchArgumentName, LanguageNames.CSharp); + BindToOption(DisplayInlineParameterNameHints, InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.CSharp); + BindToOption(ShowHintsForLiterals, InlineHintsOptionsStorage.ForLiteralParameters, LanguageNames.CSharp); + BindToOption(ShowHintsForNewExpressions, InlineHintsOptionsStorage.ForObjectCreationParameters, LanguageNames.CSharp); + BindToOption(ShowHintsForEverythingElse, InlineHintsOptionsStorage.ForOtherParameters, LanguageNames.CSharp); + BindToOption(ShowHintsForIndexers, InlineHintsOptionsStorage.ForIndexerParameters, LanguageNames.CSharp); + BindToOption(SuppressHintsWhenParameterNameMatchesTheMethodsIntent, InlineHintsOptionsStorage.SuppressForParametersThatMatchMethodIntent, LanguageNames.CSharp); + BindToOption(SuppressHintsWhenParameterNamesDifferOnlyBySuffix, InlineHintsOptionsStorage.SuppressForParametersThatDifferOnlyBySuffix, LanguageNames.CSharp); + BindToOption(SuppressHintsWhenParameterNamesMatchArgumentNames, InlineHintsOptionsStorage.SuppressForParametersThatMatchArgumentName, LanguageNames.CSharp); - BindToOption(DisplayInlineTypeHints, InlineTypeHintsOptions.Metadata.EnabledForTypes, LanguageNames.CSharp); - BindToOption(ShowHintsForVariablesWithInferredTypes, InlineTypeHintsOptions.Metadata.ForImplicitVariableTypes, LanguageNames.CSharp); - BindToOption(ShowHintsForLambdaParameterTypes, InlineTypeHintsOptions.Metadata.ForLambdaParameterTypes, LanguageNames.CSharp); - BindToOption(ShowHintsForImplicitObjectCreation, InlineTypeHintsOptions.Metadata.ForImplicitObjectCreation, LanguageNames.CSharp); + BindToOption(DisplayInlineTypeHints, InlineHintsOptionsStorage.EnabledForTypes, LanguageNames.CSharp); + BindToOption(ShowHintsForVariablesWithInferredTypes, InlineHintsOptionsStorage.ForImplicitVariableTypes, LanguageNames.CSharp); + BindToOption(ShowHintsForLambdaParameterTypes, InlineHintsOptionsStorage.ForLambdaParameterTypes, LanguageNames.CSharp); + BindToOption(ShowHintsForImplicitObjectCreation, InlineHintsOptionsStorage.ForImplicitObjectCreation, LanguageNames.CSharp); // Leave the null converter here to make sure if the option value is get from the storage (if it is null), the feature will be enabled BindToOption(ShowInheritanceMargin, FeatureOnOffOptions.ShowInheritanceMargin, LanguageNames.CSharp, () => true); @@ -232,7 +232,7 @@ private void Enable_pull_diagnostics_experimental_requires_restart_Indeterminate private void UpdateInlineHintsOptions() { - var enabledForParameters = this.OptionStore.GetOption(InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.CSharp); + var enabledForParameters = this.OptionStore.GetOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.CSharp); ShowHintsForLiterals.IsEnabled = enabledForParameters; ShowHintsForNewExpressions.IsEnabled = enabledForParameters; ShowHintsForEverythingElse.IsEnabled = enabledForParameters; @@ -241,7 +241,7 @@ private void UpdateInlineHintsOptions() SuppressHintsWhenParameterNamesDifferOnlyBySuffix.IsEnabled = enabledForParameters; SuppressHintsWhenParameterNamesMatchArgumentNames.IsEnabled = enabledForParameters; - var enabledForTypes = this.OptionStore.GetOption(InlineTypeHintsOptions.Metadata.EnabledForTypes, LanguageNames.CSharp); + var enabledForTypes = this.OptionStore.GetOption(InlineHintsOptionsStorage.EnabledForTypes, LanguageNames.CSharp); ShowHintsForVariablesWithInferredTypes.IsEnabled = enabledForTypes; ShowHintsForLambdaParameterTypes.IsEnabled = enabledForTypes; ShowHintsForImplicitObjectCreation.IsEnabled = enabledForTypes; @@ -249,25 +249,25 @@ private void UpdateInlineHintsOptions() private void DisplayInlineParameterNameHints_Checked(object sender, RoutedEventArgs e) { - this.OptionStore.SetOption(InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.CSharp, true); + this.OptionStore.SetOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.CSharp, true); UpdateInlineHintsOptions(); } private void DisplayInlineParameterNameHints_Unchecked(object sender, RoutedEventArgs e) { - this.OptionStore.SetOption(InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.CSharp, false); + this.OptionStore.SetOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.CSharp, false); UpdateInlineHintsOptions(); } private void DisplayInlineTypeHints_Checked(object sender, RoutedEventArgs e) { - this.OptionStore.SetOption(InlineTypeHintsOptions.Metadata.EnabledForTypes, LanguageNames.CSharp, true); + this.OptionStore.SetOption(InlineHintsOptionsStorage.EnabledForTypes, LanguageNames.CSharp, true); UpdateInlineHintsOptions(); } private void DisplayInlineTypeHints_Unchecked(object sender, RoutedEventArgs e) { - this.OptionStore.SetOption(InlineTypeHintsOptions.Metadata.EnabledForTypes, LanguageNames.CSharp, false); + this.OptionStore.SetOption(InlineHintsOptionsStorage.EnabledForTypes, LanguageNames.CSharp, false); UpdateInlineHintsOptions(); } } diff --git a/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs b/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs index d28f640f45551..61b9851efeb38 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs @@ -9,10 +9,12 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CodeLens; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; @@ -23,10 +25,13 @@ namespace Microsoft.VisualStudio.LanguageServices.CodeLens [ExportWorkspaceService(typeof(ICodeLensReferencesService), layer: ServiceLayer.Host), Shared] internal sealed class RemoteCodeLensReferencesService : ICodeLensReferencesService { + private readonly IGlobalOptionService _globalOptions; + [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public RemoteCodeLensReferencesService() + public RemoteCodeLensReferencesService(IGlobalOptionService globalOptions) { + _globalOptions = globalOptions; } public ValueTask GetProjectCodeLensVersionAsync(Solution solution, ProjectId projectId, CancellationToken cancellationToken) @@ -131,7 +136,7 @@ public ValueTask GetProjectCodeLensVersionAsync(Solution solution, } } - private static async Task> FixUpDescriptorsAsync( + private async Task> FixUpDescriptorsAsync( Solution solution, ImmutableArray descriptors, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var list); @@ -175,8 +180,9 @@ private static async Task> FixUpDesc continue; } - var referenceExcerpt = await excerpter.TryExcerptAsync(document, span, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false); - var tooltipExcerpt = await excerpter.TryExcerptAsync(document, span, ExcerptMode.Tooltip, cancellationToken).ConfigureAwait(false); + var classificationOptions = _globalOptions.GetClassificationOptions(document.Project.Language); + var referenceExcerpt = await excerpter.TryExcerptAsync(document, span, ExcerptMode.SingleLine, classificationOptions, cancellationToken).ConfigureAwait(false); + var tooltipExcerpt = await excerpter.TryExcerptAsync(document, span, ExcerptMode.Tooltip, classificationOptions, cancellationToken).ConfigureAwait(false); var (text, start, length) = GetReferenceInfo(referenceExcerpt, descriptor); var (before1, before2, after1, after2) = GetReferenceTexts(referenceExcerpt, tooltipExcerpt, descriptor); diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs index 0accad77828d9..05a7bc6ef1f08 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Contexts/AbstractTableDataSourceFindUsagesContext.cs @@ -352,7 +352,7 @@ public sealed override ValueTask OnDefinitionFoundAsync(DefinitionItem definitio CancellationToken cancellationToken) { var document = documentSpan.Document; - var options = ClassificationOptions.From(document.Project); + var options = _globalOptions.GetClassificationOptions(document.Project.Language); var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var (excerptResult, lineText) = await ExcerptAsync(sourceText, documentSpan, options, cancellationToken).ConfigureAwait(false); @@ -387,7 +387,7 @@ public sealed override ValueTask OnDefinitionFoundAsync(DefinitionItem definitio var excerptService = documentSpan.Document.Services.GetService(); if (excerptService != null) { - var result = await excerptService.TryExcerptAsync(documentSpan.Document, documentSpan.SourceSpan, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false); + var result = await excerptService.TryExcerptAsync(documentSpan.Document, documentSpan.SourceSpan, ExcerptMode.SingleLine, options, cancellationToken).ConfigureAwait(false); if (result != null) { return (result.Value, AbstractDocumentSpanEntry.GetLineContainingPosition(result.Value.Content, result.Value.MappedSpan.Start)); diff --git a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs index 9e13aa45320f5..a4ee794ebe11b 100644 --- a/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Def/Implementation/FindReferences/Entries/DocumentSpanEntry.cs @@ -226,7 +226,8 @@ private DisposableToolTip CreateDisposableToolTip(Document document, TextSpan so var excerptService = document.Services.GetService(); if (excerptService != null) { - var excerpt = Presenter.ThreadingContext.JoinableTaskFactory.Run(() => excerptService.TryExcerptAsync(document, sourceSpan, ExcerptMode.Tooltip, CancellationToken.None)); + var classificationOptions = Presenter._globalOptions.GetClassificationOptions(document.Project.Language); + var excerpt = Presenter.ThreadingContext.JoinableTaskFactory.Run(() => excerptService.TryExcerptAsync(document, sourceSpan, ExcerptMode.Tooltip, classificationOptions, CancellationToken.None)); if (excerpt != null) { // get tooltip from excerpt service diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.DocumentServiceProvider.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.DocumentServiceProvider.cs index ec2af8bb6c4b3..63baee6048d2b 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.DocumentServiceProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.DocumentServiceProvider.cs @@ -126,14 +126,14 @@ public override async Task> MapSpansAsync( } } - private class DocumentExcerpter : IDocumentExcerptService + private sealed class DocumentExcerpter : IDocumentExcerptService { private readonly ITextBuffer _primaryBuffer; public DocumentExcerpter(ITextBuffer primaryBuffer) => _primaryBuffer = primaryBuffer; - public async Task TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, CancellationToken cancellationToken) + public async Task TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, ClassificationOptions classificationOptions, CancellationToken cancellationToken) { // REVIEW: for now, we keep document here due to open file case, otherwise, we need to create new DocumentExcerpter for every char user types. var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -165,8 +165,7 @@ public DocumentExcerpter(ITextBuffer primaryBuffer) return null; } - var options = ClassificationOptions.From(document.Project); - var classifiedSpansOnContent = await GetClassifiedSpansOnContentAsync(document, roslynSnapshot, contentSpanOnPrimarySnapshot.Value, options, cancellationToken).ConfigureAwait(false); + var classifiedSpansOnContent = await GetClassifiedSpansOnContentAsync(document, roslynSnapshot, contentSpanOnPrimarySnapshot.Value, classificationOptions, cancellationToken).ConfigureAwait(false); // the default implementation has no idea how to classify the primary snapshot return new ExcerptResult(content, spanOnContent, classifiedSpansOnContent, document, span); diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs index 25ee249b8db8e..5c9941cab3d4f 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; @@ -66,28 +67,6 @@ internal partial class ContainedLanguage // flickering. private readonly ITagAggregator _bufferTagAggregator; - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] - internal ContainedLanguage( - IVsTextBufferCoordinator bufferCoordinator, - IComponentModel componentModel, - VisualStudioProject? project, - IVsHierarchy hierarchy, - uint itemid, - VisualStudioProjectTracker? projectTrackerOpt, - ProjectId projectId, - Guid languageServiceGuid, - AbstractFormattingRule? vbHelperFormattingRule = null) - : this(bufferCoordinator, - componentModel, - projectTrackerOpt?.Workspace ?? componentModel.GetService(), - projectId, - project, - GetFilePathFromHierarchyAndItemId(hierarchy, itemid), - languageServiceGuid, - vbHelperFormattingRule) - { - } - public static string GetFilePathFromHierarchyAndItemId(IVsHierarchy hierarchy, uint itemid) { if (!ErrorHandler.Succeeded(((IVsProject)hierarchy).GetMkDocument(itemid, out var filePath))) @@ -114,12 +93,12 @@ internal ContainedLanguage( Guid languageServiceGuid, AbstractFormattingRule? vbHelperFormattingRule = null) { - this.BufferCoordinator = bufferCoordinator; - this.ComponentModel = componentModel; - this.Project = project; + BufferCoordinator = bufferCoordinator; + ComponentModel = componentModel; + Project = project; _languageServiceGuid = languageServiceGuid; - this.Workspace = workspace; + Workspace = workspace; _editorAdaptersFactoryService = componentModel.GetService(); _diagnosticAnalyzerService = componentModel.GetService(); diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage`2.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage`2.cs deleted file mode 100644 index 968bd47b00949..0000000000000 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguage`2.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.VisualStudio.ComponentModelHost; -using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.Venus -{ - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] - internal partial class ContainedLanguage : ContainedLanguage - where TPackage : AbstractPackage - where TLanguageService : AbstractLanguageService - { - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] - public ContainedLanguage( - IVsTextBufferCoordinator bufferCoordinator, - IComponentModel componentModel, - AbstractProject project, - IVsHierarchy hierarchy, - uint itemid, - TLanguageService languageService, - SourceCodeKind sourceCodeKind, - IFormattingRule vbHelperFormattingRule, - Workspace workspace) - : base(bufferCoordinator, - componentModel, - project.VisualStudioProject, - hierarchy, - itemid, - project.ProjectTracker, - project.Id, - languageService.LanguageServiceId, - vbHelperFormattingRule: null) - { - Contract.ThrowIfTrue(vbHelperFormattingRule != null); - } - - [Obsolete("This is a compatibility shim for TypeScript; please do not use it.")] - public ContainedLanguage( - IVsTextBufferCoordinator bufferCoordinator, - IComponentModel componentModel, - AbstractProject project, - IVsHierarchy hierarchy, - uint itemid, - TLanguageService languageService, - SourceCodeKind sourceCodeKind, - IFormattingRule vbHelperFormattingRule) - : base(bufferCoordinator, - componentModel, - project.VisualStudioProject, - hierarchy, - itemid, - projectTrackerOpt: null, - project.VisualStudioProject.Id, - languageService.LanguageServiceId, - vbHelperFormattingRule: null) - { - Contract.ThrowIfTrue(vbHelperFormattingRule != null); - } - } -} diff --git a/src/VisualStudio/Core/Def/Telemetry/SyntaxTreeConfigurationOptions.cs b/src/VisualStudio/Core/Def/Telemetry/SyntaxTreeConfigurationOptions.cs deleted file mode 100644 index 8fbc9f8ad1437..0000000000000 --- a/src/VisualStudio/Core/Def/Telemetry/SyntaxTreeConfigurationOptions.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; - -namespace Microsoft.CodeAnalysis -{ - [ExportSolutionOptionProvider, Shared] - internal class WorkspaceConfigurationOptions : IOptionProvider - { - /// - /// Disables if the workspace creates recoverable trees when from its s. - /// - public static readonly Option2 DisableRecoverableTrees = new( - nameof(WorkspaceConfigurationOptions), nameof(DisableRecoverableTrees), defaultValue: false, - new FeatureFlagStorageLocation("Roslyn.DisableRecoverableTrees")); - - public static readonly Option2 DisableProjectCacheService = new( - nameof(WorkspaceConfigurationOptions), nameof(DisableProjectCacheService), defaultValue: false, - new FeatureFlagStorageLocation("Roslyn.DisableProjectCacheService")); - - ImmutableArray IOptionProvider.Options { get; } = ImmutableArray.Create( - DisableRecoverableTrees, - DisableProjectCacheService); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public WorkspaceConfigurationOptions() - { - } - } -} diff --git a/src/VisualStudio/Core/Def/Telemetry/VisualStudioSyntaxTreeConfigurationService.cs b/src/VisualStudio/Core/Def/Telemetry/VisualStudioSyntaxTreeConfigurationService.cs index 31e10e705f76a..603f0002cff4a 100644 --- a/src/VisualStudio/Core/Def/Telemetry/VisualStudioSyntaxTreeConfigurationService.cs +++ b/src/VisualStudio/Core/Def/Telemetry/VisualStudioSyntaxTreeConfigurationService.cs @@ -3,13 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Composition; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; namespace Microsoft.VisualStudio.LanguageServices.Telemetry { @@ -31,29 +29,18 @@ public bool DisableRecoverableTrees public bool DisableProjectCacheService => _globalOptions.GetOption(OptionsMetadata.DisableProjectCacheService); - [ExportSolutionOptionProvider, Shared] - internal sealed class OptionsMetadata : IOptionProvider + internal sealed class OptionsMetadata { /// /// Disables if the workspace creates recoverable trees when from its s. /// public static readonly Option2 DisableRecoverableTrees = new( - nameof(WorkspaceConfigurationOptions), nameof(DisableRecoverableTrees), defaultValue: false, + "WorkspaceConfigurationOptions", "DisableRecoverableTrees", defaultValue: false, new FeatureFlagStorageLocation("Roslyn.DisableRecoverableTrees")); public static readonly Option2 DisableProjectCacheService = new( - nameof(WorkspaceConfigurationOptions), nameof(DisableProjectCacheService), defaultValue: false, + "WorkspaceConfigurationOptions", "DisableProjectCacheService", defaultValue: false, new FeatureFlagStorageLocation("Roslyn.DisableProjectCacheService")); - - ImmutableArray IOptionProvider.Options { get; } = ImmutableArray.Create( - DisableRecoverableTrees, - DisableProjectCacheService); - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public OptionsMetadata() - { - } } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index f9a04d1304437..945ae9e206746 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.ValueTracking; using Microsoft.VisualStudio.Language.Intellisense; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Shell; using Microsoft.CodeAnalysis.Classification; @@ -25,6 +24,7 @@ internal sealed class ValueTrackedTreeItemViewModel : TreeItemViewModel private readonly IGlyphService _glyphService; private readonly IValueTrackingService _valueTrackingService; private readonly ValueTrackedItem _trackedItem; + private readonly IGlobalOptionService _globalOptions; public override bool IsNodeExpanded { @@ -43,6 +43,7 @@ private ValueTrackedTreeItemViewModel( ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IValueTrackingService valueTrackingService, + IGlobalOptionService globalOptions, IThreadingContext threadingContext, string fileName, ImmutableArray children) @@ -64,6 +65,7 @@ private ValueTrackedTreeItemViewModel( _solution = solution; _glyphService = glyphService; _valueTrackingService = valueTrackingService; + _globalOptions = globalOptions; if (children.IsEmpty) { @@ -84,13 +86,14 @@ internal static async ValueTask CreateAsync( ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IValueTrackingService valueTrackingService, + IGlobalOptionService globalOptions, IThreadingContext threadingContext, CancellationToken cancellationToken) { var document = solution.GetRequiredDocument(item.DocumentId); var fileName = document.FilePath ?? document.Name; - var options = ClassificationOptions.From(document.Project); + var options = globalOptions.GetClassificationOptions(document.Project.Language); var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, item.Span, options, cancellationToken).ConfigureAwait(false); var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, options, cancellationToken).ConfigureAwait(false); var classifiedSpans = classificationResult.ClassifiedSpans; @@ -102,6 +105,7 @@ internal static async ValueTask CreateAsync( treeViewModel, glyphService, valueTrackingService, + globalOptions, threadingContext, fileName, children: ImmutableArray.Empty); @@ -169,7 +173,7 @@ private async Task> CalculateChildrenAsync(Can cancellationToken).ConfigureAwait(false); return await valueTrackedItems.SelectAsArrayAsync((item, cancellationToken) => - CreateAsync(_solution, item, TreeViewModel, _glyphService, _valueTrackingService, ThreadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); + CreateAsync(_solution, item, TreeViewModel, _glyphService, _valueTrackingService, _globalOptions, ThreadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index e77986635bc75..735d18a399119 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.ValueTracking; @@ -41,6 +42,7 @@ internal class ValueTrackingCommandHandler : ICommandHandler "Go to value tracking"; @@ -154,11 +158,11 @@ private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbo var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); var childViewModels = await childItems.SelectAsArrayAsync((item, cancellationToken) => - ValueTrackedTreeItemViewModel.CreateAsync(solution, item, toolWindow.ViewModel, _glyphService, valueTrackingService, _threadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); + ValueTrackedTreeItemViewModel.CreateAsync(solution, item, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); RoslynDebug.AssertNotNull(location.SourceTree); var document = solution.GetRequiredDocument(location.SourceTree); - var options = ClassificationOptions.From(document.Project); + var options = _globalOptions.GetClassificationOptions(document.Project.Language); var sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, options, cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb index dcb7bc67b4e56..b33a5fcb23484 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentServiceTests.vb @@ -4,6 +4,7 @@ Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Classification Imports Microsoft.CodeAnalysis.Editor.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests.Classification Imports Microsoft.CodeAnalysis.Editor.UnitTests.Classification.FormattedClassifications @@ -115,7 +116,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus Dim service = New ContainedDocument.DocumentServiceProvider(projectedDocument.GetTextBuffer()) Dim excerptService = service.GetService(Of IDocumentExcerptService) - Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.SingleLine, CancellationToken.None) + Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.SingleLine, ClassificationOptions.Default, CancellationToken.None) Assert.True(result.HasValue) Dim content = result.Value.Content.ToString() @@ -154,7 +155,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus Dim excerptService = service.GetService(Of IDocumentExcerptService) ' make sure single line buffer doesn't throw on ExcerptMode.Tooltip - Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.Tooltip, CancellationToken.None) + Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.Tooltip, ClassificationOptions.Default, CancellationToken.None) Assert.True(result.HasValue) Dim content = result.Value.Content.ToString() @@ -201,7 +202,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus Dim service = New ContainedDocument.DocumentServiceProvider(projectedDocument.GetTextBuffer()) Dim excerptService = service.GetService(Of IDocumentExcerptService) - Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.Tooltip, CancellationToken.None) + Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.Tooltip, ClassificationOptions.Default, CancellationToken.None) Assert.True(result.HasValue) Dim content = result.Value.Content.ToString() @@ -254,7 +255,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Venus Dim service = New ContainedDocument.DocumentServiceProvider(projectedDocument.GetTextBuffer()) Dim excerptService = service.GetService(Of IDocumentExcerptService) - Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.SingleLine, CancellationToken.None) + Dim result = Await excerptService.TryExcerptAsync(workspace.CurrentSolution.GetDocument(subjectDocument.Id), GetNamedSpan(subjectDocument), ExcerptMode.SingleLine, ClassificationOptions.Default, CancellationToken.None) Assert.True(result.HasValue) Dim content = result.Value.Content.ToString() diff --git a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb index a018f172cbfe7..39787dd58804c 100644 --- a/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb +++ b/src/VisualStudio/Core/Test/Venus/DocumentService_IntegrationTests.vb @@ -134,7 +134,7 @@ class {|Definition:C1|} Using workspace = TestWorkspace.Create(input, documentServiceProvider:=TestDocumentServiceProvider.Instance) - Dim codelensService = New RemoteCodeLensReferencesService() + Dim codelensService = New RemoteCodeLensReferencesService(workspace.GlobalOptions) Dim originalDocument = workspace.Documents.First(Function(d) d.AnnotatedSpans.ContainsKey("Original")) @@ -304,7 +304,7 @@ class { } Public Shared ReadOnly Instance As Excerpter = New Excerpter() - Public Async Function TryExcerptAsync(document As Document, span As TextSpan, mode As ExcerptMode, cancellationToken As CancellationToken) As Task(Of ExcerptResult?) Implements IDocumentExcerptService.TryExcerptAsync + Public Async Function TryExcerptAsync(document As Document, span As TextSpan, mode As ExcerptMode, classificationOptions As ClassificationOptions, cancellationToken As CancellationToken) As Task(Of ExcerptResult?) Implements IDocumentExcerptService.TryExcerptAsync Dim testWorkspace = DirectCast(document.Project.Solution.Workspace, TestWorkspace) Dim testDocument = testWorkspace.GetTestDocument(document.Id) diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index 3aa576ed24e8f..5e96a8b468072 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -107,9 +107,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(EnableLineCommit, FeatureOnOffOptions.PrettyListing, LanguageNames.VisualBasic) BindToOption(AutomaticInsertionOfInterfaceAndMustOverrideMembers, FeatureOnOffOptions.AutomaticInsertionOfAbstractOrInterfaceMembers, LanguageNames.VisualBasic) BindToOption(RenameTrackingPreview, FeatureOnOffOptions.RenameTrackingPreview, LanguageNames.VisualBasic) - BindToOption(ShowRemarksInQuickInfo, QuickInfoOptions.Metadata.ShowRemarksInQuickInfo, LanguageNames.VisualBasic) + BindToOption(ShowRemarksInQuickInfo, QuickInfoOptionsStorage.ShowRemarksInQuickInfo, LanguageNames.VisualBasic) BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.VisualBasic) - BindToOption(Underline_reassigned_variables, ClassificationOptions.Metadata.ClassifyReassignedVariables, LanguageNames.VisualBasic) + BindToOption(Underline_reassigned_variables, ClassificationOptionsStorage.ClassifyReassignedVariables, LanguageNames.VisualBasic) ' Go To Definition BindToOption(NavigateToObjectBrowser, VisualStudioNavigationOptions.NavigateToObjectBrowser, LanguageNames.VisualBasic) @@ -120,12 +120,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options End Function) ' Regular expressions - BindToOption(Colorize_regular_expressions, ClassificationOptions.Metadata.ColorizeRegexPatterns, LanguageNames.VisualBasic) + BindToOption(Colorize_regular_expressions, ClassificationOptionsStorage.ColorizeRegexPatterns, LanguageNames.VisualBasic) BindToOption(Report_invalid_regular_expressions, RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic) BindToOption(Highlight_related_regular_expression_components_under_cursor, RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, LanguageNames.VisualBasic) BindToOption(Show_completion_list, CompletionOptionsStorage.ProvideRegexCompletions, LanguageNames.VisualBasic) - BindToOption(Colorize_JSON_strings, ClassificationOptions.Metadata.ColorizeJsonPatterns, LanguageNames.VisualBasic) + BindToOption(Colorize_JSON_strings, ClassificationOptionsStorage.ColorizeJsonPatterns, LanguageNames.VisualBasic) BindToOption(Report_invalid_JSON_strings, JsonFeatureOptions.ReportInvalidJsonPatterns, LanguageNames.VisualBasic) BindToOption(Highlight_related_JSON_components_under_cursor, JsonFeatureOptions.HighlightRelatedJsonComponentsUnderCursor, LanguageNames.VisualBasic) @@ -146,14 +146,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(DisplayAllHintsWhilePressingAltF1, InlineHintsViewOptions.DisplayAllHintsWhilePressingAltF1) BindToOption(ColorHints, InlineHintsViewOptions.ColorHints, LanguageNames.VisualBasic) - BindToOption(DisplayInlineParameterNameHints, InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.VisualBasic) - BindToOption(ShowHintsForLiterals, InlineParameterHintsOptions.Metadata.ForLiteralParameters, LanguageNames.VisualBasic) - BindToOption(ShowHintsForNewExpressions, InlineParameterHintsOptions.Metadata.ForObjectCreationParameters, LanguageNames.VisualBasic) - BindToOption(ShowHintsForEverythingElse, InlineParameterHintsOptions.Metadata.ForOtherParameters, LanguageNames.VisualBasic) - BindToOption(ShowHintsForIndexers, InlineParameterHintsOptions.Metadata.ForIndexerParameters, LanguageNames.VisualBasic) - BindToOption(SuppressHintsWhenParameterNameMatchesTheMethodsIntent, InlineParameterHintsOptions.Metadata.SuppressForParametersThatMatchMethodIntent, LanguageNames.VisualBasic) - BindToOption(SuppressHintsWhenParameterNamesDifferOnlyBySuffix, InlineParameterHintsOptions.Metadata.SuppressForParametersThatDifferOnlyBySuffix, LanguageNames.VisualBasic) - BindToOption(SuppressHintsWhenParameterNamesMatchArgumentNames, InlineParameterHintsOptions.Metadata.SuppressForParametersThatMatchArgumentName, LanguageNames.VisualBasic) + BindToOption(DisplayInlineParameterNameHints, InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.VisualBasic) + BindToOption(ShowHintsForLiterals, InlineHintsOptionsStorage.ForLiteralParameters, LanguageNames.VisualBasic) + BindToOption(ShowHintsForNewExpressions, InlineHintsOptionsStorage.ForObjectCreationParameters, LanguageNames.VisualBasic) + BindToOption(ShowHintsForEverythingElse, InlineHintsOptionsStorage.ForOtherParameters, LanguageNames.VisualBasic) + BindToOption(ShowHintsForIndexers, InlineHintsOptionsStorage.ForIndexerParameters, LanguageNames.VisualBasic) + BindToOption(SuppressHintsWhenParameterNameMatchesTheMethodsIntent, InlineHintsOptionsStorage.SuppressForParametersThatMatchMethodIntent, LanguageNames.VisualBasic) + BindToOption(SuppressHintsWhenParameterNamesDifferOnlyBySuffix, InlineHintsOptionsStorage.SuppressForParametersThatDifferOnlyBySuffix, LanguageNames.VisualBasic) + BindToOption(SuppressHintsWhenParameterNamesMatchArgumentNames, InlineHintsOptionsStorage.SuppressForParametersThatMatchArgumentName, LanguageNames.VisualBasic) BindToOption(ShowInheritanceMargin, FeatureOnOffOptions.ShowInheritanceMargin, LanguageNames.VisualBasic, Function() @@ -179,7 +179,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options End Sub Private Sub UpdateInlineHintsOptions() - Dim enabledForParameters = Me.OptionStore.GetOption(InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.VisualBasic) <> False + Dim enabledForParameters = Me.OptionStore.GetOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.VisualBasic) <> False ShowHintsForLiterals.IsEnabled = enabledForParameters ShowHintsForNewExpressions.IsEnabled = enabledForParameters ShowHintsForEverythingElse.IsEnabled = enabledForParameters @@ -190,12 +190,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options End Sub Private Sub DisplayInlineParameterNameHints_Checked() - Me.OptionStore.SetOption(InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.VisualBasic, True) + Me.OptionStore.SetOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.VisualBasic, True) UpdateInlineHintsOptions() End Sub Private Sub DisplayInlineParameterNameHints_Unchecked() - Me.OptionStore.SetOption(InlineParameterHintsOptions.Metadata.EnabledForParameters, LanguageNames.VisualBasic, False) + Me.OptionStore.SetOption(InlineHintsOptionsStorage.EnabledForParameters, LanguageNames.VisualBasic, False) UpdateInlineHintsOptions() End Sub End Class diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Extensions/SymbolExtensions.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Extensions/SymbolExtensions.cs index 4319c7d558b2d..e2f9e178be106 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Extensions/SymbolExtensions.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Extensions/SymbolExtensions.cs @@ -21,7 +21,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageSe { internal static class SymbolExtensions { - public static async Task> GetDescriptionAsync(this ISymbol symbol, TextDocument document, CancellationToken cancellationToken) + public static async Task> GetDescriptionAsync(this ISymbol symbol, TextDocument document, SymbolDescriptionOptions options, CancellationToken cancellationToken) { if (symbol == null) { @@ -55,7 +55,6 @@ public static async Task> GetDescriptionAsync(this ISymb } var services = codeProject.Solution.Workspace.Services; - var options = SymbolDescriptionOptions.From(document.Project); var quickInfo = await QuickInfoUtilities.CreateQuickInfoItemAsync(services, semanticModel, span: default, ImmutableArray.Create(symbol), options, cancellationToken).ConfigureAwait(false); var builder = new List(); foreach (var section in quickInfo.Sections) diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs index 0714f5e8ad27a..4bbf661b4a744 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs @@ -10,6 +10,8 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Xaml; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -29,6 +31,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler [ProvidesMethod(LSP.Methods.TextDocumentCompletionResolveName)] internal class CompletionResolveHandler : AbstractStatelessRequestHandler { + private readonly IGlobalOptionService _globalOptions; + public override string Method => LSP.Methods.TextDocumentCompletionResolveName; public override bool MutatesSolutionState => false; @@ -36,8 +40,9 @@ internal class CompletionResolveHandler : AbstractStatelessRequestHandler null; @@ -77,7 +82,8 @@ public CompletionResolveHandler() return completionItem; } - var description = await symbol.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); + var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); + var description = await symbol.GetDescriptionAsync(document, options, cancellationToken).ConfigureAwait(false); vsCompletionItem.Description = new ClassifiedTextElement(description.Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); return vsCompletionItem; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs index c4596601b081a..23c0be4fd7e7d 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs @@ -11,8 +11,10 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Xaml; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.QuickInfo; @@ -23,12 +25,15 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] [ProvidesMethod(Methods.TextDocumentHoverName)] - internal class HoverHandler : AbstractStatelessRequestHandler + internal sealed class HoverHandler : AbstractStatelessRequestHandler { + private readonly IGlobalOptionService _globalOptions; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public HoverHandler() + public HoverHandler(IGlobalOptionService globalOptions) { + _globalOptions = globalOptions; } public override string Method => Methods.TextDocumentHoverName; @@ -63,7 +68,8 @@ public HoverHandler() var descriptionBuilder = new List(info.Description); if (info.Symbol != null) { - var description = await info.Symbol.GetDescriptionAsync(document, cancellationToken).ConfigureAwait(false); + var options = _globalOptions.GetSymbolDescriptionOptions(document.Project.Language); + var description = await info.Symbol.GetDescriptionAsync(document, options, cancellationToken).ConfigureAwait(false); if (description.Any()) { if (descriptionBuilder.Any()) diff --git a/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs b/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs index 1c5ab2818eb69..47b4b8acad570 100644 --- a/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs +++ b/src/Workspaces/Core/Portable/Classification/ClassificationOptions.cs @@ -2,64 +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. -using System; -using System.Collections.Immutable; -using System.Composition; using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; namespace Microsoft.CodeAnalysis.Classification { [DataContract] internal readonly record struct ClassificationOptions( - [property: DataMember(Order = 0)] bool ClassifyReassignedVariables, - [property: DataMember(Order = 1)] bool ColorizeRegexPatterns, - [property: DataMember(Order = 2)] bool ColorizeJsonPatterns) + [property: DataMember(Order = 0)] bool ClassifyReassignedVariables = false, + [property: DataMember(Order = 1)] bool ColorizeRegexPatterns = true, + [property: DataMember(Order = 2)] bool ColorizeJsonPatterns = true) { - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider + public ClassificationOptions() + : this(ClassifyReassignedVariables: false) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - ClassifyReassignedVariables, - ColorizeRegexPatterns, - ColorizeJsonPatterns); - - private const string FeatureName = "ClassificationOptions"; - - public static PerLanguageOption2 ClassifyReassignedVariables = - new(FeatureName, "ClassifyReassignedVariables", defaultValue: false, - storageLocation: new RoamingProfileStorageLocation($"TextEditor.%LANGUAGE%.Specific.ClassificationOptions.ClassifyReassignedVariables")); - - public static PerLanguageOption2 ColorizeRegexPatterns = - new("RegularExpressionsOptions", "ColorizeRegexPatterns", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ColorizeRegexPatterns")); - - public static PerLanguageOption2 ColorizeJsonPatterns = - new("JsonFeatureOptions", "ColorizeJsonPatterns", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ColorizeJsonPatterns")); } - public static readonly ClassificationOptions Default - = new( - ClassifyReassignedVariables: Metadata.ClassifyReassignedVariables.DefaultValue, - ColorizeRegexPatterns: Metadata.ColorizeRegexPatterns.DefaultValue, - ColorizeJsonPatterns: Metadata.ColorizeJsonPatterns.DefaultValue); - - public static ClassificationOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static ClassificationOptions From(OptionSet options, string language) - => new( - ClassifyReassignedVariables: options.GetOption(Metadata.ClassifyReassignedVariables, language), - ColorizeRegexPatterns: options.GetOption(Metadata.ColorizeRegexPatterns, language), - ColorizeJsonPatterns: options.GetOption(Metadata.ColorizeJsonPatterns, language)); + public static readonly ClassificationOptions Default = new(); } } diff --git a/src/Workspaces/Core/Portable/Classification/Classifier.cs b/src/Workspaces/Core/Portable/Classification/Classifier.cs index 8d1bd995e6862..cf932212e2e42 100644 --- a/src/Workspaces/Core/Portable/Classification/Classifier.cs +++ b/src/Workspaces/Core/Portable/Classification/Classifier.cs @@ -26,8 +26,9 @@ public static async Task> GetClassifiedSpansAsync( CancellationToken cancellationToken = default) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var options = ClassificationOptions.From(document.Project); - return GetClassifiedSpans(document.Project.Solution.Workspace.Services, semanticModel, textSpan, options, cancellationToken); + + // public options do not affect classification: + return GetClassifiedSpans(document.Project.Solution.Workspace.Services, semanticModel, textSpan, ClassificationOptions.Default, cancellationToken); } /// @@ -44,7 +45,10 @@ public static IEnumerable GetClassifiedSpans( TextSpan textSpan, Workspace workspace, CancellationToken cancellationToken = default) - => GetClassifiedSpans(workspace.Services, semanticModel, textSpan, ClassificationOptions.From(workspace.CurrentSolution.Options, semanticModel.Language), cancellationToken); + { + // public options do not affect classification: + return GetClassifiedSpans(workspace.Services, semanticModel, textSpan, ClassificationOptions.Default, cancellationToken); + } internal static IEnumerable GetClassifiedSpans( HostWorkspaceServices workspaceServices, diff --git a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs index a3ad14eda6cbb..6b3a74af3da35 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs @@ -22,7 +22,7 @@ internal interface IDocumentExcerptService : IDocumentService /// /// the result might not be an exact copy of the given source or contains more then given span /// - Task TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, CancellationToken cancellationToken); + Task TryExcerptAsync(Document document, TextSpan span, ExcerptMode mode, ClassificationOptions classificationOptions, CancellationToken cancellationToken); } /// @@ -57,14 +57,14 @@ internal struct ExcerptResult /// /// this excerpt is from /// - /// should be same document in + /// should be same document in /// public readonly Document Document; /// /// span on this excerpt is from /// - /// should be same text span in + /// should be same text span in /// public readonly TextSpan Span; From f50621ab200f0e376a2df51d61da6e0651bb9573 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 15 Feb 2022 16:03:01 -0800 Subject: [PATCH 049/187] Colorize 'args' in top-level program --- .../Classification/TotalClassifierTests.cs | 52 +++++++++++++++++++ .../QuickInfo/SemanticQuickInfoSourceTests.cs | 35 +++++++++++++ .../Classification/AbstractClassifierTests.cs | 9 ++++ .../NameSyntaxClassifier.cs | 7 +++ 4 files changed, 103 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs index 50199eed066c3..343c641ab89bd 100644 --- a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs @@ -2238,6 +2238,58 @@ static C() { } Punctuation.CloseParen, Punctuation.OpenCurly, Punctuation.CloseCurly, +Punctuation.CloseCurly); + } + + [Theory] + [CombinatorialData] + [WorkItem(59569, "https://github.com/dotnet/roslyn/issues/59569")] + public async Task TestArgsInTopLevel(TestHost testHost) + { + await TestAsync( +@" +[|foreach (var arg in args) +{ +}|]", + testHost, + parseOptions: null, +ControlKeyword("foreach"), +Punctuation.OpenParen, +Keyword("var"), +Local("arg"), +ControlKeyword("in"), +Keyword("args"), +Punctuation.CloseParen, +Punctuation.OpenCurly, +Punctuation.CloseCurly); + } + + [Theory] + [CombinatorialData] + [WorkItem(59569, "https://github.com/dotnet/roslyn/issues/59569")] + public async Task TestArgsInNormalProgram(TestHost testHost) + { + await TestAsync( +@" +class Program +{ + static void Main(string[] args) + { + [|foreach (var arg in args) + { + }|] + } +}", + testHost, + parseOptions: null, +ControlKeyword("foreach"), +Punctuation.OpenParen, +Keyword("var"), +Local("arg"), +ControlKeyword("in"), +Parameter("args"), +Punctuation.CloseParen, +Punctuation.OpenCurly, Punctuation.CloseCurly); } } diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 046a3fe451a93..e1401aad99e31 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -8192,5 +8192,40 @@ Hello world Hello world """"""")); } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestArgsInTopLevel() + { + var markup = +@" +forach (var arg in $$args) +{ +} +"; + + await TestWithOptionsAsync( + Options.Regular, markup, + MainDescription($"({FeaturesResources.parameter}) string[] args")); + } + + [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] + public async Task TestArgsInNormalProgram() + { + var markup = +@" +class Program +{ + static void Main(string[] args) + { + foreach (var arg in $$args) + { + } + } +} +"; + + await TestAsync(markup, + MainDescription($"({FeaturesResources.parameter}) string[] args")); + } } } diff --git a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs index 1001e756a9598..ecf5be5384e69 100644 --- a/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs +++ b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs @@ -102,6 +102,15 @@ protected async Task TestAsync( await DefaultTestAsync(code, code, testHost, expected); } + protected async Task TestAsync( + string code, + TestHost testHost, + ParseOptions? parseOptions, + params FormattedClassification[] expected) + { + await TestAsync(code, code, testHost, parseOptions, expected); + } + protected async Task TestAsync( string code, string allCode, diff --git a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs index 4e2502c394e51..e6d353864a7ba 100644 --- a/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs +++ b/src/Workspaces/CSharp/Portable/Classification/SyntaxClassification/NameSyntaxClassifier.cs @@ -175,6 +175,13 @@ private static bool TryClassifySymbol( } } + if (name is IdentifierNameSyntax { Identifier.Text: "args" } && + symbol is IParameterSymbol { ContainingSymbol: IMethodSymbol { Name: WellKnownMemberNames.TopLevelStatementsEntryPointMethodName } }) + { + classifiedSpan = new ClassifiedSpan(name.Span, ClassificationTypeNames.Keyword); + return true; + } + if (name.IsNint || name.IsNuint) { if (symbol is ITypeSymbol type && type.IsNativeIntegerType) From e46ec3fe014e53f5704740f2005e83a29ca3f8e9 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Tue, 25 Jan 2022 19:40:30 -0800 Subject: [PATCH 050/187] add all analyzers that are configured via editorconfig to code cleanup --- .../Portable/CSharpFeaturesResources.resx | 63 +++- .../CodeCleanup/CSharpCodeCleanupService.cs | 246 ++++++++++++-- .../xlf/CSharpFeaturesResources.cs.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.de.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.es.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.fr.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.it.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.ja.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.ko.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.pl.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.pt-BR.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.ru.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.tr.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.zh-Hans.xlf | 101 +++++- .../xlf/CSharpFeaturesResources.zh-Hant.xlf | 101 +++++- .../Core/Portable/CodeCleanup/readme.md | 31 ++ .../Core/Portable/FeaturesResources.resx | 66 ++++ .../Microsoft.CodeAnalysis.Features.csproj | 3 + .../Portable/xlf/FeaturesResources.cs.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.de.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.es.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.fr.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.it.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.ja.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.ko.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.pl.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.pt-BR.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.ru.xlf | 110 ++++++ .../Portable/xlf/FeaturesResources.tr.xlf | 110 ++++++ .../xlf/FeaturesResources.zh-Hans.xlf | 110 ++++++ .../xlf/FeaturesResources.zh-Hant.xlf | 110 ++++++ .../VisualBasicCodeCleanupService.vb | 62 +++- .../Portable/VBFeaturesResources.resx | 6 + .../Portable/xlf/VBFeaturesResources.cs.xlf | 10 + .../Portable/xlf/VBFeaturesResources.de.xlf | 10 + .../Portable/xlf/VBFeaturesResources.es.xlf | 10 + .../Portable/xlf/VBFeaturesResources.fr.xlf | 10 + .../Portable/xlf/VBFeaturesResources.it.xlf | 10 + .../Portable/xlf/VBFeaturesResources.ja.xlf | 10 + .../Portable/xlf/VBFeaturesResources.ko.xlf | 10 + .../Portable/xlf/VBFeaturesResources.pl.xlf | 10 + .../xlf/VBFeaturesResources.pt-BR.xlf | 10 + .../Portable/xlf/VBFeaturesResources.ru.xlf | 10 + .../Portable/xlf/VBFeaturesResources.tr.xlf | 10 + .../xlf/VBFeaturesResources.zh-Hans.xlf | 10 + .../xlf/VBFeaturesResources.zh-Hant.xlf | 10 + .../CSharpCodeCleanupFixerDiagnosticIds.cs | 321 +++++++++++++++--- .../CommonCodeCleanUpFixerDiagnosticIds.cs | 273 +++++++++++++-- .../Def/Implementation/CodeCleanup/readme.md | 133 ++++++++ ...osoft.VisualStudio.LanguageServices.csproj | 1 + ...isualBasicCodeCleanupFixerDiagnosticIds.vb | 20 ++ 51 files changed, 3805 insertions(+), 293 deletions(-) create mode 100644 src/Features/Core/Portable/CodeCleanup/readme.md create mode 100644 src/VisualStudio/Core/Def/Implementation/CodeCleanup/readme.md diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 699c6e9693e17..e82bc83107f26 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -522,18 +522,9 @@ Add/remove braces for single-line control statements - - Apply language/framework type preferences - - - Apply implicit/explicit type preferences - Apply 'this.' qualification preferences - - Sort accessibility modifiers - Apply expression/block body preferences @@ -636,4 +627,58 @@ Convert to raw string (no indent) + + Apply blank lines between consecutive braces preferences (experimental) + + + Apply blank line after colon in constructor initializer preferences (experimental) + + + Apply conditional delegate call preferences + + + Apply deconstruct preferences + + + Apply default(T) preferences + + + Apply embedded statements on same line preferences (experimental) + + + Apply local over anonymous function preferences + + + Apply method group conversion preferences + + + Apply namespace preferences + + + Apply new() preferences + + + Apply object collection initialization preferences + + + Apply parameter null preferences + + + Apply pattern matching preferences + + + Apply range preferences + + + Apply static local function preferences + + + Apply throw expression preferences + + + Apply using statement preferences + + + Apply 'var' preferences + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs b/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs index 7515a4c86fe26..b1c88bcad6c82 100644 --- a/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs +++ b/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs @@ -21,54 +21,252 @@ internal class CSharpCodeCleanupService : AbstractCodeCleanupService /// private static readonly ImmutableArray s_diagnosticSets = ImmutableArray.Create( - new DiagnosticSet(CSharpFeaturesResources.Apply_implicit_explicit_type_preferences, - IDEDiagnosticIds.UseImplicitTypeDiagnosticId, IDEDiagnosticIds.UseExplicitTypeDiagnosticId), + // Organize usings + // dotnet_separate_import_directive_groups + // dotnet_sort_system_directives_first + new DiagnosticSet(FeaturesResources.Apply_using_directive_placement_preferences, + IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId), + // file_header_template + new DiagnosticSet(FeaturesResources.Apply_file_header_preferences, + IDEDiagnosticIds.FileHeaderMismatch), - new DiagnosticSet(CSharpFeaturesResources.Apply_this_qualification_preferences, - IDEDiagnosticIds.AddQualificationDiagnosticId, IDEDiagnosticIds.RemoveQualificationDiagnosticId), + // this. preferences + // dotnet_style_qualification_for_event + // dotnet_style_qualification_for_field + // dotnet_style_qualification_for_method + // dotnet_style_qualification_for_property + new DiagnosticSet(AnalyzersResources.Add_this_or_Me_qualification, + IDEDiagnosticIds.AddQualificationDiagnosticId, + IDEDiagnosticIds.RemoveQualificationDiagnosticId), - new DiagnosticSet(CSharpFeaturesResources.Apply_language_framework_type_preferences, + // Language keywords vs BCL types preferences + // dotnet_style_predefined_type_for_locals_parameters_members + // dotnet_style_predefined_type_for_member_access + new DiagnosticSet(FeaturesResources.Apply_language_framework_type_preferences, IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId), - new DiagnosticSet(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements, - IDEDiagnosticIds.AddBracesDiagnosticId), + // Parentheses preferences + // dotnet_style_parentheses_in_arithmetic_binary_operators + // dotnet_style_parentheses_in_other_binary_operators + // dotnet_style_parentheses_in_other_operators + // dotnet_style_parentheses_in_relational_binary_operators + new DiagnosticSet(FeaturesResources.Apply_parentheses_preferences, + IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId, + IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId), + // Modifier preferences + // dotnet_style_require_accessibility_modifiers new DiagnosticSet(AnalyzersResources.Add_accessibility_modifiers, IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId), - new DiagnosticSet(CSharpFeaturesResources.Sort_accessibility_modifiers, - IDEDiagnosticIds.OrderModifiersDiagnosticId), + // Expression-level preferences + // dotnet_style_coalesce_expression + new DiagnosticSet(FeaturesResources.Apply_coalesce_expression_preferences, + IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId), + // dotnet_style_collection_initializer + new DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences, + IDEDiagnosticIds.UseCollectionInitializerDiagnosticId), + // dotnet_style_explicit_tuple_names + new DiagnosticSet(FeaturesResources.Apply_tuple_name_preferences, + IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId), + // dotnet_style_namespace_match_folder + new DiagnosticSet(FeaturesResources.Apply_namespace_matches_folder_preferences, + IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId), + // dotnet_style_null_propagation + new DiagnosticSet(FeaturesResources.Apply_null_propagation_preferences, + IDEDiagnosticIds.UseNullPropagationDiagnosticId), + // dotnet_style_object_initializer + new DiagnosticSet(FeaturesResources.Apply_object_initializer_preferences, + IDEDiagnosticIds.UseObjectInitializerDiagnosticId), + // dotnet_style_prefer_auto_properties + new DiagnosticSet(FeaturesResources.Apply_auto_property_preferences, + IDEDiagnosticIds.UseAutoPropertyDiagnosticId), + // dotnet_style_prefer_compound_assignment + new DiagnosticSet(FeaturesResources.Apply_compound_assignment_preferences, + IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId, + IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId), + new DiagnosticSet(FeaturesResources.Apply_conditional_expression_preferences, + // dotnet_style_prefer_conditional_expression_over_assignment + IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId, + // dotnet_style_prefer_conditional_expression_over_return + IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId), + // dotnet_style_prefer_inferred_anonymous_type_member_names + // dotnet_style_prefer_inferred_tuple_names + new DiagnosticSet(FeaturesResources.Apply_inferred_anonymous_type_member_names_preferences, + IDEDiagnosticIds.UseInferredMemberNameDiagnosticId), + // dotnet_style_prefer_is_null_check_over_reference_equality_method + new DiagnosticSet(FeaturesResources.Apply_null_checking_preferences, + IDEDiagnosticIds.UseIsNullCheckDiagnosticId), + // dotnet_style_prefer_simplified_boolean_expressions + new DiagnosticSet(FeaturesResources.Apply_simplify_boolean_expression_preferences, + IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId), + // dotnet_style_prefer_simplified_interpolation + new DiagnosticSet(FeaturesResources.Apply_string_interpolation_preferences, + IDEDiagnosticIds.SimplifyInterpolationId), + // Field preferences + // dotnet_style_readonly_field new DiagnosticSet(CSharpFeaturesResources.Make_private_field_readonly_when_possible, IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId), - new DiagnosticSet(FeaturesResources.Remove_unnecessary_casts, - IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId), + // Parameter preferences + // dotnet_code_quality_unused_parameters + new DiagnosticSet(FeaturesResources.Remove_unused_parameters, + IDEDiagnosticIds.UnusedParameterDiagnosticId), + + // Suppression preferences + // dotnet_remove_unnecessary_suppression_exclusions + new DiagnosticSet(FeaturesResources.Remove_unused_suppressions, + IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId), + + // New line preferences + // dotnet_style_allow_multiple_blank_lines_experimental + new DiagnosticSet(FeaturesResources.Apply_blank_line_preferences_experimental, + IDEDiagnosticIds.MultipleBlankLinesDiagnosticId), + // dotnet_style_allow_statement_immediately_after_block_experimental + new DiagnosticSet(FeaturesResources.Apply_statement_after_block_preferences_experimental, + IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId), + + // C# Coding Conventions + // var preferences + // csharp_style_var_elsewhere + // csharp_style_var_for_built_in_types + // csharp_style_var_when_type_is_apparent + new DiagnosticSet(CSharpFeaturesResources.Apply_var_preferences, + IDEDiagnosticIds.UseImplicitTypeDiagnosticId, + IDEDiagnosticIds.UseExplicitTypeDiagnosticId), + + // Expression-bodied members new DiagnosticSet(CSharpFeaturesResources.Apply_expression_block_body_preferences, + // csharp_style_expression_bodied_accessors + IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, + // csharp_style_expression_bodied_constructors IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, + // csharp_style_expression_bodied_indexers + IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, + // csharp_style_expression_bodied_lambdas + IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId, + // csharp_style_expression_bodied_local_functions + IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId, + // csharp_style_expression_bodied_methods IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, - IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, + // csharp_style_expression_bodied_operators IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, - IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, - IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, - IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, - IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId), + IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, + // csharp_style_expression_bodied_properties + IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId), + + // Pattern matching preferences + new DiagnosticSet(CSharpFeaturesResources.Apply_pattern_matching_preferences, + // csharp_style_pattern_matching_over_as_with_null_check + IDEDiagnosticIds.InlineAsTypeCheckId, + // csharp_style_pattern_matching_over_is_with_cast_check + IDEDiagnosticIds.InlineIsTypeCheckId, + // csharp_style_prefer_extended_property_pattern + IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId, + // csharp_style_prefer_not_pattern + IDEDiagnosticIds.UseNotPatternDiagnosticId, + // csharp_style_prefer_pattern_matching + IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId, + // csharp_style_prefer_switch_expression + IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId, + // csharp_style_prefer_null_check_over_type_check + IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId), + + // Null-checking preferences + // csharp_style_conditional_delegate_call + new DiagnosticSet(CSharpFeaturesResources.Apply_conditional_delegate_call_preferences, + IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId), + // csharp_style_prefer_parameter_null_checking + new DiagnosticSet(CSharpFeaturesResources.Apply_parameter_null_preferences, + IDEDiagnosticIds.UseParameterNullCheckingId), + + // Modifier preferences + // csharp_prefer_static_local_function + new DiagnosticSet(CSharpFeaturesResources.Apply_static_local_function_preferences, + IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId), + // csharp_preferred_modifier_order + new DiagnosticSet(FeaturesResources.Sort_accessibility_modifiers, + IDEDiagnosticIds.OrderModifiersDiagnosticId, + "CS0267"), + // Code-block preferences + // csharp_prefer_braces + new DiagnosticSet(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements, + IDEDiagnosticIds.AddBracesDiagnosticId), + + // csharp_prefer_simple_using_statement + new DiagnosticSet(CSharpFeaturesResources.Apply_using_statement_preferences, + IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId), + + // csharp_style_namespace_declarations + new DiagnosticSet(CSharpFeaturesResources.Apply_namespace_preferences, + IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId), + + // csharp_style_prefer_method_group_conversion + new DiagnosticSet(CSharpFeaturesResources.Apply_method_group_conversion_preferences, + IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId), + + // Expression-level preferences + // csharp_prefer_simple_default_expression + new DiagnosticSet(CSharpFeaturesResources.Apply_default_T_preferences, + IDEDiagnosticIds.UseDefaultLiteralDiagnosticId), + + new DiagnosticSet(CSharpFeaturesResources.Apply_deconstruct_preferences, + // csharp_style_deconstructed_variable_declaration + IDEDiagnosticIds.UseDeconstructionDiagnosticId, + // csharp_style_prefer_tuple_swap + IDEDiagnosticIds.UseTupleSwapDiagnosticId), + + // csharp_style_implicit_object_creation_when_type_is_apparent + new DiagnosticSet(CSharpFeaturesResources.Apply_new_preferences, + IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId), + + // csharp_style_inlined_variable_declaration new DiagnosticSet(CSharpFeaturesResources.Apply_inline_out_variable_preferences, IDEDiagnosticIds.InlineDeclarationDiagnosticId), - new DiagnosticSet(FeaturesResources.Remove_unused_variables, - CSharpRemoveUnusedVariableCodeFixProvider.CS0168, CSharpRemoveUnusedVariableCodeFixProvider.CS0219), + new DiagnosticSet(CSharpFeaturesResources.Apply_range_preferences, + // csharp_style_prefer_index_operator + IDEDiagnosticIds.UseIndexOperatorDiagnosticId, + // csharp_style_prefer_range_operator + IDEDiagnosticIds.UseRangeOperatorDiagnosticId), - new DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences, - IDEDiagnosticIds.UseObjectInitializerDiagnosticId, IDEDiagnosticIds.UseCollectionInitializerDiagnosticId), + // csharp_style_prefer_local_over_anonymous_function + new DiagnosticSet(CSharpFeaturesResources.Apply_local_over_anonymous_function_preferences, + IDEDiagnosticIds.UseLocalFunctionDiagnosticId), - new DiagnosticSet(CSharpFeaturesResources.Apply_using_directive_placement_preferences, - IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId), + // csharp_style_throw_expression + new DiagnosticSet(CSharpFeaturesResources.Apply_throw_expression_preferences, + IDEDiagnosticIds.UseThrowExpressionDiagnosticId), - new DiagnosticSet(FeaturesResources.Apply_file_header_preferences, - IDEDiagnosticIds.FileHeaderMismatch)); + // csharp_style_unused_value_assignment_preference + // csharp_style_unused_value_expression_statement_preference + new DiagnosticSet(FeaturesResources.Apply_unused_value_preferences, + IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId, + IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId), + + // New line preferences + // csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental + new DiagnosticSet(CSharpFeaturesResources.Apply_blank_line_after_colon_in_constructor_initializer_preferences_experimental, + IDEDiagnosticIds.ConstructorInitializerPlacementDiagnosticId), + // csharp_style_allow_blank_lines_between_consecutive_braces_experimental + new DiagnosticSet(CSharpFeaturesResources.Apply_blank_lines_between_consecutive_braces_preferences_experimental, + IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId), + // csharp_style_allow_embedded_statements_on_same_line_experimental + new DiagnosticSet(CSharpFeaturesResources.Apply_embedded_statements_on_same_line_preferences_experimental, + IDEDiagnosticIds.EmbeddedStatementPlacementDiagnosticId), + + // Simplification rules + + new DiagnosticSet(FeaturesResources.Remove_unnecessary_casts, + IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId), + + new DiagnosticSet(FeaturesResources.Remove_unused_variables, + CSharpRemoveUnusedVariableCodeFixProvider.CS0168, + CSharpRemoveUnusedVariableCodeFixProvider.CS0219) + ); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index 8e1c250f2cb80..d14b2fafbddd3 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -27,24 +27,89 @@ Povolit v tomto projektu nezabezpečený kód
+ + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Použít předvolby pro text výrazu/bloku - - Apply implicit/explicit type preferences - Použít předvolby imlicitního/explicitního typu - - Apply inline 'out' variables preferences Použít předvolby vložených proměnných out - - Apply language/framework type preferences - Použít předvolby typu jazyka/architektury + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Použít předvolby kvalifikace this. + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Použít upřednostňované předvolby umístění using 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Přiřadit parametry out @@ -197,11 +277,6 @@ Zjednodušit všechny výskyty - - Sort accessibility modifiers - Seřadit modifikátory dostupnosti - - Unseal class '{0}' Rozpečetit třídu {0} diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 212ef71d02aa9..2a8fda261d4c0 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -27,24 +27,89 @@ Unsicheren Code in diesem Projekt zulassen + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Ausdrucks-/Blocktextkörpereinstellungen anwenden - - Apply implicit/explicit type preferences - Implizite/explizite Typeneinstellungen anwenden - - Apply inline 'out' variables preferences Einstellungen für out-Inlinevariablen anwenden - - Apply language/framework type preferences - Einstellungen zum Sprach-/Frameworktyp anwenden + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Einstellungen zur Qualifikation "this." anwenden + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Bevorzugte using-Platzierungseinstellungen anwenden 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters out-Parameter zuweisen @@ -197,11 +277,6 @@ Alle Vorkommen vereinfachen - - Sort accessibility modifiers - Zugriffsmodifizierer sortieren - - Unseal class '{0}' Versiegelung der Klasse "{0}" aufheben diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 25fc7f5ce954f..09c8aebc9540b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -27,24 +27,89 @@ Permitir código no seguro en este proyecto + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Aplicar preferencias de cuerpo de la expresión o del bloque - - Apply implicit/explicit type preferences - Aplicar preferencias de tipos implícitos o explícitos - - Apply inline 'out' variables preferences Aplicar preferencias de variables “out” insertadas - - Apply language/framework type preferences - Aplicar preferencias de tipo de lenguaje o marco + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Aplicar preferencias de calificación “this.” + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Aplicar preferencias de selección de ubicación de "using" 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Asignar parámetros "out" @@ -197,11 +277,6 @@ Simplificar todas las repeticiones - - Sort accessibility modifiers - Ordenar modificadores de accesibilidad - - Unseal class '{0}' Quitar el sello de la clase "{0}" diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 073f648fc59fe..b2be4ab9126d9 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -27,24 +27,89 @@ Autoriser du code unsafe dans ce projet + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Appliquer les préférences de corps d'expression/de bloc - - Apply implicit/explicit type preferences - Appliquer les préférences de type implicite/explicite - - Apply inline 'out' variables preferences Appliquer les préférences des variables 'out' inline - - Apply language/framework type preferences - Appliquer les préférences de type de langage/framework + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Appliquer les préférences de qualification 'this.' + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Appliquer les préférences de placement de 'using' par défaut 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Assigner des paramètres 'out' @@ -197,11 +277,6 @@ Simplifier toutes les occurrences - - Sort accessibility modifiers - Trier les modificateurs d'accessibilité - - Unseal class '{0}' Classe unsealed '{0}' diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index b5fbcb69dc1b9..a651cf7a87ead 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -27,24 +27,89 @@ Consenti codice unsafe in questo progetto + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Applica le preferenze relative al corpo dell'espressione o del blocco - - Apply implicit/explicit type preferences - Applica le preferenze relative al tipo implicito/esplicito - - Apply inline 'out' variables preferences Applica le preferenze relative alle variabili 'out' inline - - Apply language/framework type preferences - Applica le preferenze relative al tipo di linguaggio/framework + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Applica le preferenze relative alla qualificazione 'this.' + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Applica le preferenze di posizionamento per 'using' 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Assegnare parametri 'out' @@ -197,11 +277,6 @@ Semplifica tutte le occorrenze - - Sort accessibility modifiers - Ordina i modificatori di accessibilità - - Unseal class '{0}' Rimuovi seal dalla classe '{0}' diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index b8c685de74763..faed6869b9668 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -27,24 +27,89 @@ このプロジェクトでアンセーフ コードを許可する + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences 式/ブロック本体の基本設定を適用します - - Apply implicit/explicit type preferences - 暗黙的/明示的な型の基本設定を適用します - - Apply inline 'out' variables preferences インラインの 'out' 変数の基本設定を適用します - - Apply language/framework type preferences - 言語/フレームワークの型の基本設定を適用します + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ 'this.' 修飾の基本設定を適用します + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences 優先する 'using' 配置設定を適用する 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters 'out' パラメーターの割り当て @@ -197,11 +277,6 @@ すべての出現箇所を簡素化します - - Sort accessibility modifiers - アクセシビリティ修飾子を並べ替える - - Unseal class '{0}' クラス '{0}' のシールを解除 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 7fdc3c60ccf8b..9999e9349645a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -27,24 +27,89 @@ 이 프로젝트에서 안전하지 않은 코드 허용 + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences 식/블록 본문 기본 설정 적용 - - Apply implicit/explicit type preferences - 암시적/명시적 형식 기본 설정 적용 - - Apply inline 'out' variables preferences 인라인 'out' 변수 기본 설정 적용 - - Apply language/framework type preferences - 언어/프레임워크 형식 기본 설정 적용 + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ 'this.' 한정자 기본 설정 적용 + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences 기본 'using' 배치 기본 설정 적용 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters 'out' 매개 변수 할당 @@ -197,11 +277,6 @@ 모든 항목 단순화 - - Sort accessibility modifiers - 접근성 한정자 정렬 - - Unseal class '{0}' '{0}' 클래스 봉인 해제 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 577b1b9d0643d..288dae211b406 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -27,24 +27,89 @@ Zezwalaj na niebezpieczny kod w tym projekcie + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Zastosuj preferencje treści wyrażenia/bloku - - Apply implicit/explicit type preferences - Zastosuj preferencje niejawnego/jawnego typu - - Apply inline 'out' variables preferences Zastosuj preferencje wstawionych zmiennych „out” - - Apply language/framework type preferences - Zastosuj preferencje typu języka/platformy + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Zastosuj preferencje kwalifikacji „this.” + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Zastosuj preferowane preferencje umieszczania elementu „using” 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Przypisz parametry „out” @@ -197,11 +277,6 @@ Uprość wszystkie wystąpienia - - Sort accessibility modifiers - Sortuj modyfikatory dostępności - - Unseal class '{0}' Odpieczętuj klasę „{0}” diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index e1b357e99a54f..df5edd75aceec 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -27,24 +27,89 @@ Permitir código não seguro neste projeto + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Aplicar as preferências de expressão/corpo do bloco - - Apply implicit/explicit type preferences - Aplicar as preferências de tipo implícitas/explícitas - - Apply inline 'out' variables preferences Aplicar as preferências de variáveis 'out' embutidas - - Apply language/framework type preferences - Aplicar as preferências de linguagem/tipo de estrutura + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Aplicar as preferências de qualificação 'this.' + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Aplicar preferências de posicionamento preferencial 'using' 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Atribuir parâmetros 'out' @@ -197,11 +277,6 @@ Simplificar todas as ocorrências - - Sort accessibility modifiers - Classificar modificadores de acessibilidade - - Unseal class '{0}' Desselar a classe '{0}' diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 143a0db468755..c88ca3b2d1bf3 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -27,24 +27,89 @@ Разрешить небезопасный код в этом проекте + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences Применять предпочтения для выражения или тела блока - - Apply implicit/explicit type preferences - Применять предпочтения для неявных или явных типов - - Apply inline 'out' variables preferences Применять предпочтения для встроенных переменных out - - Apply language/framework type preferences - Применять предпочтения для типа языка или платформы + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ Применять предпочтения для квалификации this. + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Применить предпочтительные параметры размещения "using" 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters Присвоение значений параметров "out" @@ -197,11 +277,6 @@ Упростить все вхождения - - Sort accessibility modifiers - Сортировать модификаторы доступности - - Unseal class '{0}' Распечатать класс "{0}" diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 91a4a82fc5c10..cc283ae8ae080 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -27,24 +27,89 @@ Bu projede güvenli olmayan koda izin ver + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences İfade/blok gövdesi tercihlerini uygula - - Apply implicit/explicit type preferences - Örtük/açık tür tercihlerini uygula - - Apply inline 'out' variables preferences Satır içi 'out' değişkenlerine ilişkin tercihleri uygula - - Apply language/framework type preferences - Dil/çerçeve türü tercihlerini uygula + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ 'this.' nitelemesi tercihlerini uygula + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences Tercih edilen 'using' yerleştirme tercihlerini uygulayın 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters 'out' parametreleri ata @@ -197,11 +277,6 @@ Tüm yinelemeleri basitleştir - - Sort accessibility modifiers - Erişilebilirlik değiştiricilerini sırala - - Unseal class '{0}' '{0}' sınıfının mührünü aç diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 38daa1b0c923a..cfaa7f0442b7b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -27,24 +27,89 @@ 允许在此项目中使用不安全代码 + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences 应用表达式/块主体首选项 - - Apply implicit/explicit type preferences - 应用隐式/显式类型首选项 - - Apply inline 'out' variables preferences 应用内联 “out” 变量首选项 - - Apply language/framework type preferences - 应用语言/框架类型首选项 + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ 应用 “this.” 资格首选项 + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences 应用首选的 "using" 放置首选项 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters 为 "out" 参数赋值 @@ -197,11 +277,6 @@ 简化所有事件 - - Sort accessibility modifiers - 对可访问性修饰符排序 - - Unseal class '{0}' Unseal 类 "{0}" diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index ef69480d435f2..696f9451cf792 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -27,24 +27,89 @@ 在此專案中允許不安全的程式碼 + + Apply blank line after colon in constructor initializer preferences (experimental) + Apply blank line after colon in constructor initializer preferences (experimental) + + + + Apply blank lines between consecutive braces preferences (experimental) + Apply blank lines between consecutive braces preferences (experimental) + + + + Apply conditional delegate call preferences + Apply conditional delegate call preferences + + + + Apply deconstruct preferences + Apply deconstruct preferences + + + + Apply default(T) preferences + Apply default(T) preferences + + + + Apply embedded statements on same line preferences (experimental) + Apply embedded statements on same line preferences (experimental) + + Apply expression/block body preferences 套用延伸模組/區塊主體喜好設定 - - Apply implicit/explicit type preferences - 套用隱含/明確類型喜好設定 - - Apply inline 'out' variables preferences 套用內嵌 'out' 變數喜好設定 - - Apply language/framework type preferences - 套用語言/架構類型喜好設定 + + Apply local over anonymous function preferences + Apply local over anonymous function preferences + + + + Apply method group conversion preferences + Apply method group conversion preferences + + + + Apply namespace preferences + Apply namespace preferences + + + + Apply new() preferences + Apply new() preferences + + + + Apply object collection initialization preferences + Apply object collection initialization preferences + + + + Apply parameter null preferences + Apply parameter null preferences + + + + Apply pattern matching preferences + Apply pattern matching preferences + + + + Apply range preferences + Apply range preferences + + + + Apply static local function preferences + Apply static local function preferences @@ -52,11 +117,26 @@ 套用 'this.' 資格喜好設定 + + Apply throw expression preferences + Apply throw expression preferences + + Apply preferred 'using' placement preferences 套用慣用的 'using' 放置喜好設定 'using' is a C# keyword and should not be localized + + Apply using statement preferences + Apply using statement preferences + + + + Apply 'var' preferences + Apply 'var' preferences + + Assign 'out' parameters 指派 'out' 參數 @@ -197,11 +277,6 @@ 簡化所有項目 - - Sort accessibility modifiers - 排序協助工具修飾元 - - Unseal class '{0}' 為類別 '{0}' 解密 diff --git a/src/Features/Core/Portable/CodeCleanup/readme.md b/src/Features/Core/Portable/CodeCleanup/readme.md new file mode 100644 index 0000000000000..8a8a39c3dbc49 --- /dev/null +++ b/src/Features/Core/Portable/CodeCleanup/readme.md @@ -0,0 +1,31 @@ +# Visual Studio code cleanup service + +## Types used in the implementation + +- Types Roslyn defines + - [ICodeCleanupService](ICodeCleanupService.cs) interface that inherits from [ILanguageService](../../../../Workspaces/Core/Portable/Workspace/Host/ILanguageService.cs). This language service is what does all the work in code cleanup. + - [AbstractCodeCleanupService](AbstractCodeCleanupService.cs) methods common to both C# and VB implementations of [ICodeCleanupService](ICodeCleanupService.cs) + - [DiagnosticSet](DiagnosticSet.cs) type to group diagnostics that should all be fixed together (all block style settings for example) used by [AbstractCodeCleanupService](AbstractCodeCleanupService.cs) + - [EnabledDiagnosticOptions](EnabledDiagnosticOptions.cs) represents the total set of things the code cleanup service should attempt to fix. This is needed (instead of just using [DiagnosticSet](DiagnosticSet.cs)) because items like whitespace formatting and organize-usings do not use analyzers + - [OrganizeUsingsSet](OrganizeUsingsSet.cs) a type that packages the relevant organize-usings settings as boolean flags +- Types Defined by Visual Studio that Roslyn implements + - [ICodeCleanUpFixerProvider](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixerprovider) a factory that creates the [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) we want to use. + - [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) the interface we need to implement if we want Visual Studio to work for our language. +- Types Implemented by Visual Studio used by Roslyn + - Attributes used to add data to a [MEF](https://docs.microsoft.com/dotnet/framework/mef/) export of a `FixId` + - [FixIdAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.fixidattribute) as string representing the identify of a something that an [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) can fix. We use the Diagnostic Id (such as `IDE001`) for these values. + - [NameAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.utilities.nameattribute) as string representing the name of something that an [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) can fix. We also use the Diagnostic Id (such as `IDE001`) for these values since we don't have a concept of an Id being different than the Name. + - [OrderAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.utilities.orderattribute) an attribute used to order items in the UI. You can ask for an item to be ordered before or after something by referring to its `FixId` (the string given to the [FixIdAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.fixidattribute)) + - [ConfigurationKeyAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.configurationkeyattribute) unused by us, we have our own configuration mechanisms for roslyn. We always export a [ConfigurationKeyAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.configurationkeyattribute) with the value of `"unused"`. + - [HelpLinkAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.helplinkattribute) url to direct the user to. should be a page on docs.microsoft.com. + - [LocalizedNameAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.utilities.localizednameattribute) The string that will be used in the VS UI. + - [ICodeCleanUpScope](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupscope) exchange type passed to our [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) implementation used to tell us what to fix (document, project, solution, etc) + - [ICodeCleanUpExecutionContext](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupexecutioncontext)exchange type passed to our [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) implementation used to tell us what `FixId`s (aka Diagnostic Ids for us) that the user has chosen to fix in the UI. + +## How these types fit together + +The implementations for [AbstractCodeCleanupService](AbstractCodeCleanupService.cs) are [CSharpCodeCleanupService](../../../CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs) and [VisualBasicCodeCleanupService](../../../VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb). The only things these implementations are responsible for is computing the ImmutableArray of [DiagnosticSet](DiagnosticSet.cs)s that is used to determine what diagnostics code cleanup can fix. In summary: if a Diagnostic Id is not listed when calling `GetDiagnosticSets` it will not be fixed. + +The Code Cleanup language service is then consumed by our [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) implementations: [CSharpCodeCleanUpFixer](../../../../VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs) and [VisualBasicCodeCleanUpFixer](../../../../VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixer.vb) (with nearly all the logic for them living in [AbstractCodeCleanUpFixer](../../../../VisualStudio/Core/Def/Implementation/CodeCleanup/AbstractCodeCleanUpFixer.cs)). + +In order for any of our codefixes to appear in the code cleanup UI in Visual Studio we need to export a [FixIdAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.fixidattribute) on a field so Visual Studio can compose the set of diagnostic ids we care about. The common diagnostic ids that are used in both Visual Basic and C# are defined in [CommonCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs). C# specific diagnostic ids are exported in [CSharpCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs) and Visual Basic specific diagnostic ids are exported in [VisualBasicCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb). diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 0b3f3598cc546..377588652116c 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -3142,4 +3142,70 @@ Zero-width positive lookbehind assertions are typically used at the beginning of The symbol has no implementations. + + Apply auto property preferences + + + Apply blank line preferences (experimental) + + + Apply coalesce expression preferences + + + Apply compound assignment preferences + + + Apply inferred anonymous type member names preferences + + + Apply language/framework type preferences + + + Apply namespace matches folde preferences + + + Apply null checking preferences + + + Apply null propagation preferences + + + Apply object initializer preferences + + + Apply simplify boolean expression preferences + + + Apply string interpolation preferences + + + Apply tuple name preferences + + + Remove unused suppressions + + + Apply parentheses preferences + + + Apply statement after block preferences (experimental) + + + Apply unused value preferences + + + Remove unused parameters + + + Apply conditional expression preferences + + + Apply using directive placement preferences + + + Remove unnecessary Imports or usings + + + Sort Imports or usings + \ No newline at end of file diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 7cda913db91b8..c3a347c1f3ec7 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -122,6 +122,9 @@ + + + diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index b17d57b131e40..134b1b68b0d92 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -215,16 +215,106 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Aktualizace, která způsobí změnu návratového typu implicitní metody Main, vyžaduje restartování aplikace. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Použít předvolby hlaviček souborů + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Použít předvolby inicializace objektu/kolekce + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. Použití změn zdroje v době, kdy je aplikace spuštěna, není podporováno modulem runtime. @@ -2400,11 +2490,26 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Odebrat modifikátor async + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Odebrat nepotřebná přetypování + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Odebrat nepoužívané proměnné @@ -2465,6 +2570,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Tichý + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Seřadit modifikátory dostupnosti diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index d8eba792ed050..997ca0f598972 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -215,16 +215,106 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Ein Update, das bewirkt, dass der Rückgabetyp der impliziten Main-Methode geändert wird, erfordert einen Neustart der Anwendung. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Dateiheadereinstellungen anwenden + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Einstellungen zur Objekt-/Sammlungsinitialisierung anwenden + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. Das Anwenden von Quelländerungen während der Ausführung der Anwendung wird von der Laufzeit nicht unterstützt. @@ -2400,11 +2490,26 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg async-Modifizierer entfernen + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Nicht erforderliche Umwandlungen entfernen + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Nicht verwendete Variablen entfernen @@ -2465,6 +2570,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Still + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Zugriffsmodifizierer sortieren diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 7833a29910cae..878f1341bf9a6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -215,16 +215,106 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Una actualización que hace que el tipo de valor devuelto del método Main implícito cambie requiere reiniciar la aplicación. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Aplicar preferencias de encabezado de archivo + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Aplicar preferencias de inicialización de objetos o colecciones + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. El tiempo de ejecución no admite la aplicación de cambios de origen mientras se ejecuta la aplicación. @@ -2400,11 +2490,26 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Quitar el modificador "async" + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Quitar conversiones innecesarias + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Quitar variables no utilizadas @@ -2465,6 +2570,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Silencioso + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Ordenar modificadores de accesibilidad diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 7e28c69e6e8c5..da33374dd0492 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -215,16 +215,106 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Une mise à jour qui entraîne la modification du type de retour de la méthode implicite Main requiert le redémarrage de l'application. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Appliquer les préférences d'en-tête de fichier + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Appliquer les préférences d'initialisation des objets/collections + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. L’application des modifications de la source pendant l’exécution de l’application n’est pas prise en charge par le Runtime. @@ -2400,11 +2490,26 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Supprimer le modificateur 'async' + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Supprimer les casts inutiles + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Supprimer les variables inutilisées @@ -2465,6 +2570,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée Silencieux + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Trier les modificateurs d'accessibilité diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 1b07cde1483b7..7598fd3b16db9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -215,16 +215,106 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Con un aggiornamento che causa la modifica del tipo restituito del metodo Main implicito è necessario riavviare l'applicazione. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Applica le preferenze relative alle intestazioni di file + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Applica le preferenze di inizializzazione di oggetti/raccolte + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. L'applicazione delle modifiche all'origine durante l'esecuzione dell'applicazione non è supportata dal runtime. @@ -2400,11 +2490,26 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Rimuovi il modificatore 'async' + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Rimuovi i cast non necessari + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Rimuovi le variabili non usate @@ -2465,6 +2570,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Invisibile + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Ordina i modificatori di accessibilità diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 3518e47ac51d7..e502fb6284089 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -215,16 +215,106 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 暗黙的な Main メソッドの戻り値の型を変更する更新には、アプリケーションの再起動が必要です。 {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences ファイル ヘッダーの基本設定を適用する + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences オブジェクト/コレクションの初期化の基本設定を適用します + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. アプリケーションの実行中にソースの変更を適用することは、ランタイムでサポートされていません。 @@ -2400,11 +2490,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 'async' 修飾子を削除してください + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts 不要なキャストを削除する + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables 未使用の変数を削除する @@ -2465,6 +2570,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of サイレント + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers アクセシビリティ修飾子を並べ替える diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index d5cd4943a0a70..bee2d0fe9b34f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -215,16 +215,106 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 암시적 Main 메서드의 반환 유형이 변경되도록 하는 업데이트는 응용 프로그램을 다시 시작해야 합니다. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences 파일 헤더 기본 설정 적용 + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences 개체/컬렉션 초기화 기본 설정 적용 + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. 애플리케이션이 실행되는 동안 원본 변경 사항을 적용하는 것은 런타임에서 지원되지 않습니다. @@ -2400,11 +2490,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 'async' 한정자 제거 + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts 불필요한 캐스트 제거 + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables 사용하지 않는 변수 제거 @@ -2465,6 +2570,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 침묵 + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers 접근성 한정자 정렬 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 50a84b6da2dfa..1cbe2a9ffafbe 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -215,16 +215,106 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Aktualizacja, która powoduje zmianę zwracanego typu niejawnej metody Main, wymaga ponownego uruchomienia aplikacji. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Zastosuj preferencje nagłówka pliku + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Zastosuj preferencje inicjowania obiektu/kolekcji + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. Stosowanie zmian źródłowych, gdy aplikacja jest uruchomiona, nie jest obsługiwane przez środowisko uruchomieniowe. @@ -2400,11 +2490,26 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Usuń modyfikator „async” + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Usuń niepotrzebne rzutowania + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Usuń nieużywane zmienne @@ -2465,6 +2570,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Cichy + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Sortuj modyfikatory dostępności diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index fa5d7ad61c39c..432f5da46a6b7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -215,16 +215,106 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Uma atualização que faz com que o tipo de retorno do método Main implícito seja alterado requer a reinicialização do aplicativo. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Aplicar as preferências de cabeçalho de arquivo + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Aplicar as preferências de inicialização de objeto/coleção + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. Não há suporte para a aplicação de alterações de origem enquanto o aplicativo estiver em execução. @@ -2400,11 +2490,26 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Remover o modificador 'async' + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Remover conversões desnecessárias + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Remover variáveis não utilizadas @@ -2465,6 +2570,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Silencioso + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Classificar modificadores de acessibilidade diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 3476b7e7900b7..6cba133fb6606 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -215,16 +215,106 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Для обновления, приводящего к изменению возвращаемого типа неявного метода Main, требуется перезапустить приложение. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Применить параметры заголовка файла + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Применять предпочтения для инициализации объекта или коллекции + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. Применение изменений источника во время выполнения приложения не поддерживается средой выполнения. @@ -2400,11 +2490,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Удалить модификатор "async" + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Удалить ненужные приведения + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Удалить неиспользуемые переменные @@ -2465,6 +2570,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Тихий + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Сортировать модификаторы доступности diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 1922e8a8861e3..d09c2f4e3d813 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -215,16 +215,106 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Örtük Main yönteminin dönüş türü değişikliğine neden olan bir güncelleştirme, uygulamanın yeniden başlatılmasını gerektirir. {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences Dosya üst bilgisi tercihlerini uygula + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences Nesne/koleksiyon başlatma tercihlerini uygula + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. Uygulama çalışırken kaynak değişikliklerinin uygulanması çalışma zamanı tarafından desteklenmiyor. @@ -2400,11 +2490,26 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri 'async' değiştiricisini kaldırın + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts Gereksiz atamaları kaldır + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables Kullanılmayan değişkenleri kaldır @@ -2465,6 +2570,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri Sessiz + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers Erişilebilirlik değiştiricilerini sırala diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 385cee9491017..0f7a4e1525596 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -215,16 +215,106 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 导致隐式 Main 方法的返回类型更改的更新需要重新启动应用程序。 {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences 应用文件头首选项 + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences 应用对象/集合初始化首选项 + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. 在应用程序运行时,运行时不支持应用源更改。 @@ -2400,11 +2490,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 删除 "async" 修饰符 + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts 删除不必要的转换 + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables 删除未使用的变量 @@ -2465,6 +2570,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 无提示 + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers 对可访问性修饰符排序 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index fdfb9cf7132b8..3ee999ab57de6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -215,16 +215,106 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 導致隱含 Main 方法的傳回型別變更的更新需要重新啟動應用程式。 {Locked="Main"} is C# keywords and should not be localized. + + Apply auto property preferences + Apply auto property preferences + + + + Apply blank line preferences (experimental) + Apply blank line preferences (experimental) + + + + Apply coalesce expression preferences + Apply coalesce expression preferences + + + + Apply compound assignment preferences + Apply compound assignment preferences + + + + Apply conditional expression preferences + Apply conditional expression preferences + + Apply file header preferences 套用檔案標題的喜好設定 + + Apply inferred anonymous type member names preferences + Apply inferred anonymous type member names preferences + + + + Apply language/framework type preferences + Apply language/framework type preferences + + + + Apply namespace matches folde preferences + Apply namespace matches folde preferences + + + + Apply null checking preferences + Apply null checking preferences + + + + Apply null propagation preferences + Apply null propagation preferences + + Apply object/collection initialization preferences 套用物件/集合初始設定喜好設定 + + Apply object initializer preferences + Apply object initializer preferences + + + + Apply parentheses preferences + Apply parentheses preferences + + + + Apply simplify boolean expression preferences + Apply simplify boolean expression preferences + + + + Apply statement after block preferences (experimental) + Apply statement after block preferences (experimental) + + + + Apply string interpolation preferences + Apply string interpolation preferences + + + + Apply tuple name preferences + Apply tuple name preferences + + + + Apply unused value preferences + Apply unused value preferences + + + + Apply using directive placement preferences + Apply using directive placement preferences + + Applying source changes while the application is running is not supported by the runtime. 在應用程式執行時,執行階段不支援套用來源變更。 @@ -2400,11 +2490,26 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 移除 'async' 修飾元 + + Remove unnecessary Imports or usings + Remove unnecessary Imports or usings + + Remove unnecessary casts 移除不必要的 Cast + + Remove unused parameters + Remove unused parameters + + + + Remove unused suppressions + Remove unused suppressions + + Remove unused variables 移除未使用的變數 @@ -2465,6 +2570,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 靜音 + + Sort Imports or usings + Sort Imports or usings + + Sort accessibility modifiers 排序協助工具修飾元 diff --git a/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb b/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb index fa8cb2cdd4c9c..fa43004da5cd8 100644 --- a/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb +++ b/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb @@ -19,24 +19,66 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeCleanup ''' Maps format document code cleanup options to DiagnosticId[] ''' Private Shared ReadOnly s_diagnosticSets As ImmutableArray(Of DiagnosticSet) = ImmutableArray.Create( - New DiagnosticSet(VBFeaturesResources.Apply_Me_qualification_preferences, + New DiagnosticSet(FeaturesResources.Apply_using_directive_placement_preferences, + IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_file_header_preferences, + IDEDiagnosticIds.FileHeaderMismatch), + New DiagnosticSet(AnalyzersResources.Add_this_or_Me_qualification, IDEDiagnosticIds.AddQualificationDiagnosticId, IDEDiagnosticIds.RemoveQualificationDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_language_framework_type_preferences, + IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_parentheses_preferences, + IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId, IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId), New DiagnosticSet(AnalyzersResources.Add_accessibility_modifiers, IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_coalesce_expression_preferences, + IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences, + IDEDiagnosticIds.UseCollectionInitializerDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_tuple_name_preferences, + IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_namespace_matches_folder_preferences, + IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_null_propagation_preferences, + IDEDiagnosticIds.UseNullPropagationDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_object_initializer_preferences, + IDEDiagnosticIds.UseObjectInitializerDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_auto_property_preferences, + IDEDiagnosticIds.UseAutoPropertyDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_compound_assignment_preferences, + IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId, IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_conditional_expression_preferences, + IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId, IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_inferred_anonymous_type_member_names_preferences, + IDEDiagnosticIds.UseInferredMemberNameDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_null_checking_preferences, + IDEDiagnosticIds.UseIsNullCheckDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_simplify_boolean_expression_preferences, + IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_string_interpolation_preferences, + IDEDiagnosticIds.SimplifyInterpolationId), + New DiagnosticSet(AnalyzersResources.Make_field_readonly, + IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId), + New DiagnosticSet(FeaturesResources.Remove_unused_parameters, + IDEDiagnosticIds.UnusedParameterDiagnosticId), + New DiagnosticSet(FeaturesResources.Remove_unused_suppressions, + IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_blank_line_preferences_experimental, + IDEDiagnosticIds.MultipleBlankLinesDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_statement_after_block_preferences_experimental, + IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId), New DiagnosticSet(FeaturesResources.Sort_accessibility_modifiers, IDEDiagnosticIds.OrderModifiersDiagnosticId), - New DiagnosticSet(VBFeaturesResources.Make_private_field_ReadOnly_when_possible, - IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId), + New DiagnosticSet(VBFeaturesResources.Apply_isnot_preferences_experimental, + IDEDiagnosticIds.UseIsNotExpressionDiagnosticId), + New DiagnosticSet(VBFeaturesResources.Apply_object_creation_preferences, + IDEDiagnosticIds.SimplifyObjectCreationDiagnosticId), + New DiagnosticSet(FeaturesResources.Apply_unused_value_preferences, + IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId, IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId), New DiagnosticSet(FeaturesResources.Remove_unnecessary_casts, IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId), New DiagnosticSet(FeaturesResources.Remove_unused_variables, - VisualBasicRemoveUnusedVariableCodeFixProvider.BC42024), - New DiagnosticSet(FeaturesResources.Apply_object_collection_initialization_preferences, - IDEDiagnosticIds.UseObjectInitializerDiagnosticId, IDEDiagnosticIds.UseCollectionInitializerDiagnosticId), - New DiagnosticSet(VBFeaturesResources.Apply_Imports_directive_placement_preferences, - IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId), - New DiagnosticSet(FeaturesResources.Apply_file_header_preferences, - IDEDiagnosticIds.FileHeaderMismatch)) + VisualBasicRemoveUnusedVariableCodeFixProvider.BC42024)) diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx index ae74cfdba2a40..29aff9ce70e6a 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx @@ -1257,4 +1257,10 @@ Sub(<parameterList>) <statement> Shared constructor + + Apply IsNot preferences (experimental) + + + Apply object creation preferences + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf index e2999d8118615..0b56576d7baae 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf @@ -37,6 +37,16 @@ Použít předvolby pro kvalifikaci Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Změnit na DirectCast diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf index ada441194bb66..770942630d39c 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf @@ -37,6 +37,16 @@ Me-Qualifizierungseinstellungen anwenden {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' In "DirectCast" ändern diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf index 610773f10b381..a7b2c02ff9976 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf @@ -37,6 +37,16 @@ Aplicar las preferencias de calificación Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Cambiar a "DirectCast" diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf index cf2a95168ae14..0c025cbef5c45 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf @@ -37,6 +37,16 @@ Appliquer les préférences de qualification pour Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Changer en 'DirectCast' diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf index f802a35c45e58..06a70cce26a21 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf @@ -37,6 +37,16 @@ Applica le preferenze relative alla qualificazione Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Cambia in 'DirectCast' diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf index 19749d3081c57..5303af000a386 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf @@ -37,6 +37,16 @@ Me 修飾設定を適用する {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' 'DirectCast' に変更 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf index ee85b0c62ddda..8c9f838cdf0c0 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf @@ -37,6 +37,16 @@ Me 한정 기본 설정 적용 {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' 'DirectCast'로 변경 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf index aadb44e61cea0..4bf8529761df7 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf @@ -37,6 +37,16 @@ Zastosuj preferencje kwalifikacji Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Zmień na wyrażenie „DirectCast” diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf index ec3334f79b88e..81e6b11a7bcfc 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf @@ -37,6 +37,16 @@ Aplicar as preferências de qualificação de Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Alterar para 'DirectCast' diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf index e435152e22470..1ec1977607771 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf @@ -37,6 +37,16 @@ Применить параметры квалификации Me {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' Изменить на "DirectCast" diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf index a14226a4972ef..eb0f14620b149 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf @@ -37,6 +37,16 @@ Me yeterlilik tercihlerini uygula {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' 'DirectCast' olarak değiştir diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf index 454b7d9b2673a..dbfcce27d9448 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf @@ -37,6 +37,16 @@ 应用 Me 资格首选项 {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' 更改为 "DirectCast" diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf index 56d63d942b184..a3b4019742479 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf @@ -37,6 +37,16 @@ 套用 Me 資格喜好設定 {Locked="Me"} "Me" is a VB keyword and should not be localized. + + Apply IsNot preferences (experimental) + Apply IsNot preferences (experimental) + + + + Apply object creation preferences + Apply object creation preferences + + Change to 'DirectCast' 變更為 'DirectCast' diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs index 446b3143608fd..8a8a5f9d12e86 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs @@ -16,31 +16,22 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService internal static class CSharpCodeCleanUpFixerDiagnosticIds { [Export] - [FixId(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] - [Name(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] - [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_implicit_explicit_type_preferences))] - public static readonly FixIdDefinition? UseImplicitTypeDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] - [Name(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] - [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [FixId(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] + [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_implicit_explicit_type_preferences))] - public static readonly FixIdDefinition? UseExplicitTypeDiagnosticId; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] + public static readonly FixIdDefinition? CS0168; [Export] - [FixId(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] - [Name(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] - [Order(After = IDEDiagnosticIds.InlineDeclarationDiagnosticId)] + [FixId(CSharpRemoveUnusedVariableCodeFixProvider.CS0219)] + [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0219)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_language_framework_type_preferences))] - public static readonly FixIdDefinition? PreferBuiltInOrFrameworkTypeDiagnosticId; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] + public static readonly FixIdDefinition? CS0219; [Export] [FixId(IDEDiagnosticIds.AddBracesDiagnosticId)] @@ -52,22 +43,148 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? AddBracesDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [FixId(IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId)] + [Name(IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_blank_lines_between_consecutive_braces_preferences_experimental))] + public static readonly FixIdDefinition? ConsecutiveBracePlacementDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.ConstructorInitializerPlacementDiagnosticId)] + [Name(IDEDiagnosticIds.ConstructorInitializerPlacementDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_blank_line_after_colon_in_constructor_initializer_preferences_experimental))] + public static readonly FixIdDefinition? ConstructorInitializerPlacementDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId)] + [Name(IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? ConvertSwitchStatementToExpressionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.EmbeddedStatementPlacementDiagnosticId)] + [Name(IDEDiagnosticIds.EmbeddedStatementPlacementDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_embedded_statements_on_same_line_preferences_experimental))] + public static readonly FixIdDefinition? EmbeddedStatementPlacementDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.InlineAsTypeCheckId)] + [Name(IDEDiagnosticIds.InlineAsTypeCheckId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? InlineAsTypeCheckId; + + [Export] + [FixId(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] + [Name(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] + [Order(After = IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_inline_out_variable_preferences))] + public static readonly FixIdDefinition? InlineDeclarationDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.InlineIsTypeCheckId)] + [Name(IDEDiagnosticIds.InlineIsTypeCheckId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? InlineIsTypeCheckId; + + [Export] + [FixId(IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId)] + [Name(IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_conditional_delegate_call_preferences))] + public static readonly FixIdDefinition? InvokeDelegateWithConditionalAccessId; + + [Export] + [FixId(IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId)] + [Name(IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_static_local_function_preferences))] + public static readonly FixIdDefinition? MakeLocalFunctionStaticDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId)] + [Name(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_method_group_conversion_preferences))] + public static readonly FixIdDefinition? RemoveUnnecessaryLambdaExpressionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId)] + [Name(IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? SimplifyPropertyPatternDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseDeconstructionDiagnosticId)] + [Name(IDEDiagnosticIds.UseDeconstructionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_deconstruct_preferences))] + public static readonly FixIdDefinition? UseDeconstructionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseDefaultLiteralDiagnosticId)] + [Name(IDEDiagnosticIds.UseDefaultLiteralDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_default_T_preferences))] + public static readonly FixIdDefinition? UseDefaultLiteralDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] + [Name(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] + [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] + public static readonly FixIdDefinition? UseExplicitTypeDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForConstructorsDiagnosticId; + public static readonly FixIdDefinition? UseExpressionBodyForAccessorsDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] + [FixId(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForMethodsDiagnosticId; + public static readonly FixIdDefinition? UseExpressionBodyForConstructorsDiagnosticId; [Export] [FixId(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] @@ -78,6 +195,42 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForConversionOperatorsDiagnosticId; + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForIndexersDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForLambdaExpressionsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForLocalFunctionsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForMethodsDiagnosticId; + [Export] [FixId(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] [Name(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] @@ -97,48 +250,120 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? UseExpressionBodyForPropertiesDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [FixId(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId)] + [Name(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForIndexersDiagnosticId; + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_namespace_preferences))] + public static readonly FixIdDefinition? UseFileScopedNamespaceDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [FixId(IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId)] + [Name(IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForAccessorsDiagnosticId; + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_new_preferences))] + public static readonly FixIdDefinition? UseImplicitObjectCreationDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] - [Name(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] - [Order(After = IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [FixId(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [Name(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_inline_out_variable_preferences))] - public static readonly FixIdDefinition? InlineDeclarationDiagnosticId; + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] + public static readonly FixIdDefinition? UseImplicitTypeDiagnosticId; [Export] - [FixId(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] - [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] + [FixId(IDEDiagnosticIds.UseIndexOperatorDiagnosticId)] + [Name(IDEDiagnosticIds.UseIndexOperatorDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] - public static readonly FixIdDefinition? CS0168; + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_range_preferences))] + public static readonly FixIdDefinition? UseIndexOperatorDiagnosticId; [Export] - [FixId(CSharpRemoveUnusedVariableCodeFixProvider.CS0219)] - [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0219)] + [FixId(IDEDiagnosticIds.UseLocalFunctionDiagnosticId)] + [Name(IDEDiagnosticIds.UseLocalFunctionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] - public static readonly FixIdDefinition? CS0219; + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_local_over_anonymous_function_preferences))] + public static readonly FixIdDefinition? UseLocalFunctionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseNotPatternDiagnosticId)] + [Name(IDEDiagnosticIds.UseNotPatternDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? UseNotPatternDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId)] + [Name(IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? UseNullCheckOverTypeCheckDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseParameterNullCheckingId)] + [Name(IDEDiagnosticIds.UseParameterNullCheckingId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_parameter_null_preferences))] + public static readonly FixIdDefinition? UseParameterNullCheckingId; + + [Export] + [FixId(IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId)] + [Name(IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] + public static readonly FixIdDefinition? UsePatternCombinatorsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseRangeOperatorDiagnosticId)] + [Name(IDEDiagnosticIds.UseRangeOperatorDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_range_preferences))] + public static readonly FixIdDefinition? UseRangeOperatorDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId)] + [Name(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_using_statement_preferences))] + public static readonly FixIdDefinition? UseSimpleUsingStatementDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseThrowExpressionDiagnosticId)] + [Name(IDEDiagnosticIds.UseThrowExpressionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_throw_expression_preferences))] + public static readonly FixIdDefinition? UseThrowExpressionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseTupleSwapDiagnosticId)] + [Name(IDEDiagnosticIds.UseTupleSwapDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_deconstruct_preferences))] + public static readonly FixIdDefinition? UseTupleSwapDiagnosticId; } } diff --git a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs index df9ad2e77bec3..9de146c370701 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs @@ -12,6 +12,26 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeCleanup { internal static class CommonCodeCleanUpFixerDiagnosticIds { + [Export] + [FixId(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [Name(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [Order(After = AbstractCodeCleanUpFixer.FormatDocumentFixId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(ServicesVSResources), nameof(FeaturesResources.Remove_unnecessary_Imports_or_usings))] + public static readonly FixIdDefinition? RemoveUnusedImports; + + [Export] + [FixId(AbstractCodeCleanUpFixer.SortImportsFixId)] + [Name(AbstractCodeCleanUpFixer.SortImportsFixId)] + [Order(After = AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(ServicesVSResources), nameof(FeaturesResources.Sort_Imports_or_usings))] + public static readonly FixIdDefinition? SortImports; + [Export] [FixId(IDEDiagnosticIds.AddQualificationDiagnosticId)] [Name(IDEDiagnosticIds.AddQualificationDiagnosticId)] @@ -21,6 +41,15 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? AddQualificationDiagnosticId; + [Export] + [FixId(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] + [Name(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] + [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_using_directive_placement_preferences))] + public static readonly FixIdDefinition? MoveMisplacedUsingDirectivesDiagnosticId; + [Export] [FixId(IDEDiagnosticIds.RemoveQualificationDiagnosticId)] [Name(IDEDiagnosticIds.RemoveQualificationDiagnosticId)] @@ -30,6 +59,15 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? RemoveQualificationDiagnosticId; + [Export] + [FixId(AbstractCodeCleanUpFixer.FormatDocumentFixId)] + [Name(AbstractCodeCleanUpFixer.FormatDocumentFixId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(ServicesVSResources), nameof(ServicesVSResources.Format_document))] + public static readonly FixIdDefinition? FormatDocument; + [Export] [FixId(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] [Name(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] @@ -40,13 +78,41 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? AddAccessibilityModifiersDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [FixId(IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId)] + [Name(IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] - public static readonly FixIdDefinition? OrderModifiersDiagnosticId; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_parentheses_preferences))] + public static readonly FixIdDefinition? AddRequiredParenthesesDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId)] + [Name(IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_statement_after_block_preferences_experimental))] + public static readonly FixIdDefinition? ConsecutiveStatementPlacementDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId)] + [Name(IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] + public static readonly FixIdDefinition? ExpressionValueIsUnusedDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.FileHeaderMismatch)] + [Name(IDEDiagnosticIds.FileHeaderMismatch)] + [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_file_header_preferences))] + public static readonly FixIdDefinition? FileHeaderMismatch; [Export] [FixId(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] @@ -57,6 +123,42 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Make_field_readonly))] public static readonly FixIdDefinition? MakeFieldReadonlyDiagnosticId; + [Export] + [FixId(IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId)] + [Name(IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_namespace_matches_folder_preferences))] + public static readonly FixIdDefinition? MatchFolderAndNamespaceDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.MultipleBlankLinesDiagnosticId)] + [Name(IDEDiagnosticIds.MultipleBlankLinesDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_blank_line_preferences_experimental))] + public static readonly FixIdDefinition? MultipleBlankLinesDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] + public static readonly FixIdDefinition? OrderModifiersDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] + [Name(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_language_framework_type_preferences))] + public static readonly FixIdDefinition? PreferBuiltInOrFrameworkTypeDiagnosticId; + [Export] [FixId(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [Name(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] @@ -67,13 +169,76 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? RemoveUnnecessaryCastDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] - [Name(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] - [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] + [FixId(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId)] + [Name(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] - public static readonly FixIdDefinition? UseObjectInitializerDiagnosticId; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_parentheses_preferences))] + public static readonly FixIdDefinition? RemoveUnnecessaryParenthesesDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)] + [Name(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_suppressions))] + public static readonly FixIdDefinition? RemoveUnnecessarySuppressionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId)] + [Name(IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_simplify_boolean_expression_preferences))] + public static readonly FixIdDefinition? SimplifyConditionalExpressionDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.SimplifyInterpolationId)] + [Name(IDEDiagnosticIds.SimplifyInterpolationId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_string_interpolation_preferences))] + public static readonly FixIdDefinition? SimplifyInterpolationId; + + [Export] + [FixId(IDEDiagnosticIds.UnusedParameterDiagnosticId)] + [Name(IDEDiagnosticIds.UnusedParameterDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_parameters))] + public static readonly FixIdDefinition? UnusedParameterDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseAutoPropertyDiagnosticId)] + [Name(IDEDiagnosticIds.UseAutoPropertyDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_auto_property_preferences))] + public static readonly FixIdDefinition? UseAutoPropertyDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId)] + [Name(IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_compound_assignment_preferences))] + public static readonly FixIdDefinition? UseCoalesceCompoundAssignmentDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId)] + [Name(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_coalesce_expression_preferences))] + public static readonly FixIdDefinition? UseCoalesceExpressionDiagnosticId; [Export] [FixId(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] @@ -85,42 +250,84 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? UseCollectionInitializerDiagnosticId; [Export] - [FixId(AbstractCodeCleanUpFixer.FormatDocumentFixId)] - [Name(AbstractCodeCleanUpFixer.FormatDocumentFixId)] + [FixId(IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId)] + [Name(IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(ServicesVSResources), nameof(ServicesVSResources.Format_document))] - public static readonly FixIdDefinition? FormatDocument; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_compound_assignment_preferences))] + public static readonly FixIdDefinition? UseCompoundAssignmentDiagnosticId; [Export] - [FixId(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] - [Name(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] - [Order(After = AbstractCodeCleanUpFixer.FormatDocumentFixId)] + [FixId(IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId)] + [Name(IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(ServicesVSResources), nameof(ServicesVSResources.Remove_unnecessary_usings))] - public static readonly FixIdDefinition? RemoveUnusedImports; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_conditional_expression_preferences))] + public static readonly FixIdDefinition? UseConditionalExpressionForAssignmentDiagnosticId; [Export] - [FixId(AbstractCodeCleanUpFixer.SortImportsFixId)] - [Name(AbstractCodeCleanUpFixer.SortImportsFixId)] - [Order(After = AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [FixId(IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId)] + [Name(IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(ServicesVSResources), nameof(ServicesVSResources.Sort_usings))] - public static readonly FixIdDefinition? SortImports; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_conditional_expression_preferences))] + public static readonly FixIdDefinition? UseConditionalExpressionForReturnDiagnosticId; [Export] - [FixId(IDEDiagnosticIds.FileHeaderMismatch)] - [Name(IDEDiagnosticIds.FileHeaderMismatch)] - [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] + [FixId(IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId)] + [Name(IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] [HelpLink("https://www.microsoft.com")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_file_header_preferences))] - public static readonly FixIdDefinition? FileHeaderMismatch; + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_tuple_name_preferences))] + public static readonly FixIdDefinition? UseExplicitTupleNameDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseInferredMemberNameDiagnosticId)] + [Name(IDEDiagnosticIds.UseInferredMemberNameDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_inferred_anonymous_type_member_names_preferences))] + public static readonly FixIdDefinition? UseInferredMemberNameDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseIsNullCheckDiagnosticId)] + [Name(IDEDiagnosticIds.UseIsNullCheckDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_checking_preferences))] + public static readonly FixIdDefinition? UseIsNullCheckDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseNullPropagationDiagnosticId)] + [Name(IDEDiagnosticIds.UseNullPropagationDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_propagation_preferences))] + public static readonly FixIdDefinition? UseNullPropagationDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] + [Name(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] + [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] + public static readonly FixIdDefinition? UseObjectInitializerDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId)] + [Name(IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId)] + [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink("https://www.microsoft.com")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] + public static readonly FixIdDefinition? ValueAssignedIsUnusedDiagnosticId; } } diff --git a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/readme.md b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/readme.md new file mode 100644 index 0000000000000..6624ea493c183 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/readme.md @@ -0,0 +1,133 @@ +# How editorconfig, options, and diagnostics ids all fit together + +Turns out this is kinda complicated. At the bottom there is a table that maps an editorconfig option to its associated Diagnostic Ids. In some cases a single Id is reported for multiple analyzer options, in other a single editorconfig option can have two diagnostic Ids. + +Items that start with `dotnet_` and `file_header_template` are defined in [CodeStyleOptions2](../../../../../Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CodeStyleOptions2.cs) and exported for the code cleanup UI in [CommonCodeCleanUpFixerDiagnosticIds](CommonCodeCleanUpFixerDiagnosticIds.cs). + +Items that start with `csharp_` are defined in [CSharpCodeStyleOptions](../../../../../Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions.cs) and exported for the code cleanup UI in [CSharpCodeCleanUpFixerDiagnosticIds](../../../../CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs) + +Items that start with `visual_basic_` are defined in [VisualBasicCodeStyleOptions](../../../../../Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/CodeStyle/VisualBasicCodeStyleOptions.vb) amd exported for the code cleanup UI in [VisualBasicCodeCleanUpFixerDiagnosticIds](../../../../VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb) + +For editorconfig items that are handled by the formatter they are not exported for the code cleanup UI as our code cleanup service as a single yes/no option for formatting that determines if these are read and fixed. + +| **editorconfig** | **Diagnostic Ids** | +|-----------------------------------------------------------------------------------|--------------------------| +| dotnet_code_quality_unused_parameters | IDE0060 | +| dotnet_remove_unnecessary_suppression_exclusions | IDE0079 | +| dotnet_separate_import_directive_groups | IDE0065 | +| dotnet_sort_system_directives_first | IDE0065 | +| dotnet_style_allow_multiple_blank_lines_experimental | IDE2000 | +| dotnet_style_allow_statement_immediately_after_block_experimental | IDE2003 | +| dotnet_style_coalesce_expression | IDE0029 | +| dotnet_style_collection_initializer | IDE0028 | +| dotnet_style_explicit_tuple_names | IDE0033 | +| dotnet_style_namespace_match_folder | IDE0130 | +| dotnet_style_null_propagation | IDE0031 | +| dotnet_style_object_initializer | IDE0017 | +| dotnet_style_operator_placement_when_wrapping | N/A handled by formatter | +| dotnet_style_parentheses_in_arithmetic_binary_operators | IDE0047/IDE0048 | +| dotnet_style_parentheses_in_other_binary_operators | IDE0047/IDE0048 | +| dotnet_style_parentheses_in_other_operators | IDE0047/IDE0048 | +| dotnet_style_parentheses_in_relational_binary_operators | IDE0047/IDE0048 | +| dotnet_style_predefined_type_for_locals_parameters_members | IDE0049 | +| dotnet_style_predefined_type_for_member_access | IDE0049 | +| dotnet_style_prefer_auto_properties | IDE0032 | +| dotnet_style_prefer_compound_assignment | IDE0054/IDE0074 | +| dotnet_style_prefer_conditional_expression_over_assignment | IDE0045 | +| dotnet_style_prefer_conditional_expression_over_return | IDE0046 | +| dotnet_style_prefer_inferred_anonymous_type_member_names | IDE0037 | +| dotnet_style_prefer_inferred_tuple_names | IDE0037 | +| dotnet_style_prefer_is_null_check_over_reference_equality_method | IDE0041 | +| dotnet_style_prefer_simplified_boolean_expressions | IDE0075 | +| dotnet_style_prefer_simplified_interpolation | IDE0071 | +| dotnet_style_qualification_for_event | IDE0003/IDE0009 | +| dotnet_style_qualification_for_field | IDE0003/IDE0009 | +| dotnet_style_qualification_for_method | IDE0003/IDE0009 | +| dotnet_style_qualification_for_property | IDE0003/IDE0009 | +| dotnet_style_readonly_field | IDE0044 | +| dotnet_style_require_accessibility_modifiers | IDE0040 | +| csharp_indent_block_contents | N/A handled by formatter | +| csharp_indent_braces | N/A handled by formatter | +| csharp_indent_case_contents | N/A handled by formatter | +| csharp_indent_case_contents_when_block | N/A handled by formatter | +| csharp_indent_labels | N/A handled by formatter | +| csharp_indent_switch_labels | N/A handled by formatter | +| csharp_new_line_before_catch | N/A handled by formatter | +| csharp_new_line_before_else | N/A handled by formatter | +| csharp_new_line_before_finally | N/A handled by formatter | +| csharp_new_line_before_members_in_anonymous_types | N/A handled by formatter | +| csharp_new_line_before_members_in_object_initializers | N/A handled by formatter | +| csharp_new_line_before_open_brace | N/A handled by formatter | +| csharp_new_line_between_query_expression_clauses | N/A handled by formatter | +| csharp_prefer_braces | IDE0011 | +| csharp_prefer_simple_default_expression | IDE0034 | +| csharp_prefer_simple_using_statement | IDE0063 | +| csharp_prefer_static_local_function | IDE0062 | +| csharp_preferred_modifier_order | IDE0036 | +| csharp_preserve_single_line_blocks | N/A handled by formatter | +| csharp_preserve_single_line_statements | N/A handled by formatter | +| csharp_space_after_cast | N/A handled by formatter | +| csharp_space_after_colon_in_inheritance_clause | N/A handled by formatter | +| csharp_space_after_comma | N/A handled by formatter | +| csharp_space_after_dot | N/A handled by formatter | +| csharp_space_after_keywords_in_control_flow_statements | N/A handled by formatter | +| csharp_space_after_semicolon_in_for_statement | N/A handled by formatter | +| csharp_space_around_binary_operators | N/A handled by formatter | +| csharp_space_around_declaration_statements | N/A handled by formatter | +| csharp_space_before_colon_in_inheritance_clause | N/A handled by formatter | +| csharp_space_before_comma | N/A handled by formatter | +| csharp_space_before_dot | N/A handled by formatter | +| csharp_space_before_open_square_brackets | N/A handled by formatter | +| csharp_space_before_semicolon_in_for_statement | N/A handled by formatter | +| csharp_space_between_empty_square_brackets | N/A handled by formatter | +| csharp_space_between_method_call_empty_parameter_list_parentheses | N/A handled by formatter | +| csharp_space_between_method_call_name_and_opening_parenthesis | N/A handled by formatter | +| csharp_space_between_method_call_parameter_list_parentheses | N/A handled by formatter | +| csharp_space_between_method_declaration_empty_parameter_list_parentheses | N/A handled by formatter | +| csharp_space_between_method_declaration_name_and_open_parenthesis | N/A handled by formatter | +| csharp_space_between_method_declaration_parameter_list_parentheses | N/A handled by formatter | +| csharp_space_between_parentheses | N/A handled by formatter | +| csharp_space_between_square_brackets | N/A handled by formatter | +| csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental | IDE2004 | +| csharp_style_allow_blank_lines_between_consecutive_braces_experimental | IDE2002 | +| csharp_style_allow_embedded_statements_on_same_line_experimental | IDE2001 | +| csharp_style_conditional_delegate_call | IDE1005 | +| csharp_style_deconstructed_variable_declaration | IDE0042 | +| csharp_style_expression_bodied_accessors | IDE0027 | +| csharp_style_expression_bodied_constructors | IDE0021 | +| csharp_style_expression_bodied_indexers | IDE0026 | +| csharp_style_expression_bodied_lambdas | IDE0053 | +| csharp_style_expression_bodied_local_functions | IDE0061 | +| csharp_style_expression_bodied_methods | IDE0022 | +| csharp_style_expression_bodied_operators | IDE0023/IDE0024 | +| csharp_style_expression_bodied_properties | IDE0025 | +| csharp_style_implicit_object_creation_when_type_is_apparent | IDE0090 | +| csharp_style_inlined_variable_declaration | IDE0018 | +| csharp_style_namespace_declarations | IDE0161 | +| csharp_style_pattern_matching_over_as_with_null_check | IDE0019 | +| csharp_style_pattern_matching_over_is_with_cast_check | IDE0020 | +| csharp_style_prefer_extended_property_pattern | IDE0170 | +| csharp_style_prefer_index_operator | IDE0056 | +| csharp_style_prefer_local_over_anonymous_function | IDE0039 | +| csharp_style_prefer_method_group_conversion | IDE0200 | +| csharp_style_prefer_not_pattern | IDE0083 | +| csharp_style_prefer_null_check_over_type_check | IDE0150 | +| csharp_style_prefer_null_check_over_type_check | IDE0150 | +| csharp_style_prefer_parameter_null_checking | IDE0190 | +| csharp_style_prefer_pattern_matching | IDE0078 | +| csharp_style_prefer_range_operator | IDE0057 | +| csharp_style_prefer_switch_expression | IDE0066 | +| csharp_style_prefer_tuple_swap | IDE0180 | +| csharp_style_throw_expression | IDE0016 | +| csharp_style_unused_value_assignment_preference | IDE0059 | +| csharp_style_unused_value_expression_statement_preference | IDE0058 | +| csharp_style_var_elsewhere | IDE0007/IDE0008 | +| csharp_style_var_for_built_in_types | IDE0007/IDE0008 | +| csharp_style_var_when_type_is_apparent | IDE0007/IDE0008 | +| csharp_using_directive_placement | IDE0065 | +| file_header_template | IDE0073 | +| visual_basic_preferred_modifier_order | IDE0036 | +| visual_basic_style_prefer_isnot_expression | IDE0084 | +| visual_basic_style_prefer_simplified_object_creation | IDE0140 | +| visual_basic_style_unused_value_assignment_preference | IDE0059 | +| visual_basic_style_unused_value_expression_statement_preference | IDE0058 | \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 2dd33442b401e..3933f403ce6b6 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -34,6 +34,7 @@ + diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb index ae3042b746689..093121fbdf8d6 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb @@ -5,12 +5,14 @@ Imports System.ComponentModel.Composition Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedVariable Imports Microsoft.VisualStudio.Language.CodeCleanUp Imports Microsoft.VisualStudio.Utilities Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService Friend NotInheritable Class VisualBasicCodeCleanUpFixerDiagnosticIds + @@ -19,5 +21,23 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService Public Shared ReadOnly BC42024 As FixIdDefinition + + + + + + + + + Public Shared ReadOnly SimplifyObjectCreationDiagnosticId As FixIdDefinition + + + + + + + + + Public Shared ReadOnly UseIsNotExpressionDiagnosticId As FixIdDefinition End Class End Namespace From 820d728837a6714cde419c61bc0f23a189c15e7b Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Wed, 26 Jan 2022 22:53:35 -0800 Subject: [PATCH 051/187] add links to documentation --- .../CSharpCodeCleanupFixerDiagnosticIds.cs | 78 +++++++++---------- .../CommonCodeCleanUpFixerDiagnosticIds.cs | 70 ++++++++--------- ...isualBasicCodeCleanupFixerDiagnosticIds.vb | 6 +- 3 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs index 8a8a5f9d12e86..6b525f81053a2 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs @@ -20,7 +20,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://docs.microsoft.com/dotnet/csharp/misc/cs0168")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] public static readonly FixIdDefinition? CS0168; @@ -29,7 +29,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0219)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://docs.microsoft.com/dotnet/csharp/misc/cs0168")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] public static readonly FixIdDefinition? CS0219; @@ -38,7 +38,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddBracesDiagnosticId)] [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddBracesDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements))] public static readonly FixIdDefinition? AddBracesDiagnosticId; @@ -47,7 +47,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://www.microsoft.com")] // Experimental features, not documented [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_blank_lines_between_consecutive_braces_preferences_experimental))] public static readonly FixIdDefinition? ConsecutiveBracePlacementDiagnosticId; @@ -56,7 +56,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ConstructorInitializerPlacementDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://www.microsoft.com")] // Experimental features, not documented [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_blank_line_after_colon_in_constructor_initializer_preferences_experimental))] public static readonly FixIdDefinition? ConstructorInitializerPlacementDiagnosticId; @@ -65,7 +65,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ConvertSwitchStatementToExpressionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? ConvertSwitchStatementToExpressionDiagnosticId; @@ -74,7 +74,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.EmbeddedStatementPlacementDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://www.microsoft.com")] // Experimental features, not documented [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_embedded_statements_on_same_line_preferences_experimental))] public static readonly FixIdDefinition? EmbeddedStatementPlacementDiagnosticId; @@ -83,7 +83,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InlineAsTypeCheckId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineAsTypeCheckId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? InlineAsTypeCheckId; @@ -92,7 +92,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] [Order(After = IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineDeclarationDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_inline_out_variable_preferences))] public static readonly FixIdDefinition? InlineDeclarationDiagnosticId; @@ -101,7 +101,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InlineIsTypeCheckId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineIsTypeCheckId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? InlineIsTypeCheckId; @@ -110,7 +110,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InvokeDelegateWithConditionalAccessId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_conditional_delegate_call_preferences))] public static readonly FixIdDefinition? InvokeDelegateWithConditionalAccessId; @@ -119,7 +119,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_static_local_function_preferences))] public static readonly FixIdDefinition? MakeLocalFunctionStaticDiagnosticId; @@ -128,7 +128,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryLambdaExpressionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_method_group_conversion_preferences))] public static readonly FixIdDefinition? RemoveUnnecessaryLambdaExpressionDiagnosticId; @@ -137,7 +137,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyPropertyPatternDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? SimplifyPropertyPatternDiagnosticId; @@ -146,7 +146,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseDeconstructionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseDeconstructionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_deconstruct_preferences))] public static readonly FixIdDefinition? UseDeconstructionDiagnosticId; @@ -155,7 +155,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseDefaultLiteralDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseDefaultLiteralDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_default_T_preferences))] public static readonly FixIdDefinition? UseDefaultLiteralDiagnosticId; @@ -164,7 +164,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTypeDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] public static readonly FixIdDefinition? UseExplicitTypeDiagnosticId; @@ -173,7 +173,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForAccessorsDiagnosticId; @@ -182,7 +182,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForConstructorsDiagnosticId; @@ -191,7 +191,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForConversionOperatorsDiagnosticId; @@ -200,7 +200,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForIndexersDiagnosticId; @@ -209,7 +209,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForLambdaExpressionsDiagnosticId; @@ -218,7 +218,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForLocalFunctionsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForLocalFunctionsDiagnosticId; @@ -227,7 +227,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForMethodsDiagnosticId; @@ -236,7 +236,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForOperatorsDiagnosticId; @@ -245,7 +245,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId)] [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForPropertiesDiagnosticId; @@ -254,7 +254,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_namespace_preferences))] public static readonly FixIdDefinition? UseFileScopedNamespaceDiagnosticId; @@ -263,7 +263,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitObjectCreationDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_new_preferences))] public static readonly FixIdDefinition? UseImplicitObjectCreationDiagnosticId; @@ -272,7 +272,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitTypeDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] public static readonly FixIdDefinition? UseImplicitTypeDiagnosticId; @@ -281,7 +281,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseIndexOperatorDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseIndexOperatorDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_range_preferences))] public static readonly FixIdDefinition? UseIndexOperatorDiagnosticId; @@ -290,7 +290,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseLocalFunctionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseLocalFunctionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_local_over_anonymous_function_preferences))] public static readonly FixIdDefinition? UseLocalFunctionDiagnosticId; @@ -299,7 +299,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseNotPatternDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNotPatternDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? UseNotPatternDiagnosticId; @@ -308,7 +308,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNullCheckOverTypeCheckDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? UseNullCheckOverTypeCheckDiagnosticId; @@ -317,7 +317,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseParameterNullCheckingId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseParameterNullCheckingId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_parameter_null_preferences))] public static readonly FixIdDefinition? UseParameterNullCheckingId; @@ -326,7 +326,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UsePatternCombinatorsDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? UsePatternCombinatorsDiagnosticId; @@ -335,7 +335,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseRangeOperatorDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseRangeOperatorDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_range_preferences))] public static readonly FixIdDefinition? UseRangeOperatorDiagnosticId; @@ -344,7 +344,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseSimpleUsingStatementDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_using_statement_preferences))] public static readonly FixIdDefinition? UseSimpleUsingStatementDiagnosticId; @@ -353,7 +353,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseThrowExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseThrowExpressionDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_throw_expression_preferences))] public static readonly FixIdDefinition? UseThrowExpressionDiagnosticId; @@ -362,7 +362,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseTupleSwapDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseTupleSwapDiagnosticId}")] [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_deconstruct_preferences))] public static readonly FixIdDefinition? UseTupleSwapDiagnosticId; } diff --git a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs index 9de146c370701..27c57ac6ec39b 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs @@ -17,7 +17,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] [Order(After = AbstractCodeCleanUpFixer.FormatDocumentFixId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(ServicesVSResources), nameof(FeaturesResources.Remove_unnecessary_Imports_or_usings))] public static readonly FixIdDefinition? RemoveUnusedImports; @@ -27,7 +27,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(AbstractCodeCleanUpFixer.SortImportsFixId)] [Order(After = AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(ServicesVSResources), nameof(FeaturesResources.Sort_Imports_or_usings))] public static readonly FixIdDefinition? SortImports; @@ -37,7 +37,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddQualificationDiagnosticId)] [Order(After = IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddQualificationDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? AddQualificationDiagnosticId; @@ -46,7 +46,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_using_directive_placement_preferences))] public static readonly FixIdDefinition? MoveMisplacedUsingDirectivesDiagnosticId; @@ -55,7 +55,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveQualificationDiagnosticId)] [Order(After = IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveQualificationDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? RemoveQualificationDiagnosticId; @@ -63,7 +63,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [FixId(AbstractCodeCleanUpFixer.FormatDocumentFixId)] [Name(AbstractCodeCleanUpFixer.FormatDocumentFixId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-formatting")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(ServicesVSResources), nameof(ServicesVSResources.Format_document))] public static readonly FixIdDefinition? FormatDocument; @@ -73,7 +73,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] [Order(After = IDEDiagnosticIds.AddBracesDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_accessibility_modifiers))] public static readonly FixIdDefinition? AddAccessibilityModifiersDiagnosticId; @@ -82,7 +82,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_parentheses_preferences))] public static readonly FixIdDefinition? AddRequiredParenthesesDiagnosticId; @@ -91,7 +91,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://www.microsoft.com")] // Experimental features, not documented [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_statement_after_block_preferences_experimental))] public static readonly FixIdDefinition? ConsecutiveStatementPlacementDiagnosticId; @@ -100,7 +100,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ExpressionValueIsUnusedDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] public static readonly FixIdDefinition? ExpressionValueIsUnusedDiagnosticId; @@ -109,7 +109,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.FileHeaderMismatch)] [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.FileHeaderMismatch}")] [ExportMetadata("EnableByDefault", true)] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_file_header_preferences))] public static readonly FixIdDefinition? FileHeaderMismatch; @@ -119,7 +119,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] [Order(After = IDEDiagnosticIds.AddQualificationDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Make_field_readonly))] public static readonly FixIdDefinition? MakeFieldReadonlyDiagnosticId; @@ -128,7 +128,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://www.microsoft.com")] // Features not documented tracked by https://github.com/dotnet/roslyn/issues/59103 [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_namespace_matches_folder_preferences))] public static readonly FixIdDefinition? MatchFolderAndNamespaceDiagnosticId; @@ -137,7 +137,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.MultipleBlankLinesDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink("https://www.microsoft.com")] // Experimental features, not documented [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_blank_line_preferences_experimental))] public static readonly FixIdDefinition? MultipleBlankLinesDiagnosticId; @@ -146,7 +146,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.OrderModifiersDiagnosticId}")] [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] public static readonly FixIdDefinition? OrderModifiersDiagnosticId; @@ -155,7 +155,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_language_framework_type_preferences))] public static readonly FixIdDefinition? PreferBuiltInOrFrameworkTypeDiagnosticId; @@ -164,7 +164,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [Order(After = IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unnecessary_casts))] public static readonly FixIdDefinition? RemoveUnnecessaryCastDiagnosticId; @@ -173,7 +173,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_parentheses_preferences))] public static readonly FixIdDefinition? RemoveUnnecessaryParenthesesDiagnosticId; @@ -182,7 +182,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessarySuppressionDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_suppressions))] public static readonly FixIdDefinition? RemoveUnnecessarySuppressionDiagnosticId; @@ -191,7 +191,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyConditionalExpressionDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_simplify_boolean_expression_preferences))] public static readonly FixIdDefinition? SimplifyConditionalExpressionDiagnosticId; @@ -200,7 +200,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.SimplifyInterpolationId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.SimplifyInterpolationId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_string_interpolation_preferences))] public static readonly FixIdDefinition? SimplifyInterpolationId; @@ -209,7 +209,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UnusedParameterDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UnusedParameterDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_parameters))] public static readonly FixIdDefinition? UnusedParameterDiagnosticId; @@ -218,7 +218,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseAutoPropertyDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseAutoPropertyDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_auto_property_preferences))] public static readonly FixIdDefinition? UseAutoPropertyDiagnosticId; @@ -227,7 +227,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_compound_assignment_preferences))] public static readonly FixIdDefinition? UseCoalesceCompoundAssignmentDiagnosticId; @@ -236,7 +236,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCoalesceExpressionDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_coalesce_expression_preferences))] public static readonly FixIdDefinition? UseCoalesceExpressionDiagnosticId; @@ -245,7 +245,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCollectionInitializerDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] public static readonly FixIdDefinition? UseCollectionInitializerDiagnosticId; @@ -254,7 +254,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_compound_assignment_preferences))] public static readonly FixIdDefinition? UseCompoundAssignmentDiagnosticId; @@ -263,7 +263,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseConditionalExpressionForAssignmentDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_conditional_expression_preferences))] public static readonly FixIdDefinition? UseConditionalExpressionForAssignmentDiagnosticId; @@ -272,7 +272,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseConditionalExpressionForReturnDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_conditional_expression_preferences))] public static readonly FixIdDefinition? UseConditionalExpressionForReturnDiagnosticId; @@ -281,7 +281,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTupleNameDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_tuple_name_preferences))] public static readonly FixIdDefinition? UseExplicitTupleNameDiagnosticId; @@ -290,7 +290,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseInferredMemberNameDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseInferredMemberNameDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_inferred_anonymous_type_member_names_preferences))] public static readonly FixIdDefinition? UseInferredMemberNameDiagnosticId; @@ -299,7 +299,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseIsNullCheckDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseIsNullCheckDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_checking_preferences))] public static readonly FixIdDefinition? UseIsNullCheckDiagnosticId; @@ -308,7 +308,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseNullPropagationDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseNullPropagationDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_propagation_preferences))] public static readonly FixIdDefinition? UseNullPropagationDiagnosticId; @@ -317,7 +317,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseObjectInitializerDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] public static readonly FixIdDefinition? UseObjectInitializerDiagnosticId; @@ -326,7 +326,7 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [Name(IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId)] [Order(After = IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] [ConfigurationKey("unused")] - [HelpLink("https://www.microsoft.com")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId}")] [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] public static readonly FixIdDefinition? ValueAssignedIsUnusedDiagnosticId; } diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb index 093121fbdf8d6..998cb17b5628a 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb @@ -18,7 +18,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly BC42024 As FixIdDefinition @@ -27,7 +27,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly SimplifyObjectCreationDiagnosticId As FixIdDefinition @@ -36,7 +36,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly UseIsNotExpressionDiagnosticId As FixIdDefinition End Class From 96e445e62f208a86f7ec637a0a30335b71e4c59a Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Thu, 10 Feb 2022 20:34:44 -0800 Subject: [PATCH 052/187] re-baseline existing tests --- .../CSharpTest/Formatting/CodeCleanupTests.cs | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index 5fb6fb0351e9b..e552b36e78ce9 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -81,7 +81,7 @@ internal class Program { private static void Main(string[] args) { - List list = new List(); + List list = new(); Console.WriteLine(list.Count); } } @@ -104,6 +104,7 @@ static async Task Main(string[] args) Barrier b = new Barrier(0); var list = new List(); Console.WriteLine(list.Count); + b.Dispose(); } } "; @@ -117,9 +118,10 @@ internal class Program { private static async Task Main(string[] args) { - Barrier b = new Barrier(0); - List list = new List(); + Barrier b = new(0); + List list = new(); Console.WriteLine(list.Count); + b.Dispose(); } } "; @@ -159,7 +161,7 @@ private static void Main(string[] args) { Console.WriteLine(""Hello World!""); - new Goo(); + _ = new Goo(); } } @@ -204,7 +206,7 @@ private static void Main(string[] args) { Console.WriteLine(""Hello World!""); - new Goo(); + _ = new Goo(); } } @@ -222,23 +224,27 @@ public Task FixAddRemoveBraces() { var code = @"class Program { - void Method() + int Method() { int a = 0; if (a > 0) a ++; + + return a; } } "; var expected = @"internal class Program { - private void Method() + private int Method() { int a = 0; if (a > 0) { a++; } + + return a; } } "; @@ -421,6 +427,7 @@ private void Method() { Console.WriteLine(); List list = new List(); + Console.WriteLine(list.Length); } } } @@ -436,7 +443,8 @@ internal class Program private void Method() { Console.WriteLine(); - List list = new List(); + List list = new(); + Console.WriteLine(list.Length); } } } @@ -460,7 +468,8 @@ internal class Program private void Method() { Console.WriteLine(); - List list = new List(); + List list = new(); + Console.WriteLine(list.Length); } } } @@ -477,7 +486,8 @@ internal class Program private void Method() { Console.WriteLine(); - List list = new List(); + List list = new(); + Console.WriteLine(list.Length); } } } @@ -501,7 +511,8 @@ internal class Program private void Method() { Console.WriteLine(); - List list = new List(); + List list = new(); + Console.WriteLine(list.Length); } } } @@ -527,7 +538,8 @@ internal class Program private void Method() { Console.WriteLine(); - List list = new List(); + List list = new(); + Console.WriteLine(list.Length); } } } From 9737ccd2866a0be7a71bc1841ea5751892e6f6f5 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Fri, 11 Feb 2022 18:19:40 -0800 Subject: [PATCH 053/187] fix feature name to not be experiemental --- .../Portable/CodeCleanup/VisualBasicCodeCleanupService.vb | 2 +- src/Features/VisualBasic/Portable/VBFeaturesResources.resx | 4 ++-- .../VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf | 6 +++--- .../VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf | 6 +++--- .../Portable/xlf/VBFeaturesResources.zh-Hans.xlf | 6 +++--- .../Portable/xlf/VBFeaturesResources.zh-Hant.xlf | 6 +++--- .../VisualBasicCodeCleanupFixerDiagnosticIds.vb | 2 +- 16 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb b/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb index fa43004da5cd8..c4baa2d7f6718 100644 --- a/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb +++ b/src/Features/VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb @@ -69,7 +69,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeCleanup IDEDiagnosticIds.ConsecutiveStatementPlacementDiagnosticId), New DiagnosticSet(FeaturesResources.Sort_accessibility_modifiers, IDEDiagnosticIds.OrderModifiersDiagnosticId), - New DiagnosticSet(VBFeaturesResources.Apply_isnot_preferences_experimental, + New DiagnosticSet(VBFeaturesResources.Apply_isnot_preferences, IDEDiagnosticIds.UseIsNotExpressionDiagnosticId), New DiagnosticSet(VBFeaturesResources.Apply_object_creation_preferences, IDEDiagnosticIds.SimplifyObjectCreationDiagnosticId), diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx index 29aff9ce70e6a..1dcbc463afa9d 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx @@ -1257,8 +1257,8 @@ Sub(<parameterList>) <statement> Shared constructor - - Apply IsNot preferences (experimental) + + Apply IsNot preferences Apply object creation preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf index 0b56576d7baae..d2e2a67c7f45f 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf @@ -37,9 +37,9 @@ Použít předvolby pro kvalifikaci Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf index 770942630d39c..ad3347120f439 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf @@ -37,9 +37,9 @@ Me-Qualifizierungseinstellungen anwenden {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf index a7b2c02ff9976..07a349d500ac5 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf @@ -37,9 +37,9 @@ Aplicar las preferencias de calificación Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf index 0c025cbef5c45..a78971dde3458 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf @@ -37,9 +37,9 @@ Appliquer les préférences de qualification pour Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf index 06a70cce26a21..b4e6165ea2dd0 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf @@ -37,9 +37,9 @@ Applica le preferenze relative alla qualificazione Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf index 5303af000a386..5ed260cb3630e 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf @@ -37,9 +37,9 @@ Me 修飾設定を適用する {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf index 8c9f838cdf0c0..40af68794a182 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf @@ -37,9 +37,9 @@ Me 한정 기본 설정 적용 {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf index 4bf8529761df7..39329a5c74db4 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf @@ -37,9 +37,9 @@ Zastosuj preferencje kwalifikacji Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf index 81e6b11a7bcfc..960458d1bff70 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf @@ -37,9 +37,9 @@ Aplicar as preferências de qualificação de Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf index 1ec1977607771..7dbd767442ed9 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf @@ -37,9 +37,9 @@ Применить параметры квалификации Me {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf index eb0f14620b149..268ada03192eb 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf @@ -37,9 +37,9 @@ Me yeterlilik tercihlerini uygula {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf index dbfcce27d9448..7a4acca09dff7 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf @@ -37,9 +37,9 @@ 应用 Me 资格首选项 {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf index a3b4019742479..a7e38ee503a33 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf @@ -37,9 +37,9 @@ 套用 Me 資格喜好設定 {Locked="Me"} "Me" is a VB keyword and should not be localized. - - Apply IsNot preferences (experimental) - Apply IsNot preferences (experimental) + + Apply IsNot preferences + Apply IsNot preferences diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb index 998cb17b5628a..6f3d771d61461 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb @@ -37,7 +37,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.LanguageService - + Public Shared ReadOnly UseIsNotExpressionDiagnosticId As FixIdDefinition End Class End Namespace From f50e8a77b968323a138993530dc3be54594e8aba Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Fri, 11 Feb 2022 16:29:13 -0800 Subject: [PATCH 054/187] Update src/Features/Core/Portable/CodeCleanup/readme.md Co-authored-by: Youssef Victor --- src/Features/Core/Portable/CodeCleanup/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/CodeCleanup/readme.md b/src/Features/Core/Portable/CodeCleanup/readme.md index 8a8a39c3dbc49..fcfc8ccf5a064 100644 --- a/src/Features/Core/Portable/CodeCleanup/readme.md +++ b/src/Features/Core/Portable/CodeCleanup/readme.md @@ -7,7 +7,7 @@ - [AbstractCodeCleanupService](AbstractCodeCleanupService.cs) methods common to both C# and VB implementations of [ICodeCleanupService](ICodeCleanupService.cs) - [DiagnosticSet](DiagnosticSet.cs) type to group diagnostics that should all be fixed together (all block style settings for example) used by [AbstractCodeCleanupService](AbstractCodeCleanupService.cs) - [EnabledDiagnosticOptions](EnabledDiagnosticOptions.cs) represents the total set of things the code cleanup service should attempt to fix. This is needed (instead of just using [DiagnosticSet](DiagnosticSet.cs)) because items like whitespace formatting and organize-usings do not use analyzers - - [OrganizeUsingsSet](OrganizeUsingsSet.cs) a type that packages the relevant organize-usings settings as boolean flags + - [OrganizeUsingsSet](OrganizeUsingsSettings.cs) a type that packages the relevant organize-usings settings as boolean flags - Types Defined by Visual Studio that Roslyn implements - [ICodeCleanUpFixerProvider](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixerprovider) a factory that creates the [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) we want to use. - [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) the interface we need to implement if we want Visual Studio to work for our language. From a6e05ba8bfcc19b1fb46583fdca7cbd391a6aa91 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Fri, 11 Feb 2022 18:51:16 -0800 Subject: [PATCH 055/187] add steps for updating the code cleanup service --- .../Core/Portable/CodeCleanup/readme.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/Features/Core/Portable/CodeCleanup/readme.md b/src/Features/Core/Portable/CodeCleanup/readme.md index fcfc8ccf5a064..a99a29e0bbd00 100644 --- a/src/Features/Core/Portable/CodeCleanup/readme.md +++ b/src/Features/Core/Portable/CodeCleanup/readme.md @@ -29,3 +29,42 @@ The implementations for [AbstractCodeCleanupService](AbstractCodeCleanupService. The Code Cleanup language service is then consumed by our [ICodeCleanUpFixer](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.icodecleanupfixer) implementations: [CSharpCodeCleanUpFixer](../../../../VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixer.cs) and [VisualBasicCodeCleanUpFixer](../../../../VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixer.vb) (with nearly all the logic for them living in [AbstractCodeCleanUpFixer](../../../../VisualStudio/Core/Def/Implementation/CodeCleanup/AbstractCodeCleanUpFixer.cs)). In order for any of our codefixes to appear in the code cleanup UI in Visual Studio we need to export a [FixIdAttribute](https://docs.microsoft.com/dotnet/api/microsoft.visualstudio.language.codecleanup.fixidattribute) on a field so Visual Studio can compose the set of diagnostic ids we care about. The common diagnostic ids that are used in both Visual Basic and C# are defined in [CommonCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs). C# specific diagnostic ids are exported in [CSharpCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs) and Visual Basic specific diagnostic ids are exported in [VisualBasicCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb). + +## Adding a new codefix to the code cleanup service + +1. Add the Diagnostic Id to the CodeCleanupService + 1. If the code fixer should work for C# add it to [CSharpCodeCleanupService](../../../CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs#L22) + 1. If it logically fits in with an existing `DiagnosticSet` add the new diagnostic Id there + 1. If there are no `DiagnosticSet`s that make sense add a new `DiagnosticSet` to the `s_diagnosticSets` `ImmutableArray`. + 1. If the codefix is used across both languages add a new resource string to [`FeaturesResources`](../FeaturesResources.resx) with a description for your `DiagnosticSet` + 1. If it only apples to a single language add a new resource string to [`CSharpFeaturesResources`](../../../CSharp/Portable/CSharpFeaturesResources.resx)with a description for your `DiagnosticSet` + 1. If the code fixer should work for Visual Basic add it to [VisualBasicCodeCleanupService](../../../VisualBasic/Portable/CodeCleanup/VisualBasicCodeCleanupService.vb#L21) + 1. If it logically fits in with an existing `DiagnosticSet` add the new diagnostic Id there + 1. If there are no `DiagnosticSet`s that make sense add a new `DiagnosticSet` to the `s_diagnosticSets` `ImmutableArray`. + 1. If the codefix is used across both languages add a new resource string to [`FeaturesResources`](../FeaturesResources.resx) with a description for your `DiagnosticSet` + 1. If it only apples to a single language add a new resource string to [`VBFeaturesResources`](../../../VisualBasic/Portable/VBFeaturesResources.resx)with a description for your `DiagnosticSet` +1. Export the `FixIdDefinition` for Visual Studio to show + 1. If the code fixer works for both Visual Basic and C# add a new `public` `static` field of type `FixIdDefinition` to [CommonCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs) + 1. Add the following attributes to the field + 1. `[Export]` + 1. `[FixId(IDEDiagnosticIds.NewDiagnosticIdThatYouAdded)]` + 1. `[Name(IDEDiagnosticIds.NewDiagnosticIdThatYouAdded)]` + 1. `[ConfigurationKey("unused")]` + 1. If the feature is not experimental add the help link url `[HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.NewDiagnosticIdThatYouAdded}")]` + 1. `[LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.string_you_added_in_1_a_ii_i))]` + 1. If the code fixer only in C# add a new `public` `static` field of type `FixIdDefinition` to [CSharpCodeCleanupFixerDiagnosticIds](../../../../VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs) + 1. Add the following attributes to the field + 1. `[Export]` + 1. `[FixId(IDEDiagnosticIds.NewDiagnosticIdThatYouAdded)]` + 1. `[Name(IDEDiagnosticIds.NewDiagnosticIdThatYouAdded)]` + 1. `[ConfigurationKey("unused")]` + 1. If the feature is not experimental add the help link url `[HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.NewDiagnosticIdThatYouAdded}")]` + 1. `[LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.string_you_added_in_1_a_ii_ii))]` + 1. If the code fixer only in Visual Basic add a new `Public` `Shared` field of type `FixIdDefinition` to [VisualBasicCodeCleanUpFixerDiagnosticIds](../../../../VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodeCleanupFixerDiagnosticIds.vb) + 1. Add the following attributes to the field + 1. `` + 1. `` + 1. `` + 1. `` + 1. If the feature is not experimental add the help link url `` + 1. `` From 1092d75a4eb2d81c700d448be9a0852ec305d580 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Mon, 14 Feb 2022 12:00:12 -0800 Subject: [PATCH 056/187] assert the correct number of diagnostic Ids are returned --- .../CSharpTest/Formatting/CodeCleanupTests.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index e552b36e78ce9..d13b03d827391 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -5,6 +5,7 @@ #nullable disable using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; @@ -550,6 +551,51 @@ private void Method() return AssertCodeCleanupResult(expected, code, OutsidePreferPreservationOption); } + [Theory] + [Trait(Traits.Feature, Traits.Features.CodeCleanup)] + [InlineData(LanguageNames.CSharp, 32)] + [InlineData(LanguageNames.VisualBasic, 67)] + public void VerifyAllVisualBasicCodeStyleFixersAreSupportedByCodeCleanup(string language, int expectedNumberOfUnsupportedDiagnosticIds) + { + var supportedDiagnostics = GetSupportedDiagnosticIdsForCodeCleanupService(language); + + // No Duplicates + Assert.Equal(supportedDiagnostics, supportedDiagnostics.Distinct()); + + // Exact Number of Unsupported Diagnostic Ids + var ideDiagnosticIds = typeof(IDEDiagnosticIds).GetFields().Select(f => f.GetValue(f) as string).ToArray(); + var unsupportedDiagnosticIds = ideDiagnosticIds.Except(supportedDiagnostics).ToArray(); + Assert.Equal(expectedNumberOfUnsupportedDiagnosticIds, unsupportedDiagnosticIds.Length); + } + + private static string[] GetSupportedDiagnosticIdsForCodeCleanupService(string language) + { + using var workspace = GetTestWorkspaceForLanguage(language); + var hostdoc = workspace.Documents.Single(); + var document = workspace.CurrentSolution.GetDocument(hostdoc.Id); + + var codeCleanupService = document.GetLanguageService(); + + var enabledDiagnostics = codeCleanupService.GetAllDiagnostics(); + var supportedDiagnostics = enabledDiagnostics.Diagnostics.SelectMany(x => x.DiagnosticIds).ToArray(); + return supportedDiagnostics; + + TestWorkspace GetTestWorkspaceForLanguage(string language) + { + if (language == LanguageNames.CSharp) + { + return TestWorkspace.CreateCSharp(string.Empty, composition: EditorTestCompositions.EditorFeaturesWpf); + } + + if (language == LanguageNames.VisualBasic) + { + return TestWorkspace.CreateVisualBasic(string.Empty, composition: EditorTestCompositions.EditorFeaturesWpf); + } + + return null; + } + } + /// /// Assert the expected code value equals the actual processed input . /// From 454552faca6495201c82f5509edcddda009e2cf7 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Tue, 15 Feb 2022 10:17:45 -0800 Subject: [PATCH 057/187] do not sort list alphabetically instead append to the end --- .../CSharpCodeCleanupFixerDiagnosticIds.cs | 198 +++++++++--------- .../CommonCodeCleanUpFixerDiagnosticIds.cs | 180 ++++++++-------- 2 files changed, 189 insertions(+), 189 deletions(-) diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs index 6b525f81053a2..6e412876dd3cf 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs @@ -15,6 +15,105 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService { internal static class CSharpCodeCleanUpFixerDiagnosticIds { + [Export] + [FixId(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [Name(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitTypeDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] + public static readonly FixIdDefinition? UseImplicitTypeDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] + [Name(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] + [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTypeDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] + public static readonly FixIdDefinition? UseExplicitTypeDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.AddBracesDiagnosticId)] + [Name(IDEDiagnosticIds.AddBracesDiagnosticId)] + [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddBracesDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements))] + public static readonly FixIdDefinition? AddBracesDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForConstructorsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForMethodsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForConversionOperatorsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForOperatorsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForPropertiesDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForIndexersDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] + [Name(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] + [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] + public static readonly FixIdDefinition? UseExpressionBodyForAccessorsDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] + [Name(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] + [Order(After = IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineDeclarationDiagnosticId}")] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_inline_out_variable_preferences))] + public static readonly FixIdDefinition? InlineDeclarationDiagnosticId; + [Export] [FixId(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] [Name(CSharpRemoveUnusedVariableCodeFixProvider.CS0168)] @@ -33,15 +132,6 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unused_variables))] public static readonly FixIdDefinition? CS0219; - [Export] - [FixId(IDEDiagnosticIds.AddBracesDiagnosticId)] - [Name(IDEDiagnosticIds.AddBracesDiagnosticId)] - [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddBracesDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements))] - public static readonly FixIdDefinition? AddBracesDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId)] [Name(IDEDiagnosticIds.ConsecutiveBracePlacementDiagnosticId)] @@ -87,15 +177,6 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_pattern_matching_preferences))] public static readonly FixIdDefinition? InlineAsTypeCheckId; - [Export] - [FixId(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] - [Name(IDEDiagnosticIds.InlineDeclarationDiagnosticId)] - [Order(After = IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.InlineDeclarationDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_inline_out_variable_preferences))] - public static readonly FixIdDefinition? InlineDeclarationDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.InlineIsTypeCheckId)] [Name(IDEDiagnosticIds.InlineIsTypeCheckId)] @@ -159,51 +240,6 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_default_T_preferences))] public static readonly FixIdDefinition? UseDefaultLiteralDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] - [Name(IDEDiagnosticIds.UseExplicitTypeDiagnosticId)] - [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExplicitTypeDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] - public static readonly FixIdDefinition? UseExplicitTypeDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForAccessorsDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForConstructorsDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForConversionOperatorsDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForIndexersDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId)] [Name(IDEDiagnosticIds.UseExpressionBodyForLambdaExpressionsDiagnosticId)] @@ -222,33 +258,6 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] public static readonly FixIdDefinition? UseExpressionBodyForLocalFunctionsDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForMethodsDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForOperatorsDiagnosticId; - - [Export] - [FixId(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId)] - [Name(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId)] - [Order(After = IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_expression_block_body_preferences))] - public static readonly FixIdDefinition? UseExpressionBodyForPropertiesDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId)] [Name(IDEDiagnosticIds.UseFileScopedNamespaceDiagnosticId)] @@ -267,15 +276,6 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_new_preferences))] public static readonly FixIdDefinition? UseImplicitObjectCreationDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] - [Name(IDEDiagnosticIds.UseImplicitTypeDiagnosticId)] - [Order(After = IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseImplicitTypeDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Apply_var_preferences))] - public static readonly FixIdDefinition? UseImplicitTypeDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.UseIndexOperatorDiagnosticId)] [Name(IDEDiagnosticIds.UseIndexOperatorDiagnosticId)] diff --git a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs index 27c57ac6ec39b..94a8ee91a4d4a 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/CommonCodeCleanUpFixerDiagnosticIds.cs @@ -12,26 +12,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeCleanup { internal static class CommonCodeCleanUpFixerDiagnosticIds { - [Export] - [FixId(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] - [Name(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] - [Order(After = AbstractCodeCleanUpFixer.FormatDocumentFixId)] - [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(ServicesVSResources), nameof(FeaturesResources.Remove_unnecessary_Imports_or_usings))] - public static readonly FixIdDefinition? RemoveUnusedImports; - - [Export] - [FixId(AbstractCodeCleanUpFixer.SortImportsFixId)] - [Name(AbstractCodeCleanUpFixer.SortImportsFixId)] - [Order(After = AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] - [ConfigurationKey("unused")] - [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(ServicesVSResources), nameof(FeaturesResources.Sort_Imports_or_usings))] - public static readonly FixIdDefinition? SortImports; - [Export] [FixId(IDEDiagnosticIds.AddQualificationDiagnosticId)] [Name(IDEDiagnosticIds.AddQualificationDiagnosticId)] @@ -41,15 +21,6 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? AddQualificationDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] - [Name(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] - [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId}")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_using_directive_placement_preferences))] - public static readonly FixIdDefinition? MoveMisplacedUsingDirectivesDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.RemoveQualificationDiagnosticId)] [Name(IDEDiagnosticIds.RemoveQualificationDiagnosticId)] @@ -59,6 +30,60 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_this_or_Me_qualification))] public static readonly FixIdDefinition? RemoveQualificationDiagnosticId; + [Export] + [FixId(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [Name(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [Order(After = IDEDiagnosticIds.AddBracesDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId}")] + [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_accessibility_modifiers))] + public static readonly FixIdDefinition? AddAccessibilityModifiersDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] + [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.OrderModifiersDiagnosticId}")] + [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] + public static readonly FixIdDefinition? OrderModifiersDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] + [Name(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] + [Order(After = IDEDiagnosticIds.AddQualificationDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId}")] + [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Make_field_readonly))] + public static readonly FixIdDefinition? MakeFieldReadonlyDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [Name(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] + [Order(After = IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId}")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unnecessary_casts))] + public static readonly FixIdDefinition? RemoveUnnecessaryCastDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] + [Name(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] + [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseObjectInitializerDiagnosticId}")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] + public static readonly FixIdDefinition? UseObjectInitializerDiagnosticId; + + [Export] + [FixId(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] + [Name(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] + [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCollectionInitializerDiagnosticId}")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] + public static readonly FixIdDefinition? UseCollectionInitializerDiagnosticId; + [Export] [FixId(AbstractCodeCleanUpFixer.FormatDocumentFixId)] [Name(AbstractCodeCleanUpFixer.FormatDocumentFixId)] @@ -69,13 +94,43 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds public static readonly FixIdDefinition? FormatDocument; [Export] - [FixId(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] - [Name(IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] - [Order(After = IDEDiagnosticIds.AddBracesDiagnosticId)] + [FixId(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [Name(AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [Order(After = AbstractCodeCleanUpFixer.FormatDocumentFixId)] [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId}")] - [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Add_accessibility_modifiers))] - public static readonly FixIdDefinition? AddAccessibilityModifiersDiagnosticId; + [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unnecessary_Imports_or_usings))] + public static readonly FixIdDefinition? RemoveUnusedImports; + + [Export] + [FixId(AbstractCodeCleanUpFixer.SortImportsFixId)] + [Name(AbstractCodeCleanUpFixer.SortImportsFixId)] + [Order(After = AbstractCodeCleanUpFixer.RemoveUnusedImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink("https://docs.microsoft.com/visualstudio/ide/reference/options-text-editor-csharp-advanced#using-directives")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Sort_Imports_or_usings))] + public static readonly FixIdDefinition? SortImports; + + [Export] + [FixId(IDEDiagnosticIds.FileHeaderMismatch)] + [Name(IDEDiagnosticIds.FileHeaderMismatch)] + [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.FileHeaderMismatch}")] + [ExportMetadata("EnableByDefault", true)] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_file_header_preferences))] + public static readonly FixIdDefinition? FileHeaderMismatch; + + [Export] + [FixId(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] + [Name(IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId)] + [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] + [ConfigurationKey("unused")] + [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MoveMisplacedUsingDirectivesDiagnosticId}")] + [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_using_directive_placement_preferences))] + public static readonly FixIdDefinition? MoveMisplacedUsingDirectivesDiagnosticId; [Export] [FixId(IDEDiagnosticIds.AddRequiredParenthesesDiagnosticId)] @@ -104,25 +159,6 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_unused_value_preferences))] public static readonly FixIdDefinition? ExpressionValueIsUnusedDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.FileHeaderMismatch)] - [Name(IDEDiagnosticIds.FileHeaderMismatch)] - [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.FileHeaderMismatch}")] - [ExportMetadata("EnableByDefault", true)] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_file_header_preferences))] - public static readonly FixIdDefinition? FileHeaderMismatch; - - [Export] - [FixId(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] - [Name(IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] - [Order(After = IDEDiagnosticIds.AddQualificationDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId}")] - [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Make_field_readonly))] - public static readonly FixIdDefinition? MakeFieldReadonlyDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId)] [Name(IDEDiagnosticIds.MatchFolderAndNamespaceDiagnosticId)] @@ -141,15 +177,6 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_blank_line_preferences_experimental))] public static readonly FixIdDefinition? MultipleBlankLinesDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [Name(IDEDiagnosticIds.OrderModifiersDiagnosticId)] - [Order(After = IDEDiagnosticIds.AddAccessibilityModifiersDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.OrderModifiersDiagnosticId}")] - [LocalizedName(typeof(AnalyzersResources), nameof(AnalyzersResources.Order_modifiers))] - public static readonly FixIdDefinition? OrderModifiersDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] [Name(IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] @@ -159,15 +186,6 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_language_framework_type_preferences))] public static readonly FixIdDefinition? PreferBuiltInOrFrameworkTypeDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] - [Name(IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId)] - [Order(After = IDEDiagnosticIds.MakeFieldReadonlyDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.RemoveUnnecessaryCastDiagnosticId}")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Remove_unnecessary_casts))] - public static readonly FixIdDefinition? RemoveUnnecessaryCastDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId)] [Name(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId)] @@ -240,15 +258,6 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_coalesce_expression_preferences))] public static readonly FixIdDefinition? UseCoalesceExpressionDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] - [Name(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId)] - [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseCollectionInitializerDiagnosticId}")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] - public static readonly FixIdDefinition? UseCollectionInitializerDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId)] [Name(IDEDiagnosticIds.UseCompoundAssignmentDiagnosticId)] @@ -312,15 +321,6 @@ internal static class CommonCodeCleanUpFixerDiagnosticIds [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_null_propagation_preferences))] public static readonly FixIdDefinition? UseNullPropagationDiagnosticId; - [Export] - [FixId(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] - [Name(IDEDiagnosticIds.UseObjectInitializerDiagnosticId)] - [Order(After = IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId)] - [ConfigurationKey("unused")] - [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.UseObjectInitializerDiagnosticId}")] - [LocalizedName(typeof(FeaturesResources), nameof(FeaturesResources.Apply_object_collection_initialization_preferences))] - public static readonly FixIdDefinition? UseObjectInitializerDiagnosticId; - [Export] [FixId(IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId)] [Name(IDEDiagnosticIds.ValueAssignedIsUnusedDiagnosticId)] From 0b272b53573ccccabd6567bfa7c5b07ae06df748 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Tue, 15 Feb 2022 16:46:27 -0800 Subject: [PATCH 058/187] Fix --- .../IntelliSense/AsyncCompletion/CompletionSource.cs | 2 +- .../AsyncCompletion/ItemManager.CompletionListUpdater.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 87eec35acbdcb..440ef2eb4ac92 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -487,7 +487,7 @@ private static void UpdateSessionData(CompletionSessionData sessionData, Complet if (item is null) throw new ArgumentNullException(nameof(item)); - if (!CompletionItemData.TryGetData(item, out var itemData) && !itemData.IsProvidedByRoslynCompletionSource) + if (!CompletionItemData.TryGetData(item, out var itemData) || !itemData.IsProvidedByRoslynCompletionSource) return null; Contract.ThrowIfNull(itemData.TriggerLocation); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index 3967d663d4584..27427bb85f36b 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -73,7 +73,7 @@ public CompletionListUpdater( _applicableToSpan = applicableToSpan; _filterText = applicableToSpan.GetText(_snapshotData.Snapshot); - _hasSuggestedItemOptions = sessionData.HasSuggestionItemOptions || _snapshotData.DisplaySuggestionItem; + _hasSuggestedItemOptions = _sessionData.HasSuggestionItemOptions || _snapshotData.DisplaySuggestionItem; // We prefer using the original snapshot, which should always be available from items provided by Roslyn's CompletionSource. // Only use data.Snapshot in the theoretically possible but rare case when all items we are handling are from some non-Roslyn CompletionSource. From fdaf97dc75e3f8bfd36db2f3da37117474d4a598 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 15 Feb 2022 20:04:09 -0800 Subject: [PATCH 059/187] Make method async and remove reliance on IWorkspaceThreadingService --- .../Diagnostics/DiagnosticAnalyzerService.cs | 6 +----- ...nosticAnalyzerService_IncrementalAnalyzer.cs | 2 +- .../EngineV2/DiagnosticIncrementalAnalyzer.cs | 6 +----- ...icIncrementalAnalyzer_IncrementalAnalyzer.cs | 17 +++++------------ 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs index dfff67117aba0..0167fa8aa2ab9 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -35,7 +34,6 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService public IAsynchronousOperationListener Listener { get; } - private readonly IWorkspaceThreadingService? _workspaceThreadingService; private readonly ConditionalWeakTable _map; private readonly ConditionalWeakTable.CreateValueCallback _createIncrementalAnalyzer; @@ -43,13 +41,11 @@ internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public DiagnosticAnalyzerService( IDiagnosticUpdateSourceRegistrationService registrationService, - IAsynchronousOperationListenerProvider listenerProvider, - [Import(AllowDefault = true)] IWorkspaceThreadingService? workspaceThreadingService) + IAsynchronousOperationListenerProvider listenerProvider) { AnalyzerInfoCache = new DiagnosticAnalyzerInfoCache(); Listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService); - _workspaceThreadingService = workspaceThreadingService; _map = new ConditionalWeakTable(); _createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback; _eventMap = new EventMap(); diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs index b0cb7ab3ff507..2267bf8724f05 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerService_IncrementalAnalyzer.cs @@ -34,7 +34,7 @@ private DiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Workspac // subscribe to active context changed event for new workspace workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged; - return new DiagnosticIncrementalAnalyzer(this, LogAggregator.GetNextId(), workspace, AnalyzerInfoCache, _workspaceThreadingService); + return new DiagnosticIncrementalAnalyzer(this, LogAggregator.GetNextId(), workspace, AnalyzerInfoCache); } private void OnDocumentActiveContextChanged(object? sender, DocumentActiveContextChangedEventArgs e) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs index 452bb3809cb45..7d30d7e4fba0d 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Workspaces.Diagnostics; @@ -34,7 +33,6 @@ internal partial class DiagnosticIncrementalAnalyzer : IIncrementalAnalyzer2 private readonly StateManager _stateManager; private readonly InProcOrRemoteHostAnalyzerRunner _diagnosticAnalyzerRunner; private readonly IDocumentTrackingService _documentTrackingService; - private readonly IWorkspaceThreadingService? _workspaceThreadingService; private ConditionalWeakTable _projectCompilationsWithAnalyzers; internal DiagnosticAnalyzerService AnalyzerService { get; } @@ -45,14 +43,12 @@ public DiagnosticIncrementalAnalyzer( DiagnosticAnalyzerService analyzerService, int correlationId, Workspace workspace, - DiagnosticAnalyzerInfoCache analyzerInfoCache, - IWorkspaceThreadingService? workspaceThreadingService) + DiagnosticAnalyzerInfoCache analyzerInfoCache) { Contract.ThrowIfNull(analyzerService); AnalyzerService = analyzerService; Workspace = workspace; - _workspaceThreadingService = workspaceThreadingService; _documentTrackingService = workspace.Services.GetRequiredService(); _correlationId = correlationId; diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs index 91a7e16a8b31a..26ab6be283489 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer_IncrementalAnalyzer.cs @@ -444,7 +444,7 @@ private void RaiseProjectDiagnosticsIfNeeded( return; } - AnalyzerService.RaiseBulkDiagnosticsUpdated(raiseEvents => + AnalyzerService.RaiseBulkDiagnosticsUpdated(async raiseEvents => { foreach (var stateSet in stateSets) { @@ -474,7 +474,7 @@ private void RaiseProjectDiagnosticsIfNeeded( if (oldAnalysisResult.IsEmpty && !newAnalysisResult.IsEmpty) { // add new diagnostics - RaiseProjectDiagnosticsCreated(project, stateSet, oldAnalysisResult, newAnalysisResult, raiseEvents); + await RaiseProjectDiagnosticsCreatedAsync(project, stateSet, oldAnalysisResult, newAnalysisResult, raiseEvents, CancellationToken.None).ConfigureAwait(false); continue; } @@ -487,7 +487,7 @@ private void RaiseProjectDiagnosticsIfNeeded( RaiseProjectDiagnosticsRemoved(stateSet, oldAnalysisResult.ProjectId, documentsToRemove, handleActiveFile: false, raiseEvents); // next update or create new ones - RaiseProjectDiagnosticsCreated(project, stateSet, oldAnalysisResult, newAnalysisResult, raiseEvents); + await RaiseProjectDiagnosticsCreatedAsync(project, stateSet, oldAnalysisResult, newAnalysisResult, raiseEvents, CancellationToken.None).ConfigureAwait(false); } }); } @@ -537,7 +537,7 @@ private void RaiseDocumentDiagnosticsIfNeeded( RaiseDiagnosticsCreated(document, stateSet, kind, newItems, raiseEvents); } - private void RaiseProjectDiagnosticsCreated(Project project, StateSet stateSet, DiagnosticAnalysisResult oldAnalysisResult, DiagnosticAnalysisResult newAnalysisResult, Action raiseEvents) + private async Task RaiseProjectDiagnosticsCreatedAsync(Project project, StateSet stateSet, DiagnosticAnalysisResult oldAnalysisResult, DiagnosticAnalysisResult newAnalysisResult, Action raiseEvents, CancellationToken cancellationToken) { RoslynDebug.Assert(newAnalysisResult.DocumentIds != null); @@ -550,14 +550,7 @@ private void RaiseProjectDiagnosticsCreated(Project project, StateSet stateSet, if (document is null && project.Solution.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true }) { - if (_workspaceThreadingService is not null) - { - document = _workspaceThreadingService.Run(() => project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).AsTask()); - } - else - { - document = project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).AsTask().WaitAndGetResult_CanCallOnBackground(CancellationToken.None); - } + document = await project.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); } if (document == null) From 20cb688c496f475ac606d311a43f6be6c1306c34 Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Tue, 15 Feb 2022 22:32:19 -0800 Subject: [PATCH 060/187] Handle nullability attributes in param-nullchecking (#59393) --- .../Portable/FlowAnalysis/NullableWalker.cs | 6 +- .../Symbols/Source/ParameterHelpers.cs | 34 +++- .../Semantics/NullCheckedParameterTests.cs | 174 +++++++++++++++++- 3 files changed, 201 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index d052ecd9e89cf..d3cb290a6dc5c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -2067,7 +2067,7 @@ internal static bool AreParameterAnnotationsCompatible( overriddenType, overriddenAnnotations, // We don't consider '!!' when deciding whether 'overridden' is compatible with 'override' - isNullChecked: false); + applyParameterNullCheck: false); if (isBadAssignment(valueState, overridingType, overridingAnnotations)) { return false; @@ -2527,9 +2527,9 @@ private void EnterParameter(ParameterSymbol parameter, TypeWithAnnotations param return null; } - internal static TypeWithState GetParameterState(TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, bool isNullChecked) + internal static TypeWithState GetParameterState(TypeWithAnnotations parameterType, FlowAnalysisAnnotations parameterAnnotations, bool applyParameterNullCheck) { - if (isNullChecked) + if (applyParameterNullCheck) { return TypeWithState.Create(parameterType.Type, NullableFlowState.NotNull); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 910cd4273f970..1b51643ada5cc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -809,15 +809,43 @@ internal static void ReportParameterNullCheckingErrors(DiagnosticBag diagnostics { diagnostics.Add(ErrorCode.ERR_DiscardCannotBeNullChecked, location); } - if (parameter.TypeWithAnnotations.NullableAnnotation.IsAnnotated() - || parameter.Type.IsNullableTypeOrTypeParameter()) + + var annotations = parameter.FlowAnalysisAnnotations; + if ((annotations & FlowAnalysisAnnotations.NotNull) == 0 + && NullableWalker.GetParameterState(parameter.TypeWithAnnotations, annotations, applyParameterNullCheck: false).State.MayBeNull() + && !isTypeParameterWithPossiblyNonNullableType(parameter.TypeWithAnnotations, annotations)) { diagnostics.Add(ErrorCode.WRN_NullCheckingOnNullableType, location, parameter); } - else if (parameter.Type.IsValueType && !parameter.Type.IsPointerOrFunctionPointer()) + + if (parameter.Type.IsNonNullableValueType() && !parameter.Type.IsPointerOrFunctionPointer()) { diagnostics.Add(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, location, parameter); } + + // For type parameters, we only want to give the warning if no type argument would result in a non-nullable type. + static bool isTypeParameterWithPossiblyNonNullableType(TypeWithAnnotations typeWithAnnotations, FlowAnalysisAnnotations annotations) + { + if (!typeWithAnnotations.Type.IsTypeParameter()) + { + return false; + } + + // We avoid checking the nullable annotations, etc. of constraints due to implementation complexity, + // and consider it acceptable to miss "!! on nullable type" warnings in scenarios like `void M(U u!!) where U : T?`. + if (typeWithAnnotations.NullableAnnotation.IsAnnotated()) + { + return false; + } + + // `void M([AllowNull] T t!!)` + if ((annotations & FlowAnalysisAnnotations.AllowNull) != 0) + { + return false; + } + + return true; + } } internal static ImmutableArray ConditionallyCreateInModifiers(RefKind refKind, bool addRefReadOnlyModifier, Binder binder, BindingDiagnosticBag diagnostics, SyntaxNode syntax) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullCheckedParameterTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullCheckedParameterTests.cs index 781d1b889736d..5b22538850a77 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullCheckedParameterTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullCheckedParameterTests.cs @@ -725,16 +725,13 @@ internal override void F(U u!! = default) { } } class B3 : A { - internal override void F(U u!! = default) { } + internal override void F(U u!! = default) { } // note: 'U' is a nullable type here but we don't give a warning due to complexity of accurately searching the constraints. }"; var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); compilation.VerifyDiagnostics( - // (12,35): warning CS8719: Parameter 'u' is null-checked but is null by default. - // internal override void F(U u!! = default) { } - Diagnostic(ErrorCode.WRN_NullCheckedHasDefaultNull, "u").WithArguments("u").WithLocation(12, 35), - // (16,35): warning CS8721: Nullable value type 'U' is null-checked and will throw if null. - // internal override void F(U u!! = default) { } - Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "u").WithArguments("U").WithLocation(16, 35)); + // (12,35): warning CS8993: Parameter 'u' is null-checked but is null by default. + // internal override void F(U u!! = default) { } + Diagnostic(ErrorCode.WRN_NullCheckedHasDefaultNull, "u").WithArguments("u").WithLocation(12, 35)); } [Fact] @@ -1034,6 +1031,9 @@ static void M4([DisallowNull] T x3, [DisallowNull] T y3!!) // (16,9): warning CS8602: Dereference of a possibly null reference. // x1.ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(16, 9), + // (19,55): warning CS8995: Nullable type 'T' is null-checked and will throw if null. + // static void M3([AllowNull] T x2, [AllowNull] T y2!!) + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "y2").WithArguments("T").WithLocation(19, 55), // (21,9): warning CS8602: Dereference of a possibly null reference. // x2.ToString(); // 4 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x2").WithLocation(21, 9)); @@ -1070,5 +1070,165 @@ public override void M2(string s) { } // 3 Diagnostic(ErrorCode.WRN_TopLevelNullabilityMismatchInParameterTypeOnOverride, "M2").WithArguments("s").WithLocation(13, 26) ); } + + [Fact] + public void TestNullabilityAttributes() + { + var source = +@" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +class C +{ + void M( + string s1!!, + [NotNull] string s2!!, + [DisallowNull] string s3!!, + [AllowNull] string s4!!, // 1 + [AllowNull, DisallowNull] string s5!!, // 2 + [AllowNull, NotNull] string s6!!, + + string? s7!!, // 3 + [NotNull] string? s8!!, // ok: this is a typical signature for an 'AssertNotNull' style method. + [DisallowNull] string? s9!!, + [AllowNull] string? s10!!, // 4 + [AllowNull, DisallowNull] string? s11!!, // 5 + [AllowNull, NotNull] string? s12!!, + + int i1!!, // 6 + [NotNull] int i2!!, // 7 + [DisallowNull] int i3!!, // 8 + [AllowNull] int i4!!, // 9 + [AllowNull, DisallowNull] int i5!!, // 10 + [AllowNull, NotNull] int i6!!, // 11 + + int? i7!!, // 12 + [NotNull] int? i8!!, + [DisallowNull] int? i9!!, + [AllowNull] int? i10!!, // 13 + [AllowNull, DisallowNull] int? i11!!, // 14 + [AllowNull, NotNull] int? i12!! + ) { } +}"; + var comp = CreateCompilation(new[] { source, AllowNullAttributeDefinition, DisallowNullAttributeDefinition, NotNullAttributeDefinition }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (11,28): warning CS8995: Nullable type 'string' is null-checked and will throw if null. + // [AllowNull] string s4!!, // 1 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "s4").WithArguments("string").WithLocation(11, 28), + // (12,42): warning CS8995: Nullable type 'string' is null-checked and will throw if null. + // [AllowNull, DisallowNull] string s5!!, // 2 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "s5").WithArguments("string").WithLocation(12, 42), + // (15,17): warning CS8995: Nullable type 'string?' is null-checked and will throw if null. + // string? s7!!, // 3 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "s7").WithArguments("string?").WithLocation(15, 17), + // (18,29): warning CS8995: Nullable type 'string?' is null-checked and will throw if null. + // [AllowNull] string? s10!!, // 4 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "s10").WithArguments("string?").WithLocation(18, 29), + // (19,43): warning CS8995: Nullable type 'string?' is null-checked and will throw if null. + // [AllowNull, DisallowNull] string? s11!!, // 5 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "s11").WithArguments("string?").WithLocation(19, 43), + // (22,13): error CS8992: Parameter 'int' is a non-nullable value type and cannot be null-checked. + // int i1!!, // 6 + Diagnostic(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, "i1").WithArguments("int").WithLocation(22, 13), + // (23,23): error CS8992: Parameter 'int' is a non-nullable value type and cannot be null-checked. + // [NotNull] int i2!!, // 7 + Diagnostic(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, "i2").WithArguments("int").WithLocation(23, 23), + // (24,28): error CS8992: Parameter 'int' is a non-nullable value type and cannot be null-checked. + // [DisallowNull] int i3!!, // 8 + Diagnostic(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, "i3").WithArguments("int").WithLocation(24, 28), + // (25,25): error CS8992: Parameter 'int' is a non-nullable value type and cannot be null-checked. + // [AllowNull] int i4!!, // 9 + Diagnostic(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, "i4").WithArguments("int").WithLocation(25, 25), + // (26,39): error CS8992: Parameter 'int' is a non-nullable value type and cannot be null-checked. + // [AllowNull, DisallowNull] int i5!!, // 10 + Diagnostic(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, "i5").WithArguments("int").WithLocation(26, 39), + // (27,34): error CS8992: Parameter 'int' is a non-nullable value type and cannot be null-checked. + // [AllowNull, NotNull] int i6!!, // 11 + Diagnostic(ErrorCode.ERR_NonNullableValueTypeIsNullChecked, "i6").WithArguments("int").WithLocation(27, 34), + // (29,14): warning CS8995: Nullable type 'int?' is null-checked and will throw if null. + // int? i7!!, // 12 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "i7").WithArguments("int?").WithLocation(29, 14), + // (32,26): warning CS8995: Nullable type 'int?' is null-checked and will throw if null. + // [AllowNull] int? i10!!, // 13 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "i10").WithArguments("int?").WithLocation(32, 26), + // (33,40): warning CS8995: Nullable type 'int?' is null-checked and will throw if null. + // [AllowNull, DisallowNull] int? i11!!, // 14 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "i11").WithArguments("int?").WithLocation(33, 40) + ); + } + + [Theory] + [InlineData("")] + [InlineData("where T : class")] + [InlineData("where T : class?")] + [InlineData("where T : notnull")] + public void TestNullabilityAttributes_Generic(string constraints) + { + var source = +@" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +class C +{ + void M( + T t1!!, + [NotNull] T t2!!, + [DisallowNull] T t3!!, + [AllowNull] T t4!!, // 1 + [AllowNull, DisallowNull] T t5!!, // 2 + [AllowNull, NotNull] T t6!!, + + T? t7!!, // 3 + [NotNull] T? t8!!, + [DisallowNull] T? t9!!, + [AllowNull] T? t10!!, // 4 + [AllowNull, DisallowNull] T? t11!!, // 5 + [AllowNull, NotNull] T? t12!! + ) " + constraints + @" { } +}"; + var comp = CreateCompilation(new[] { source, AllowNullAttributeDefinition, DisallowNullAttributeDefinition, NotNullAttributeDefinition }, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (11,23): warning CS8995: Nullable type 'T' is null-checked and will throw if null. + // [AllowNull] T t4!!, // 1 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "t4").WithArguments("T").WithLocation(11, 23), + // (12,37): warning CS8995: Nullable type 'T' is null-checked and will throw if null. + // [AllowNull, DisallowNull] T t5!!, // 2 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "t5").WithArguments("T").WithLocation(12, 37), + // (15,12): warning CS8995: Nullable type 'T?' is null-checked and will throw if null. + // T? t7!!, // 3 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "t7").WithArguments("T?").WithLocation(15, 12), + // (18,24): warning CS8995: Nullable type 'T?' is null-checked and will throw if null. + // [AllowNull] T? t10!!, // 4 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "t10").WithArguments("T?").WithLocation(18, 24), + // (19,38): warning CS8995: Nullable type 'T?' is null-checked and will throw if null. + // [AllowNull, DisallowNull] T? t11!! // 5 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "t11").WithArguments("T?").WithLocation(19, 38) + ); + } + + [Fact] + public void AnnotatedTypeParameter_Indirect() + { + var source = @" +#nullable enable + +class C +{ + void M( + T? t!!, // 1 + U u!!) where U : T? + { + } +}"; + // note: U is always nullable when a reference type, + // but we don't warn on '!!' for it due to complexity of accurately searching the constraints. + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,12): warning CS8995: Nullable type 'T?' is null-checked and will throw if null. + // T? t!!, // 1 + Diagnostic(ErrorCode.WRN_NullCheckingOnNullableType, "t").WithArguments("T?").WithLocation(7, 12)); + } } } From e096cf2ae64284d83bf1c6c797bc1eee3ab4e7a1 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 16 Feb 2022 10:29:34 -0800 Subject: [PATCH 061/187] Make AbstractDiagnosticsTaggerProvider.OnDiagnosticsUpdated handler asynchronous --- .../AbstractDiagnosticsTaggerProvider.cs | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs index 41bf1776b403d..f678f96324403 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -75,32 +75,38 @@ private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) return; } - var document = e.Solution.GetDocument(e.DocumentId); + var asyncToken = AsyncListener.BeginAsyncOperation(nameof(OnDiagnosticsUpdatedAsync)); + _ = OnDiagnosticsUpdatedAsync(e.Id, e.Workspace, e.Solution, e.DocumentId).CompletesAsyncOperation(asyncToken); - // If we couldn't find a normal document, and all features are enabled for source generated documents, - // attempt to locate a matching source generated document in the project. - if (document is null - && e.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true } - && e.Solution.GetProject(e.DocumentId.ProjectId) is { } project) + static async Task OnDiagnosticsUpdatedAsync(object updateGroupId, Workspace workspace, Solution solution, DocumentId documentId) { - document = ThreadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentAsync(e.DocumentId, CancellationToken.None).AsTask()); - } + var document = solution.GetDocument(documentId); - // Open documents *should* always have their SourceText available, but we cannot guarantee - // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's - // a small risk that between calling .IsOpen the file may then close, which then would - // cause TryGetText to fail. However, that's ok. In that case, if we do need to tag this - // document, we'll just use the current editor snapshot. If that's the same, then the tags - // will be hte same. If it is different, we'll eventually hear about the new diagnostics - // for it and we'll reach our fixed point. - if (document != null && document.IsOpen()) - { - // This should always be fast since the document is open. - var sourceText = document.State.GetTextSynchronously(cancellationToken: default); - snapshot = sourceText.FindCorrespondingEditorTextSnapshot(); - if (snapshot != null) + // If we couldn't find a normal document, and all features are enabled for source generated documents, + // attempt to locate a matching source generated document in the project. + if (document is null + && workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true } + && solution.GetProject(documentId.ProjectId) is { } project) { - _diagnosticIdToTextSnapshot.GetValue(e.Id, _ => snapshot); + document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); + } + + // Open documents *should* always have their SourceText available, but we cannot guarantee + // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's + // a small risk that between calling .IsOpen the file may then close, which then would + // cause TryGetText to fail. However, that's ok. In that case, if we do need to tag this + // document, we'll just use the current editor snapshot. If that's the same, then the tags + // will be hte same. If it is different, we'll eventually hear about the new diagnostics + // for it and we'll reach our fixed point. + if (document != null && document.IsOpen()) + { + // This should always be fast since the document is open. + var sourceText = document.State.GetTextSynchronously(cancellationToken: default); + var snapshot = sourceText.FindCorrespondingEditorTextSnapshot(); + if (snapshot != null) + { + _diagnosticIdToTextSnapshot.GetValue(updateGroupId, _ => snapshot); + } } } } From e96fb91c8380c1f05592ea6d48de828b8f6f84f5 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 10:38:28 -0800 Subject: [PATCH 062/187] Simplify fields and lambdas --- .../Core/Portable/CodeFixes/CodeFixService.cs | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index c28c4dcc1c907..7c755da5f6618 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -39,8 +39,9 @@ internal partial class CodeFixService : ICodeFixService new((d1, d2) => DiagnosticId.CompareOrdinal(d1.Id, d2.Id)); private readonly IDiagnosticAnalyzerService _diagnosticService; + private readonly ImmutableArray> _fixers; + private readonly Dictionary>> _fixersPerLanguageMap; - private readonly Func>>>> _getWorkspaceFixersMap; private ImmutableDictionary>>>? _lazyWorkspaceFixersMap; private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap; @@ -48,7 +49,6 @@ internal partial class CodeFixService : ICodeFixService private ImmutableDictionary> _fixerToFixableIdsMap = ImmutableDictionary>.Empty; private readonly Lazy> _lazyFixerToMetadataMap; - private readonly Func>>> _getFixerPriorityMap; private ImmutableDictionary>>? _lazyFixerPriorityMap; private readonly ConditionalWeakTable _analyzerReferenceToFixersMap; @@ -70,16 +70,14 @@ public CodeFixService( _errorLoggers = loggers; _diagnosticService = diagnosticAnalyzerService; + _fixers = fixers.ToImmutableArray(); + _fixersPerLanguageMap = _fixers.ToPerLanguageMapWithMultipleLanguages(); + _lazyFixerToMetadataMap = new(() => fixers.Where(service => service.IsValueCreated).ToImmutableDictionary(service => service.Value, service => service.Metadata)); - var fixersPerLanguageMap = fixers.ToPerLanguageMapWithMultipleLanguages(); var configurationProvidersPerLanguageMap = configurationProviders.ToPerLanguageMapWithMultipleLanguages(); - _getWorkspaceFixersMap = workspace => GetFixerPerLanguageMap(fixersPerLanguageMap, workspace); _configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProvidersPerLanguageMap); - // REVIEW: currently, fixer's priority is statically defined by the fixer itself. might considering making it more dynamic or configurable. - _getFixerPriorityMap = workspace => GetFixerPriorityPerLanguageMap(fixersPerLanguageMap, workspace); - // Per-project fixers _projectFixersMap = new ConditionalWeakTable, ImmutableDictionary>>(); _analyzerReferenceToFixersMap = new ConditionalWeakTable(); @@ -306,7 +304,7 @@ private bool TryGetWorkspaceFixersMap(Document document, [NotNullWhen(true)] out { if (_lazyWorkspaceFixersMap == null) { - var workspaceFixersMap = _getWorkspaceFixersMap(document.Project.Solution.Workspace); + var workspaceFixersMap = GetFixerPerLanguageMap(document.Project.Solution.Workspace); Interlocked.CompareExchange(ref _lazyWorkspaceFixersMap, workspaceFixersMap, null); } @@ -317,7 +315,7 @@ private bool TryGetWorkspaceFixersPriorityMap(Document document, [NotNullWhen(tr { if (_lazyFixerPriorityMap == null) { - var fixersPriorityByLanguageMap = _getFixerPriorityMap(document.Project.Solution.Workspace); + var fixersPriorityByLanguageMap = GetFixerPriorityPerLanguageMap(document.Project.Solution.Workspace); Interlocked.CompareExchange(ref _lazyFixerPriorityMap, fixersPriorityByLanguageMap, null); } @@ -846,18 +844,17 @@ private static ImmutableArray GetAndTestFixableDiagnosticIds(CodeFixProv } private ImmutableDictionary>>> GetFixerPerLanguageMap( - Dictionary>> fixersPerLanguage, Workspace workspace) { var fixerMap = ImmutableDictionary.Create>>>(); var extensionManager = workspace.Services.GetService(); - foreach (var languageKindAndFixers in fixersPerLanguage) + foreach (var (diagnosticId, lazyFixers) in _fixersPerLanguageMap) { var lazyMap = new Lazy>>(() => { var mutableMap = new Dictionary>(); - foreach (var lazyFixer in languageKindAndFixers.Value) + foreach (var lazyFixer in lazyFixers) { if (!TryGetWorkspaceFixer(lazyFixer, workspace, logExceptionWithInfoBar: true, out var fixer)) { @@ -885,7 +882,7 @@ private ImmutableDictionary GetConfigurationFixProviders(Li } } - private ImmutableDictionary>> GetFixerPriorityPerLanguageMap( - Dictionary>> fixersPerLanguage, - Workspace workspace) + private ImmutableDictionary>> GetFixerPriorityPerLanguageMap(Workspace workspace) { var languageMap = ImmutableDictionary.CreateBuilder>>(); - foreach (var languageAndFixers in fixersPerLanguage) + foreach (var (diagnosticId, lazyFixers) in _fixersPerLanguageMap) { var lazyMap = new Lazy>(() => { var priorityMap = ImmutableDictionary.CreateBuilder(); - var lazyFixers = ExtensionOrderer.Order(languageAndFixers.Value); - for (var i = 0; i < lazyFixers.Count; i++) + var fixers = ExtensionOrderer.Order(lazyFixers); + for (var i = 0; i < fixers.Count; i++) { if (!TryGetWorkspaceFixer(lazyFixers[i], workspace, logExceptionWithInfoBar: false, out var fixer)) { @@ -941,7 +936,7 @@ private ImmutableDictionary Date: Wed, 16 Feb 2022 10:46:05 -0800 Subject: [PATCH 063/187] Simplify further --- .../Core/Portable/CodeFixes/CodeFixService.cs | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 7c755da5f6618..12cdfeea51d91 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -42,22 +42,22 @@ internal partial class CodeFixService : ICodeFixService private readonly ImmutableArray> _fixers; private readonly Dictionary>> _fixersPerLanguageMap; - private ImmutableDictionary>>>? _lazyWorkspaceFixersMap; - private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap; + private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new(); // Shared by project fixers and workspace fixers. - private ImmutableDictionary> _fixerToFixableIdsMap = ImmutableDictionary>.Empty; private readonly Lazy> _lazyFixerToMetadataMap; - private ImmutableDictionary>>? _lazyFixerPriorityMap; - - private readonly ConditionalWeakTable _analyzerReferenceToFixersMap; - private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeFixProvider; + private readonly ConditionalWeakTable _analyzerReferenceToFixersMap = new(); + private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeFixProvider = r => new ProjectCodeFixProvider(r); private readonly ImmutableDictionary>> _configurationProvidersMap; private readonly IEnumerable> _errorLoggers; - private ImmutableDictionary _fixAllProviderMap; + private ImmutableDictionary>>>? _lazyWorkspaceFixersMap; + private ImmutableDictionary>>? _lazyFixerPriorityMap; + + private ImmutableDictionary> _fixerToFixableIdsMap = ImmutableDictionary>.Empty; + private ImmutableDictionary _fixAllProviderMap = ImmutableDictionary.Empty; [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] @@ -74,15 +74,8 @@ public CodeFixService( _fixersPerLanguageMap = _fixers.ToPerLanguageMapWithMultipleLanguages(); _lazyFixerToMetadataMap = new(() => fixers.Where(service => service.IsValueCreated).ToImmutableDictionary(service => service.Value, service => service.Metadata)); - var configurationProvidersPerLanguageMap = configurationProviders.ToPerLanguageMapWithMultipleLanguages(); - - _configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProvidersPerLanguageMap); - // Per-project fixers - _projectFixersMap = new ConditionalWeakTable, ImmutableDictionary>>(); - _analyzerReferenceToFixersMap = new ConditionalWeakTable(); - _createProjectCodeFixProvider = new ConditionalWeakTable.CreateValueCallback(r => new ProjectCodeFixProvider(r)); - _fixAllProviderMap = ImmutableDictionary.Empty; + _configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProviders); } private ImmutableDictionary FixerToMetadataMap => _lazyFixerToMetadataMap.Value; @@ -238,10 +231,10 @@ int GetValue(CodeFixCollection c) { // Ensure that we do not register duplicate configuration fixes. using var _ = PooledHashSet.GetInstance(out var registeredConfigurationFixTitles); - foreach (var spanAndDiagnostic in aggregatedDiagnostics) + foreach (var (span, diagnosticList) in aggregatedDiagnostics) { await AppendConfigurationsAsync( - document, spanAndDiagnostic.Key, spanAndDiagnostic.Value, + document, span, diagnosticList, result, registeredConfigurationFixTitles, cancellationToken).ConfigureAwait(false); } } @@ -874,9 +867,9 @@ private ImmutableDictionary>(); - foreach (var diagnosticIdAndFixers in mutableMap) + foreach (var (diagnosticId, fixers) in mutableMap) { - immutableMap.Add(diagnosticIdAndFixers.Key, diagnosticIdAndFixers.Value.AsImmutableOrEmpty()); + immutableMap.Add(diagnosticId, fixers.AsImmutableOrEmpty()); } return immutableMap.ToImmutable(); @@ -889,16 +882,18 @@ private ImmutableDictionary>> GetConfigurationProvidersPerLanguageMap( - Dictionary>> configurationProvidersPerLanguage) + IEnumerable> configurationProviders) { - var configurationFixerMap = ImmutableDictionary.Create>>(); - foreach (var languageKindAndFixers in configurationProvidersPerLanguage) + var configurationProvidersPerLanguageMap = configurationProviders.ToPerLanguageMapWithMultipleLanguages(); + + var configurationFixerMap = ImmutableDictionary.CreateBuilder>>(); + foreach (var (diagnosticId, lazyFixers) in configurationProvidersPerLanguageMap) { - var lazyConfigurationFixers = new Lazy>(() => GetConfigurationFixProviders(languageKindAndFixers.Value)); - configurationFixerMap = configurationFixerMap.Add(languageKindAndFixers.Key, lazyConfigurationFixers); + var lazyConfigurationFixers = new Lazy>(() => GetConfigurationFixProviders(lazyFixers)); + configurationFixerMap.Add(diagnosticId, lazyConfigurationFixers); } - return configurationFixerMap; + return configurationFixerMap.ToImmutable(); static ImmutableArray GetConfigurationFixProviders(List> languageKindAndFixers) { From 25a439f7783c81f45cc49e1c74b761d257cf16ee Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 10:50:52 -0800 Subject: [PATCH 064/187] Simplify further --- .../Core/Portable/CodeFixes/CodeFixService.cs | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 12cdfeea51d91..1c186eab3a7d8 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -46,12 +46,10 @@ internal partial class CodeFixService : ICodeFixService // Shared by project fixers and workspace fixers. private readonly Lazy> _lazyFixerToMetadataMap; - private readonly ConditionalWeakTable _analyzerReferenceToFixersMap = new(); private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeFixProvider = r => new ProjectCodeFixProvider(r); - private readonly ImmutableDictionary>> _configurationProvidersMap; - private readonly IEnumerable> _errorLoggers; + private readonly ImmutableArray> _errorLoggers; private ImmutableDictionary>>>? _lazyWorkspaceFixersMap; private ImmutableDictionary>>? _lazyFixerPriorityMap; @@ -67,8 +65,8 @@ public CodeFixService( [ImportMany] IEnumerable> fixers, [ImportMany] IEnumerable> configurationProviders) { - _errorLoggers = loggers; _diagnosticService = diagnosticAnalyzerService; + _errorLoggers = loggers.ToImmutableArray(); _fixers = fixers.ToImmutableArray(); _fixersPerLanguageMap = _fixers.ToPerLanguageMapWithMultipleLanguages(); @@ -162,7 +160,7 @@ public async Task> GetFixesAsync( // group diagnostics by their diagnostics span // invariant: later code gathers & runs CodeFixProviders for diagnostics with one identical diagnostics span (that gets set later as CodeFixCollection's TextSpan) // order diagnostics by span. - SortedDictionary>? aggregatedDiagnostics = null; + var aggregatedDiagnostics = new SortedDictionary>(); // For 'CodeActionPriorityRequest.Normal' or 'CodeActionPriorityRequest.Low', we do not compute suppression/configuration fixes, // those fixes have a dedicated request priority 'CodeActionPriorityRequest.Lowest'. @@ -187,11 +185,10 @@ public async Task> GetFixesAsync( cancellationToken.ThrowIfCancellationRequested(); - aggregatedDiagnostics ??= new SortedDictionary>(); aggregatedDiagnostics.GetOrAdd(diagnostic.GetTextSpan(), _ => new List()).Add(diagnostic); } - if (aggregatedDiagnostics == null) + if (aggregatedDiagnostics.Count == 0) return ImmutableArray.Empty; // Order diagnostics by DiagnosticId so the fixes are in a deterministic order. @@ -793,8 +790,6 @@ private bool IsInteractiveCodeFixProvider(CodeFixProvider provider) AddImport.AbstractAddImportCodeFixProvider; } - private static readonly Func> s_createList = _ => new List(); - private ImmutableArray GetFixableDiagnosticIds(CodeFixProvider fixer, IExtensionManager? extensionManager) { // If we are passed a null extension manager it means we do not have access to a document so there is nothing to @@ -861,7 +856,7 @@ private ImmutableDictionary new List()); list.Add(fixer); } } @@ -942,13 +937,14 @@ private ImmutableDictionary> GetProjectFixer // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive return project.Solution.Workspace.Kind == WorkspaceKind.Interactive ? ImmutableDictionary>.Empty - : _projectFixersMap.GetValue(project.AnalyzerReferences, pId => ComputeProjectFixers(project)); + : _projectFixersMap.GetValue(project.AnalyzerReferences, _ => ComputeProjectFixers(project)); } private ImmutableDictionary> ComputeProjectFixers(Project project) { var extensionManager = project.Solution.Workspace.Services.GetService(); - ImmutableDictionary>.Builder? builder = null; + + var builder = ImmutableDictionary.CreateBuilder>(); foreach (var reference in project.AnalyzerReferences) { var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); @@ -958,22 +954,14 @@ private ImmutableDictionary> ComputeProjectF foreach (var id in fixableIds) { if (string.IsNullOrWhiteSpace(id)) - { continue; - } - builder ??= ImmutableDictionary.CreateBuilder>(); - var list = builder.GetOrAdd(id, s_createList); + var list = builder.GetOrAdd(id, static _ => new List()); list.Add(fixer); } } } - if (builder == null) - { - return ImmutableDictionary>.Empty; - } - return builder.ToImmutable(); } } From b0a61d22104169bc0c0f31c390b52fd262502ec0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 11:35:41 -0800 Subject: [PATCH 065/187] Compute fixes by order of fixers, so we don't have to sort them afterwards --- .../Core/Portable/CodeFixes/CodeFixService.cs | 219 ++++++++++-------- 1 file changed, 126 insertions(+), 93 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 1c186eab3a7d8..b5ca56acd6252 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -42,7 +42,7 @@ internal partial class CodeFixService : ICodeFixService private readonly ImmutableArray> _fixers; private readonly Dictionary>> _fixersPerLanguageMap; - private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new(); + private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new(); // Shared by project fixers and workspace fixers. private readonly Lazy> _lazyFixerToMetadataMap; @@ -183,51 +183,33 @@ public async Task> GetFixesAsync( if (diagnostic.IsSuppressed) continue; - cancellationToken.ThrowIfCancellationRequested(); - - aggregatedDiagnostics.GetOrAdd(diagnostic.GetTextSpan(), _ => new List()).Add(diagnostic); + var list = aggregatedDiagnostics.GetOrAdd(diagnostic.GetTextSpan(), static _ => new List()); + list.Add(diagnostic); } if (aggregatedDiagnostics.Count == 0) return ImmutableArray.Empty; // Order diagnostics by DiagnosticId so the fixes are in a deterministic order. - foreach (var diagnosticsWithSpan in aggregatedDiagnostics.Values) - { - diagnosticsWithSpan.Sort(s_diagnosticDataComparisonById); - } + foreach (var (_, diagnosticList) in aggregatedDiagnostics) + diagnosticList.Sort(s_diagnosticDataComparisonById); // append fixes for all diagnostics with the same diagnostics span - using var resultDisposer = ArrayBuilder.GetInstance(out var result); + using var _1 = ArrayBuilder.GetInstance(out var result); // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. if (priority != CodeActionRequestPriority.Lowest) { - foreach (var spanAndDiagnostic in aggregatedDiagnostics) - { - await AppendFixesAsync( - document, spanAndDiagnostic.Key, spanAndDiagnostic.Value, fixAllForInSpan: false, - priority, options, result, addOperationScope, cancellationToken).ConfigureAwait(false); - } - } - - if (result.Count > 0 && TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) - { - // sort the result to the order defined by the fixers -#pragma warning disable IDE0007 // Use implicit type - Explicit type is need to suppress an incorrect nullable warning on dereferencing the map. - ImmutableDictionary priorityMap = fixersForLanguage.Value; -#pragma warning restore IDE0007 // Use implicit type - result.Sort((d1, d2) => GetValue(d1).CompareTo(GetValue(d2))); - - int GetValue(CodeFixCollection c) - => priorityMap.TryGetValue((CodeFixProvider)c.Provider, out var value) ? value : int.MaxValue; + await AppendFixesAsync( + document, aggregatedDiagnostics, fixAllForInSpan: false, + priority, options, result, addOperationScope, cancellationToken).ConfigureAwait(false); } // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive if (document.Project.Solution.Workspace.Kind != WorkspaceKind.Interactive && includeSuppressionFixes) { // Ensure that we do not register duplicate configuration fixes. - using var _ = PooledHashSet.GetInstance(out var registeredConfigurationFixTitles); + using var _2 = PooledHashSet.GetInstance(out var registeredConfigurationFixTitles); foreach (var (span, diagnosticList) in aggregatedDiagnostics) { await AppendConfigurationsAsync( @@ -254,16 +236,22 @@ Func GetFixableDiagnosticFilter(Document document) } } - public async Task GetDocumentFixAllForIdInSpanAsync(Document document, TextSpan range, string diagnosticId, CodeActionOptions options, CancellationToken cancellationToken) + public async Task GetDocumentFixAllForIdInSpanAsync( + Document document, TextSpan range, string diagnosticId, CodeActionOptions options, CancellationToken cancellationToken) { var diagnostics = (await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, diagnosticId, includeSuppressedDiagnostics: false, cancellationToken: cancellationToken).ConfigureAwait(false)).ToList(); if (diagnostics.Count == 0) - { return null; - } using var resultDisposer = ArrayBuilder.GetInstance(out var result); - await AppendFixesAsync(document, range, diagnostics, fixAllForInSpan: true, CodeActionRequestPriority.None, options, result, addOperationScope: _ => null, cancellationToken).ConfigureAwait(false); + var spanToDiagnostics = new SortedDictionary> + { + {range, diagnostics }, + }; + + await AppendFixesAsync( + document, spanToDiagnostics, fixAllForInSpan: true, CodeActionRequestPriority.None, + options, result, addOperationScope: static _ => null, cancellationToken).ConfigureAwait(false); // TODO: Just get the first fix for now until we have a way to config user's preferred fix // https://github.com/dotnet/roslyn/issues/27066 @@ -356,8 +344,7 @@ private bool TryGetWorkspaceFixer( private async Task AppendFixesAsync( Document document, - TextSpan span, - IEnumerable diagnostics, + SortedDictionary> spanToDiagnostics, bool fixAllForInSpan, CodeActionRequestPriority priority, CodeActionOptions options, @@ -371,40 +358,48 @@ private async Task AppendFixesAsync( var hasAnyProjectFixer = projectFixersMap.Any(); if (!hasAnySharedFixer && !hasAnyProjectFixer) - { return; - } - - var allFixers = new List(); // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive var isInteractive = document.Project.Solution.Workspace.Kind == WorkspaceKind.Interactive; // gather CodeFixProviders for all distinct diagnostics found for current span - foreach (var diagnosticId in diagnostics.Select(d => d.Id).Distinct()) - { - cancellationToken.ThrowIfCancellationRequested(); + using var _1 = ArrayBuilder.GetInstance(out var allFixers); + using var _2 = PooledDictionary diagnostics)>>.GetInstance(out var fixerToRangesAndDiagnostics); - // Prioritize NuGet based project code fixers over VSIX based workspace code fixers. - if (hasAnyProjectFixer && projectFixersMap.TryGetValue(diagnosticId, out var projectFixers)) + foreach (var (range, diagnostics) in spanToDiagnostics) + { + foreach (var diagnosticId in diagnostics.Select(d => d.Id).Distinct()) { - Debug.Assert(!isInteractive); - allFixers.AddRange(projectFixers); - } + cancellationToken.ThrowIfCancellationRequested(); - if (hasAnySharedFixer && fixerMap!.Value.TryGetValue(diagnosticId, out var workspaceFixers)) - { - if (isInteractive) + // Prioritize NuGet based project code fixers over VSIX based workspace code fixers. + if (hasAnyProjectFixer && projectFixersMap.TryGetValue(diagnosticId, out var projectFixers)) { - allFixers.AddRange(workspaceFixers.Where(IsInteractiveCodeFixProvider)); + Debug.Assert(!isInteractive); + AddAllFixers(projectFixers, range, diagnostics); } - else + + if (hasAnySharedFixer && fixerMap!.Value.TryGetValue(diagnosticId, out var workspaceFixers)) { - allFixers.AddRange(workspaceFixers); + if (isInteractive) + { + AddAllFixers(workspaceFixers.WhereAsArray(IsInteractiveCodeFixProvider), range, diagnostics); + } + else + { + AddAllFixers(workspaceFixers, range, diagnostics); + } } } } + allFixers.RemoveDuplicates(); + + // Now, sort the fixers so that the ones that are ordered before others get their chance to run first. + if (TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) + allFixers.Sort(new FixerComparer(fixersForLanguage.Value)); + var extensionManager = document.Project.Solution.Workspace.Services.GetService(); // Run each CodeFixProvider to gather individual CodeFixes for reported diagnostics. @@ -420,39 +415,42 @@ private async Task AppendFixesAsync( try { - foreach (var fixer in allFixers.Distinct()) + foreach (var fixer in allFixers) { cancellationToken.ThrowIfCancellationRequested(); if (priority != CodeActionRequestPriority.None && priority != fixer.RequestPriority) continue; - await AppendFixesOrConfigurationsAsync( - document, span, diagnostics, fixAllForInSpan, result, fixer, - hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), - getFixes: dxs => - { - var fixerName = fixer.GetType().Name; - FixerToMetadataMap.TryGetValue(fixer, out var fixerMetadata); - - using (addOperationScope(fixerName)) - using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixerName, cancellationToken)) + foreach (var (span, diagnostics) in fixerToRangesAndDiagnostics[fixer]) + { + await AppendFixesOrConfigurationsAsync( + document, span, diagnostics, fixAllForInSpan, result, fixer, + hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), + getFixes: dxs => { - if (fixAllForInSpan) - { - var primaryDiagnostic = dxs.First(); - return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, fixerMetadata, options, - ImmutableArray.Create(primaryDiagnostic), uniqueDiagosticToEquivalenceKeysMap, - diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); - } - else + var fixerName = fixer.GetType().Name; + FixerToMetadataMap.TryGetValue(fixer, out var fixerMetadata); + + using (addOperationScope(fixerName)) + using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixerName, cancellationToken)) { - return GetCodeFixesAsync(document, span, fixer, fixerMetadata, options, dxs, - uniqueDiagosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); + if (fixAllForInSpan) + { + var primaryDiagnostic = dxs.First(); + return GetCodeFixesAsync(document, primaryDiagnostic.Location.SourceSpan, fixer, fixerMetadata, options, + ImmutableArray.Create(primaryDiagnostic), uniqueDiagosticToEquivalenceKeysMap, + diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); + } + else + { + return GetCodeFixesAsync(document, span, fixer, fixerMetadata, options, dxs, + uniqueDiagosticToEquivalenceKeysMap, diagnosticAndEquivalenceKeyToFixersMap, cancellationToken); + } } - } - }, - cancellationToken: cancellationToken).ConfigureAwait(false); + }, + cancellationToken).ConfigureAwait(false); + } // Just need the first result if we are doing fix all in span if (fixAllForInSpan && result.Any()) @@ -466,6 +464,18 @@ await AppendFixesOrConfigurationsAsync( pooledSet.Free(); } } + + return; + + void AddAllFixers( + ImmutableArray fixers, + TextSpan range, + List diagnostics) + { + allFixers.AddRange(fixers); + foreach (var fixer in fixers) + fixerToRangesAndDiagnostics.GetOrAdd(fixer, static _ => new()).Add((range, diagnostics)); + } } private static async Task> GetCodeFixesAsync( @@ -611,7 +621,8 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) } // If the fix provider supports fix all occurrences, then get the corresponding FixAllProviderInfo and fix all context. - var fixAllProviderInfo = extensionManager.PerformFunction(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null); + var fixAllProviderInfo = extensionManager.PerformFunction( + fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null); FixAllState? fixAllState = null; var supportedScopes = ImmutableArray.Empty; @@ -620,8 +631,8 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) var codeFixProvider = (fixer as CodeFixProvider) ?? new WrapperCodeFixProvider((IConfigurationFixProvider)fixer, diagnostics.Select(d => d.Id)); var diagnosticIds = diagnostics.Where(fixAllProviderInfo.CanBeFixed) - .Select(d => d.Id) - .ToImmutableHashSet(); + .Select(d => d.Id) + .ToImmutableHashSet(); // When computing FixAll for unnecessary pragma suppression diagnostic, // we need to include suppressed diagnostics, as well as reported compiler and analyzer diagnostics. @@ -932,37 +943,59 @@ private ImmutableDictionary> GetProjectFixers(Project project) + private ImmutableDictionary> GetProjectFixers(Project project) { // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive return project.Solution.Workspace.Kind == WorkspaceKind.Interactive - ? ImmutableDictionary>.Empty + ? ImmutableDictionary>.Empty : _projectFixersMap.GetValue(project.AnalyzerReferences, _ => ComputeProjectFixers(project)); } - private ImmutableDictionary> ComputeProjectFixers(Project project) + private ImmutableDictionary> ComputeProjectFixers(Project project) { var extensionManager = project.Solution.Workspace.Services.GetService(); - var builder = ImmutableDictionary.CreateBuilder>(); - foreach (var reference in project.AnalyzerReferences) + using var _ = PooledDictionary>.GetInstance(out var builder); + try { - var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); - foreach (var fixer in projectCodeFixerProvider.GetExtensions(project.Language)) + foreach (var reference in project.AnalyzerReferences) { - var fixableIds = this.GetFixableDiagnosticIds(fixer, extensionManager); - foreach (var id in fixableIds) + var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); + foreach (var fixer in projectCodeFixerProvider.GetExtensions(project.Language)) { - if (string.IsNullOrWhiteSpace(id)) - continue; + var fixableIds = this.GetFixableDiagnosticIds(fixer, extensionManager); + foreach (var id in fixableIds) + { + if (string.IsNullOrWhiteSpace(id)) + continue; - var list = builder.GetOrAdd(id, static _ => new List()); - list.Add(fixer); + var list = builder.GetOrAdd(id, static _ => ArrayBuilder.GetInstance()); + list.Add(fixer); + } } } + + return builder.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutable()); } + finally + { + foreach (var kvp in builder) + kvp.Value.Free(); + } + } + + private sealed class FixerComparer : IComparer + { + private readonly ImmutableDictionary _priorityMap; + + public FixerComparer(ImmutableDictionary priorityMap) + => _priorityMap = priorityMap; + + public int Compare([AllowNull] CodeFixProvider x, [AllowNull] CodeFixProvider y) + => GetValue(x).CompareTo(GetValue(y)); - return builder.ToImmutable(); + private int GetValue(CodeFixProvider provider) + => _priorityMap.TryGetValue(provider, out var value) ? value : int.MaxValue; } } } From f0a81c843c3078b2584e989aa32ca3fadd8c56a9 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 16 Feb 2022 11:46:06 -0800 Subject: [PATCH 066/187] List-patterns: report error when no suitable length member (#59475) --- .../CSharp/Portable/Binder/Binder_Patterns.cs | 21 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../IOperationTests_IIsPatternExpression.cs | 3 + .../PatternMatchingTests_ListPatterns.cs | 308 ++++++++++++++++++ 18 files changed, 393 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 4075977e98ce0..9b3082b53180d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -289,7 +289,12 @@ private BoundListPattern BindListPattern( BoundListPatternReceiverPlaceholder? receiverPlaceholder; BoundListPatternIndexPlaceholder? argumentPlaceholder; - if (inputType.IsErrorType()) + if (inputType.IsDynamic()) + { + Error(diagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); + } + + if (inputType.IsErrorType() || inputType.IsDynamic()) { hasErrors = true; elementType = inputType; @@ -337,13 +342,9 @@ private bool IsCountableAndIndexable(SyntaxNode node, TypeSymbol inputType, out private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inputType, uint inputValEscape, BindingDiagnosticBag diagnostics, out BoundExpression indexerAccess, out BoundExpression lengthAccess, out BoundListPatternReceiverPlaceholder? receiverPlaceholder, out BoundListPatternIndexPlaceholder argumentPlaceholder) { - bool hasErrors = false; - if (inputType.IsDynamic()) - { - hasErrors |= true; - Error(diagnostics, ErrorCode.ERR_UnsupportedTypeForListPattern, node, inputType); - } + Debug.Assert(!inputType.IsDynamic()); + bool hasErrors = false; receiverPlaceholder = new BoundListPatternReceiverPlaceholder(node, GetValEscape(inputType, inputValEscape), inputType) { WasCompilerGenerated = true }; if (inputType.IsSZArray()) { @@ -359,7 +360,11 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu } else { - hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, diagnostics); + if (!TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, diagnostics)) + { + hasErrors = true; + Error(diagnostics, ErrorCode.ERR_ListPatternRequiresLength, node, inputType); + } } var analyzedArguments = AnalyzedArguments.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5fd068b866369..27dc5ef9d0477 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6760,6 +6760,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ List patterns may not be used for a value of type '{0}'. + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + Slice patterns may not be used for a value of type '{0}'. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f891efdf9ae55..9e700b7a7ffab 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2020,6 +2020,7 @@ internal enum ErrorCode ERR_StructHasInitializersAndNoDeclaredConstructor = 8983, ERR_EncUpdateFailedDelegateTypeChanged = 8984, + ERR_ListPatternRequiresLength = 8985, ERR_DiscardCannotBeNullChecked = 8990, ERR_MustNullCheckInImplementation = 8991, ERR_NonNullableValueTypeIsNullChecked = 8992, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index cf8a63c0ca222..9c336908ebfc4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -807,6 +807,11 @@ Hodnota direktivy #line chybí nebo je mimo rozsah. + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Žádná přetížená metoda {0} neodpovídá ukazateli na funkci {1}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index f26a9dabd49f8..934438d76aadf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -807,6 +807,11 @@ Der Wert der #line-Anweisung fehlt oder liegt außerhalb des gültigen Bereichs. + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Keine Überladung für "{0}" stimmt mit dem Funktionszeiger "{1}" überein. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index bf487453d1132..2bd8e9edd9051 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -807,6 +807,11 @@ Falta el valor de directiva #line o está fuera del rango + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Ninguna sobrecarga correspondiente a "{0}" coincide con el puntero de función "{1}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 42694a4856397..39aa3091023dd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -807,6 +807,11 @@ La valeur de directive #line est manquante ou hors limites + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Aucune surcharge pour '{0}' ne correspond au pointeur de fonction '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 970507838682e..74f6a27c71f0e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -807,6 +807,11 @@ Il valore della direttiva #line manca oppure non è compreso nell'intervallo + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Nessun overload per '{0}' corrisponde al puntatore a funzione '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 8e4920c4ec36e..1509c49c2c691 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -807,6 +807,11 @@ #line ディレクティブの値が見つからないか、範囲外です + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' 関数ポインター '{1}' に一致する '{0}' のオーバーロードはありません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 02c6a1afa275d..48281d4c3f598 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -807,6 +807,11 @@ #line 지시문 값이 없거나 범위를 벗어났습니다. + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' 함수 포인터 '{1}'과(와) 일치하는 '{0}'에 대한 오버로드가 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 0b54d90f80b4f..088f9743b49da 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -807,6 +807,11 @@ Brak wartości dyrektywy #line lub jest ona poza zakresem + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Żadne z przeciążeń dla elementu „{0}” nie pasuje do wskaźnika funkcji „{1}” diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 5d7a3f8be564c..bdf7271904d24 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -807,6 +807,11 @@ O valor da diretiva de #line está ausente ou fora do intervalo + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Nenhuma sobrecarga de '{0}' corresponde ao ponteiro de função '{1}' diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 5e4da7accecbc..81f2ee366e999 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -807,6 +807,11 @@ Значение директивы #line отсутствует или находится за пределами допустимого диапазона + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' Нет перегруженного метода для "{0}", который соответствует указателю на функцию "{1}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 96705aed73f17..09d21e1a96de5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -807,6 +807,11 @@ #line yönerge değeri eksik veya aralık dışı + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' '{0}' için aşırı yüklemelerin hiçbiri '{1}' işlev işaretçisiyle eşleşmiyor diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index d9a0d08a783b2..0649d83ec8cea 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -807,6 +807,11 @@ #line 指令值缺失或超出范围 + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' “{0}”没有与函数指针“{1}”匹配的重载 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 551b2ed6161a4..a575eaa5beaac 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -807,6 +807,11 @@ #Line 指示詞值遺漏或超出範圍 + + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + List patterns may not be used for a value of type '{0}'. No suitable 'Length' or 'Count' property was found. + + No overload for '{0}' matches function pointer '{1}' '{0}' 沒有任何多載符合函式指標 '{1}' diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 79b3158d0aff7..6fe2b772abfe0 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -2319,6 +2319,9 @@ void M() "; var expectedDiagnostics = new[] { + // (8,31): error CS8985: List patterns may not be used for a value of type 'X'. No suitable 'Length' or 'Count' property was found. + // _ = /**/this is []/**/; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("X").WithLocation(8, 31), // (8,31): error CS0518: Predefined type 'System.Index' is not defined or imported // _ = /**/this is []/**/; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Index").WithLocation(8, 31) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 9daac7b509bb6..f5d0698de3da1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -927,6 +927,9 @@ void M(object o) "; var expectedDiagnostics = new[] { + // error CS8985: List patterns may not be used for a value of type 'object'. No suitable 'Length' or 'Count' property was found. + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, listPattern).WithArguments("object"), + // error CS0021: Cannot apply indexing with [] to an expression of type 'object' Diagnostic(ErrorCode.ERR_BadIndexLHS, listPattern).WithArguments("object"), @@ -3674,6 +3677,9 @@ public void M() // (4,17): error CS0547: 'C.Length': property or indexer cannot have void type // public void Length => throw null; Diagnostic(ErrorCode.ERR_PropertyCantHaveVoidType, "Length").WithArguments("C.Length").WithLocation(4, 17), + // (8,21): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = this is [1]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[1]").WithArguments("C").WithLocation(8, 21), // (8,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = this is [1]; Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "System.Index", "int").WithLocation(8, 21), @@ -3701,6 +3707,9 @@ public void M() "; var compilation = CreateCompilation(new[] { source, TestSources.Index }); compilation.VerifyEmitDiagnostics( + // (9,21): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = this is [1]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[1]").WithArguments("C").WithLocation(9, 21), // (9,21): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = this is [1]; Diagnostic(ErrorCode.ERR_BadArgType, "[1]").WithArguments("1", "System.Index", "int").WithLocation(9, 21), @@ -3710,6 +3719,30 @@ public void M() ); } + [Fact] + public void ListPattern_StringLength_SystemIndexIndexer() + { + var source = @" +class C +{ + public string Length => throw null; + public int this[System.Index i] => throw null; + + public void M() + { + _ = this is [1]; + _ = this[^1]; + } +} +"; + var compilation = CreateCompilation(new[] { source, TestSources.Index }); + compilation.VerifyEmitDiagnostics( + // (9,21): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = this is [1]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[1]").WithArguments("C").WithLocation(9, 21) + ); + } + [Fact] public void SlicePattern_VoidReturn() { @@ -4503,6 +4536,9 @@ public void M() "; var compilation = CreateCompilationWithIL(new[] { source, TestSources.Index, TestSources.Range }, il); compilation.VerifyEmitDiagnostics( + // (6,24): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = new C() is [var item, ..var rest]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), // (6,24): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), @@ -4727,6 +4763,9 @@ public void M() "; var compilation = CreateCompilationWithIL(source, il); compilation.VerifyEmitDiagnostics( + // (6,24): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = new C() is [var item, ..var rest]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), // (6,24): error CS0021: Cannot apply indexing with [] to an expression of type 'C' // _ = new C() is [var item, ..var rest]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var item, ..var rest]").WithArguments("C").WithLocation(6, 24), @@ -5954,6 +5993,9 @@ class C }"; var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( + // (4,5): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // [..] => 1, + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[..]").WithArguments("C").WithLocation(4, 5), // (4,5): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // [..] => 1, Diagnostic(ErrorCode.ERR_BadArgType, "[..]").WithArguments("1", "System.Index", "int").WithLocation(4, 5), @@ -5981,6 +6023,9 @@ class C }"; var comp = CreateCompilation(new[] { src, TestSources.Index }); comp.VerifyEmitDiagnostics( + // (4,5): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // [..] => 1, + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[..]").WithArguments("C").WithLocation(4, 5), // (4,5): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // [..] => 1, Diagnostic(ErrorCode.ERR_BadArgType, "[..]").WithArguments("1", "System.Index", "int").WithLocation(4, 5), @@ -7634,6 +7679,9 @@ class C "; var compilation = CreateCompilationWithIndex(source); compilation.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = new C() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("C").WithLocation(2, 16), // (2,16): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' // _ = new C() is []; Diagnostic(ErrorCode.ERR_BadArgType, "[]").WithArguments("1", "System.Index", "int").WithLocation(2, 16), @@ -7781,6 +7829,9 @@ public int this[System.Range r] { set { } } "; var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // _ = new C() is [var x, .. var y]; // 1, 2, 3 + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[var x, .. var y]").WithArguments("C").WithLocation(2, 16), // (2,16): error CS0154: The property or indexer 'C.this[Index]' cannot be used in this context because it lacks the get accessor // _ = new C() is [var x, .. var y]; // 1, 2, 3 Diagnostic(ErrorCode.ERR_PropertyLacksGet, "[var x, .. var y]").WithArguments("C.this[System.Index]").WithLocation(2, 16), @@ -7796,6 +7847,38 @@ public int this[System.Range r] { set { } } ); } + [Fact] + public void ListPattern_SetOnlyIndexers_LengthWithGetter() + { + var source = @" +_ = new C() is [var x, .. var y]; // 1, 2 +_ = new C()[^1]; // 3 +_ = new C()[..]; // 4 + +class C +{ + public int Length => 0; + public int this[System.Index i] { set { } } + public int this[System.Range r] { set { } } +} +"; + var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); + comp.VerifyEmitDiagnostics( + // (2,16): error CS0154: The property or indexer 'C.this[Index]' cannot be used in this context because it lacks the get accessor + // _ = new C() is [var x, .. var y]; // 1, 2 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "[var x, .. var y]").WithArguments("C.this[System.Index]").WithLocation(2, 16), + // (2,24): error CS0154: The property or indexer 'C.this[Range]' cannot be used in this context because it lacks the get accessor + // _ = new C() is [var x, .. var y]; // 1, 2 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, ".. var y").WithArguments("C.this[System.Range]").WithLocation(2, 24), + // (3,5): error CS0154: The property or indexer 'C.this[Index]' cannot be used in this context because it lacks the get accessor + // _ = new C()[^1]; // 3 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new C()[^1]").WithArguments("C.this[System.Index]").WithLocation(3, 5), + // (4,5): error CS0154: The property or indexer 'C.this[Range]' cannot be used in this context because it lacks the get accessor + // _ = new C()[..]; // 4 + Diagnostic(ErrorCode.ERR_PropertyLacksGet, "new C()[..]").WithArguments("C.this[System.Range]").WithLocation(4, 5) + ); + } + [Fact] public void SlicePattern_OnConsList() { @@ -8282,6 +8365,9 @@ class C : INotCountable "; var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range }); comp.VerifyEmitDiagnostics( + // (2,34): error CS8985: List patterns may not be used for a value of type 'INotCountable'. No suitable 'Length' or 'Count' property was found. + // _ = new C() is INotCountable and [var x, .. var y]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[var x, .. var y]").WithArguments("INotCountable").WithLocation(2, 34), // (2,34): error CS0021: Cannot apply indexing with [] to an expression of type 'INotCountable' // _ = new C() is INotCountable and [var x, .. var y]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var x, .. var y]").WithArguments("INotCountable").WithLocation(2, 34), @@ -8337,6 +8423,9 @@ public void ListPattern_Tuples() "; var comp = CreateCompilation(new[] { source, TestSources.Index, TestSources.Range, TestSources.ITuple }); comp.VerifyEmitDiagnostics( + // (2,15): error CS8985: List patterns may not be used for a value of type '(int, int)'. No suitable 'Length' or 'Count' property was found. + // _ = (1, 2) is [var x, .. var y]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[var x, .. var y]").WithArguments("(int, int)").WithLocation(2, 15), // (2,15): error CS0021: Cannot apply indexing with [] to an expression of type '(int, int)' // _ = (1, 2) is [var x, .. var y]; Diagnostic(ErrorCode.ERR_BadIndexLHS, "[var x, .. var y]").WithArguments("(int, int)").WithLocation(2, 15), @@ -8381,6 +8470,219 @@ public void ListPattern_NullTestOnSlice() ); } + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength() + { + var source = @" +_ = new S() is []; +_ = new S() is [..]; +_ = new S() is [0, .. var x, 1]; + +struct S +{ + public int this[System.Index i] => 0; + public int this[System.Range i] => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16), + // (3,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is [..]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[..]").WithArguments("S").WithLocation(3, 16), + // (4,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is [0, .. var x, 1]; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[0, .. var x, 1]").WithArguments("S").WithLocation(4, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_WithIntIndexer() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[int i] => i; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16), + // (2,16): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_BadArgType, "[]").WithArguments("1", "System.Index", "int").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_PrivateLength() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[System.Index i] => 0; + private int Length => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_ProtectedLength() + { + var source = @" +_ = new S() is []; + +class S +{ + public int this[System.Index i] => 0; + protected int Length => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_PrivateCount() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[System.Index i] => 0; + private int Count => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_LengthMethod() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[System.Index i] => 0; + public int Length() => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_WriteOnlyLength() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[System.Index i] => 0; + public int Length { set { throw null; } } +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_ObjectLength() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[System.Index i] => 0; + public object Length => null; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_StaticLength() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[System.Index i] => 0; + public static int Length => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16) + ); + } + + [Fact, WorkItem(59465, "https://github.com/dotnet/roslyn/issues/59465")] + public void MissingLength_StaticLength_IntIndexer() + { + var source = @" +_ = new S() is []; + +struct S +{ + public int this[int i] => 0; + public static int Length => 0; +} +"; + var comp = CreateCompilationWithIndexAndRangeAndSpan(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS8985: List patterns may not be used for a value of type 'S'. No suitable 'Length' or 'Count' property was found. + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[]").WithArguments("S").WithLocation(2, 16), + // (2,16): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = new S() is []; + Diagnostic(ErrorCode.ERR_BadArgType, "[]").WithArguments("1", "System.Index", "int").WithLocation(2, 16) + ); + } + [Fact, WorkItem(58738, "https://github.com/dotnet/roslyn/issues/58738")] public void ListPattern_AbstractFlowPass_isBoolTest() { @@ -8405,6 +8707,9 @@ public void ListPattern_AbstractFlowPass_isBoolTest() // (4,22): error CS0029: Cannot implicitly convert type 'int' to 'int[]' // if (a is [var x] and x is [1]) Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("int", "int[]").WithLocation(4, 22), + // (4,27): error CS8985: List patterns may not be used for a value of type 'bool'. No suitable 'Length' or 'Count' property was found. + // if (a is [var x] and x is [1]) + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[1]").WithArguments("bool").WithLocation(4, 27), // (4,27): error CS0021: Cannot apply indexing with [] to an expression of type 'bool' // if (a is [var x] and x is [1]) Diagnostic(ErrorCode.ERR_BadIndexLHS, "[1]").WithArguments("bool").WithLocation(4, 27), @@ -8417,6 +8722,9 @@ public void ListPattern_AbstractFlowPass_isBoolTest() // (13,23): error CS0029: Cannot implicitly convert type 'bool' to 'bool[]' // if ((b is [var z] and z) is [true]) Diagnostic(ErrorCode.ERR_NoImplicitConv, "z").WithArguments("bool", "bool[]").WithLocation(13, 23), + // (13,29): error CS8985: List patterns may not be used for a value of type 'bool'. No suitable 'Length' or 'Count' property was found. + // if ((b is [var z] and z) is [true]) + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, "[true]").WithArguments("bool").WithLocation(13, 29), // (13,29): error CS0021: Cannot apply indexing with [] to an expression of type 'bool' // if ((b is [var z] and z) is [true]) Diagnostic(ErrorCode.ERR_BadIndexLHS, "[true]").WithArguments("bool").WithLocation(13, 29) From a0618c4b82cf7918536d1f44d1638149a36e0cc2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 11:50:33 -0800 Subject: [PATCH 067/187] Move --- src/Features/Core/Portable/CodeFixes/CodeFixService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index b5ca56acd6252..24b5fa1096df0 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -450,11 +450,11 @@ await AppendFixesOrConfigurationsAsync( } }, cancellationToken).ConfigureAwait(false); - } - // Just need the first result if we are doing fix all in span - if (fixAllForInSpan && result.Any()) - return; + // Just need the first result if we are doing fix all in span + if (fixAllForInSpan && result.Any()) + return; + } } } finally From da64131ad60ce3d39c0e4d6b0bf6065d035825df Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 12:14:21 -0800 Subject: [PATCH 068/187] Allow codefix service to produce codefixes in a streaming fashion --- .../Core/Portable/CodeFixes/CodeFixService.cs | 95 ++++++++++--------- .../Portable/CodeFixes/ICodeFixService.cs | 11 ++- .../Extensions/IAsyncEnumerableExtensions.cs | 24 +++++ 3 files changed, 84 insertions(+), 46 deletions(-) create mode 100644 src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 24b5fa1096df0..2024a876c3cb6 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -142,13 +142,13 @@ public async Task GetMostSevereFixableDiagnosticAsync( return null; } - public async Task> GetFixesAsync( + public async IAsyncEnumerable StreamFixesAsync( Document document, TextSpan range, CodeActionRequestPriority priority, CodeActionOptions options, Func addOperationScope, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic service to give back // current diagnostics for the given span, and it will use that to get fixes. internally diagnostic service will either return cached information @@ -172,7 +172,7 @@ public async Task> GetFixesAsync( ? GetFixableDiagnosticFilter(document) : null; - // We only need to compute suppression/configurationofixes when request priority is + // We only need to compute suppression/configuration fixes when request priority is // 'CodeActionPriorityRequest.Lowest' or 'CodeActionPriorityRequest.None'. var includeSuppressionFixes = priority is CodeActionRequestPriority.Lowest or CodeActionRequestPriority.None; @@ -188,21 +188,21 @@ public async Task> GetFixesAsync( } if (aggregatedDiagnostics.Count == 0) - return ImmutableArray.Empty; + yield break; // Order diagnostics by DiagnosticId so the fixes are in a deterministic order. foreach (var (_, diagnosticList) in aggregatedDiagnostics) diagnosticList.Sort(s_diagnosticDataComparisonById); - // append fixes for all diagnostics with the same diagnostics span - using var _1 = ArrayBuilder.GetInstance(out var result); - // 'CodeActionRequestPriority.Lowest' is used when the client only wants suppression/configuration fixes. if (priority != CodeActionRequestPriority.Lowest) { - await AppendFixesAsync( + await foreach (var collection in StreamFixesAsync( document, aggregatedDiagnostics, fixAllForInSpan: false, - priority, options, result, addOperationScope, cancellationToken).ConfigureAwait(false); + priority, options, addOperationScope, cancellationToken).ConfigureAwait(false)) + { + yield return collection; + } } // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive @@ -212,13 +212,15 @@ await AppendFixesAsync( using var _2 = PooledHashSet.GetInstance(out var registeredConfigurationFixTitles); foreach (var (span, diagnosticList) in aggregatedDiagnostics) { - await AppendConfigurationsAsync( - document, span, diagnosticList, - result, registeredConfigurationFixTitles, cancellationToken).ConfigureAwait(false); + await foreach (var codeFixCollection in StreamConfigurationFixesAsync( + document, span, diagnosticList, registeredConfigurationFixTitles, cancellationToken).ConfigureAwait(false)) + { + yield return codeFixCollection; + } } } - return result.ToImmutable(); + yield break; // Local functions Func GetFixableDiagnosticFilter(Document document) @@ -249,13 +251,16 @@ Func GetFixableDiagnosticFilter(Document document) {range, diagnostics }, }; - await AppendFixesAsync( + await foreach (var collection in StreamFixesAsync( document, spanToDiagnostics, fixAllForInSpan: true, CodeActionRequestPriority.None, - options, result, addOperationScope: static _ => null, cancellationToken).ConfigureAwait(false); + options, addOperationScope: static _ => null, cancellationToken).ConfigureAwait(false)) + { + // TODO: Just get the first fix for now until we have a way to config user's preferred fix + // https://github.com/dotnet/roslyn/issues/27066 + return collection; + } - // TODO: Just get the first fix for now until we have a way to config user's preferred fix - // https://github.com/dotnet/roslyn/issues/27066 - return result.ToImmutable().FirstOrDefault(); + return null; } public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document document, string diagnosticId, IProgressTracker progressTracker, CodeActionOptions options, CancellationToken cancellationToken) @@ -342,15 +347,14 @@ private bool TryGetWorkspaceFixer( } } - private async Task AppendFixesAsync( + private async IAsyncEnumerable StreamFixesAsync( Document document, SortedDictionary> spanToDiagnostics, bool fixAllForInSpan, CodeActionRequestPriority priority, CodeActionOptions options, - ArrayBuilder result, Func addOperationScope, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap); @@ -358,7 +362,7 @@ private async Task AppendFixesAsync( var hasAnyProjectFixer = projectFixersMap.Any(); if (!hasAnySharedFixer && !hasAnyProjectFixer) - return; + yield break; // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive var isInteractive = document.Project.Solution.Workspace.Kind == WorkspaceKind.Interactive; @@ -424,8 +428,8 @@ private async Task AppendFixesAsync( foreach (var (span, diagnostics) in fixerToRangesAndDiagnostics[fixer]) { - await AppendFixesOrConfigurationsAsync( - document, span, diagnostics, fixAllForInSpan, result, fixer, + var codeFixCollection = await TryGetFixesOrConfigurationsAsync( + document, span, diagnostics, fixAllForInSpan, fixer, hasFix: d => this.GetFixableDiagnosticIds(fixer, extensionManager).Contains(d.Id), getFixes: dxs => { @@ -451,9 +455,14 @@ await AppendFixesOrConfigurationsAsync( }, cancellationToken).ConfigureAwait(false); - // Just need the first result if we are doing fix all in span - if (fixAllForInSpan && result.Any()) - return; + if (codeFixCollection != null) + { + yield return codeFixCollection; + + // Just need the first result if we are doing fix all in span + if (fixAllForInSpan) + yield break; + } } } } @@ -465,7 +474,7 @@ await AppendFixesOrConfigurationsAsync( } } - return; + yield break; void AddAllFixers( ImmutableArray fixers, @@ -557,17 +566,17 @@ static ImmutableArray FilterApplicableDiagnostics( } } - private async Task AppendConfigurationsAsync( + private async IAsyncEnumerable StreamConfigurationFixesAsync( Document document, TextSpan diagnosticsSpan, IEnumerable diagnostics, - ArrayBuilder result, PooledHashSet registeredConfigurationFixTitles, - CancellationToken cancellationToken) + [EnumeratorCancellation] CancellationToken cancellationToken) { - if (!_configurationProvidersMap.TryGetValue(document.Project.Language, out var lazyConfigurationProviders) || lazyConfigurationProviders.Value == null) + if (!_configurationProvidersMap.TryGetValue(document.Project.Language, out var lazyConfigurationProviders) || + lazyConfigurationProviders.Value == null) { - return; + yield break; } // append CodeFixCollection for each CodeFixProvider @@ -575,8 +584,8 @@ private async Task AppendConfigurationsAsync( { using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, provider, cancellationToken)) { - await AppendFixesOrConfigurationsAsync( - document, diagnosticsSpan, diagnostics, fixAllForInSpan: false, result, provider, + var codeFixCollection = await TryGetFixesOrConfigurationsAsync( + document, diagnosticsSpan, diagnostics, fixAllForInSpan: false, provider, hasFix: d => provider.IsFixableDiagnostic(d), getFixes: async dxs => { @@ -584,16 +593,17 @@ await AppendFixesOrConfigurationsAsync( return fixes.WhereAsArray(f => registeredConfigurationFixTitles.Add(f.Action.Title)); }, cancellationToken).ConfigureAwait(false); + if (codeFixCollection != null) + yield return codeFixCollection; } } } - private async Task AppendFixesOrConfigurationsAsync( + private async Task TryGetFixesOrConfigurationsAsync( Document document, TextSpan fixesSpan, IEnumerable diagnosticsWithSameSpan, bool fixAllForInSpan, - ArrayBuilder result, TCodeFixProvider fixer, Func hasFix, Func, Task>> getFixes, @@ -607,7 +617,7 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) if (diagnostics.Length <= 0) { // this can happen for suppression case where all diagnostics can't be suppressed - return; + return null; } var extensionManager = document.Project.Solution.Workspace.Services.GetRequiredService(); @@ -616,9 +626,7 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) defaultValue: ImmutableArray.Empty).ConfigureAwait(false); if (fixes.IsDefaultOrEmpty) - { - return; - } + return null; // If the fix provider supports fix all occurrences, then get the corresponding FixAllProviderInfo and fix all context. var fixAllProviderInfo = extensionManager.PerformFunction( @@ -667,10 +675,9 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) supportedScopes = fixAllProviderInfo.SupportedScopes; } - var codeFix = new CodeFixCollection( + return new CodeFixCollection( fixer, fixesSpan, fixes, fixAllState, supportedScopes, diagnostics.First()); - result.Add(codeFix); } /// Looks explicitly for an . @@ -992,7 +999,7 @@ public FixerComparer(ImmutableDictionary priorityMap) => _priorityMap = priorityMap; public int Compare([AllowNull] CodeFixProvider x, [AllowNull] CodeFixProvider y) - => GetValue(x).CompareTo(GetValue(y)); + => GetValue(x!).CompareTo(GetValue(y!)); private int GetValue(CodeFixProvider provider) => _priorityMap.TryGetValue(provider, out var value) ? value : int.MaxValue; diff --git a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs b/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs index c001b53f37052..fc04f0104fd90 100644 --- a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -15,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes { internal interface ICodeFixService { - Task> GetFixesAsync(Document document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptions options, Func addOperationScope, CancellationToken cancellationToken); + IAsyncEnumerable StreamFixesAsync(Document document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptions options, Func addOperationScope, CancellationToken cancellationToken); Task GetDocumentFixAllForIdInSpanAsync(Document document, TextSpan textSpan, string diagnosticId, CodeActionOptions options, CancellationToken cancellationToken); Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document document, string diagnosticId, IProgressTracker progressTracker, CodeActionOptions options, CancellationToken cancellationToken); CodeFixProvider? GetSuppressionFixer(string language, IEnumerable diagnosticIds); @@ -24,7 +25,13 @@ internal interface ICodeFixService internal static class ICodeFixServiceExtensions { + public static IAsyncEnumerable StreamFixesAsync(this ICodeFixService service, Document document, TextSpan range, CodeActionOptions options, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, range, CodeActionRequestPriority.None, options, addOperationScope: _ => null, cancellationToken); + public static Task> GetFixesAsync(this ICodeFixService service, Document document, TextSpan range, CodeActionOptions options, CancellationToken cancellationToken) - => service.GetFixesAsync(document, range, CodeActionRequestPriority.None, options, addOperationScope: _ => null, cancellationToken); + => service.StreamFixesAsync(document, range, options, cancellationToken).ToImmutableArrayAsync(); + + public static Task> GetFixesAsync(this ICodeFixService service, Document document, TextSpan textSpan, CodeActionRequestPriority priority, CodeActionOptions options, Func addOperationScope, CancellationToken cancellationToken) + => service.StreamFixesAsync(document, textSpan, priority, options, addOperationScope, cancellationToken).ToImmutableArrayAsync(); } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs new file mode 100644 index 0000000000000..c246588d4acef --- /dev/null +++ b/src/Workspaces/Core/Portable/Shared/Extensions/IAsyncEnumerableExtensions.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.Shared.Extensions +{ + internal static class IAsyncEnumerableExtensions + { + public static async Task> ToImmutableArrayAsync(this IAsyncEnumerable values) + { + using var _ = ArrayBuilder.GetInstance(out var result); + await foreach (var value in values.ConfigureAwait(false)) + result.Add(value); + + return result.ToImmutable(); + } + } +} From 48f02cbd60fa1c82ddc486818eca04c981bc9a8d Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 12:36:36 -0800 Subject: [PATCH 069/187] NRT --- src/Features/Core/Portable/CodeFixes/CodeFixService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 24b5fa1096df0..3091c6e3785b7 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -992,7 +992,7 @@ public FixerComparer(ImmutableDictionary priorityMap) => _priorityMap = priorityMap; public int Compare([AllowNull] CodeFixProvider x, [AllowNull] CodeFixProvider y) - => GetValue(x).CompareTo(GetValue(y)); + => GetValue(x!).CompareTo(GetValue(y!)); private int GetValue(CodeFixProvider provider) => _priorityMap.TryGetValue(provider, out var value) ? value : int.MaxValue; From 52bdc01db337967e9d71080b548a5419a44ed996 Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Wed, 16 Feb 2022 22:45:09 +0200 Subject: [PATCH 070/187] Respect PreferParameterNullChecking user option (#59561) --- .../AddParameterCheckTests.cs | 47 +++++++++++++++++-- ...ddParameterCheckCodeRefactoringProvider.cs | 9 +++- ...ddParameterCheckCodeRefactoringProvider.cs | 4 +- ...ddParameterCheckCodeRefactoringProvider.vb | 4 +- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs index cc1287e100ecb..6c2f63b18d9a0 100644 --- a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs @@ -29,8 +29,10 @@ public async Task TestEmptyFile() await VerifyCS.VerifyRefactoringAsync(code, code); } - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] - public async Task TestSimpleReferenceType() + [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] + [InlineData("csharp_style_prefer_parameter_null_checking = true")] + [InlineData("")] + public async Task TestSimpleReferenceType(string editorConfig) { await new VerifyCS.Test { @@ -52,7 +54,46 @@ class C public C(string s!!) { } -}" +}", + EditorConfig = $@" +[*] +{editorConfig} +" + }.RunAsync(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] + public async Task TestDoNotPreferParameterNullChecking() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersionExtensions.CSharpNext, + TestCode = @" +using System; + +class C +{ + public C([||]string s) + { + } +}", + FixedCode = @" +using System; + +class C +{ + public C(string s) + { + if (s is null) + { + throw new ArgumentNullException(nameof(s)); + } + } +}", + EditorConfig = @" +[*] +csharp_style_prefer_parameter_null_checking = false +", }.RunAsync(); } diff --git a/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs index fd114d1d3d3ac..50021c5381471 100644 --- a/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs @@ -5,6 +5,7 @@ using System; using System.Composition; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; @@ -102,14 +103,18 @@ protected override StatementSyntax CreateParameterCheckIfStatement(DocumentOptio @else: null); } - protected override Document? TryAddNullCheckToParameterDeclaration(Document document, ParameterSyntax parameterSyntax, CancellationToken cancellationToken) + protected override async Task TryAddNullCheckToParameterDeclarationAsync(Document document, ParameterSyntax parameterSyntax, CancellationToken cancellationToken) { var tree = parameterSyntax.SyntaxTree; if (!tree.Options.LanguageVersion().IsCSharp11OrAbove()) return null; + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + if (!options.GetOption(CSharpCodeStyleOptions.PreferParameterNullChecking).Value) + return null; + // We expect the syntax tree to already be in memory since we already have a node from the tree - var syntaxRoot = tree.GetRoot(cancellationToken); + var syntaxRoot = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); syntaxRoot = syntaxRoot.ReplaceNode( parameterSyntax, parameterSyntax.WithExclamationExclamationToken(Token(SyntaxKind.ExclamationExclamationToken))); diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 62c1143f6fcda..01ef17c1bd039 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -42,7 +42,7 @@ internal abstract partial class AbstractAddParameterCheckCodeRefactoringProvider protected abstract bool PrefersThrowExpression(DocumentOptionSet options); protected abstract string EscapeResourceString(string input); protected abstract TStatementSyntax CreateParameterCheckIfStatement(DocumentOptionSet options, TExpressionSyntax condition, TStatementSyntax ifTrueStatement); - protected abstract Document? TryAddNullCheckToParameterDeclaration(Document document, TParameterSyntax parameterSyntax, CancellationToken cancellationToken); + protected abstract Task TryAddNullCheckToParameterDeclarationAsync(Document document, TParameterSyntax parameterSyntax, CancellationToken cancellationToken); protected override async Task> GetRefactoringsForAllParametersAsync( Document document, @@ -331,7 +331,7 @@ private async Task AddNullCheckAsync( CancellationToken cancellationToken) { // First see if we can adopt the '!!' parameter null checking syntax. - var modifiedDocument = TryAddNullCheckToParameterDeclaration(document, parameterSyntax, cancellationToken); + var modifiedDocument = await TryAddNullCheckToParameterDeclarationAsync(document, parameterSyntax, cancellationToken).ConfigureAwait(false); if (modifiedDocument != null) { return modifiedDocument; diff --git a/src/Features/VisualBasic/Portable/InitializeParameter/VisualBasicAddParameterCheckCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/InitializeParameter/VisualBasicAddParameterCheckCodeRefactoringProvider.vb index 8872ff80664a8..98d6344cad377 100644 --- a/src/Features/VisualBasic/Portable/InitializeParameter/VisualBasicAddParameterCheckCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/InitializeParameter/VisualBasicAddParameterCheckCodeRefactoringProvider.vb @@ -72,8 +72,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.InitializeParameter elseBlock:=Nothing) End Function - Protected Overrides Function TryAddNullCheckToParameterDeclaration(document As Document, parameterSyntax As ParameterSyntax, cancellationToken As CancellationToken) As Document - Return Nothing + Protected Overrides Function TryAddNullCheckToParameterDeclarationAsync(document As Document, parameterSyntax As ParameterSyntax, cancellationToken As CancellationToken) As Task(Of Document) + Return Task.FromResult(Of Document)(Nothing) End Function End Class End Namespace From fb703600cc6e8ca32bb6db3ab0f6d0e4938028c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Wed, 16 Feb 2022 13:24:37 -0800 Subject: [PATCH 071/187] Move global options to EditorFeature layer (#59071) * ForceLowMemoryMode options * RemoteHostOptions --- .../Core/Remote}/RemoteHostOptions.cs | 24 ---------------- .../Core/Remote}/SolutionChecksumUpdater.cs | 0 .../VisualStudioRemoteHostClientProvider.cs | 8 ++++-- ...osoft.VisualStudio.LanguageServices.csproj | 1 - .../OptionPages/ForceLowMemoryMode.cs | 28 +++++++++++-------- .../OptionPages/ForceLowMemoryMode_Options.cs | 19 ------------- .../VisualStudioDiagnosticsWindowPackage.cs | 4 +-- .../Remote/InProcRemostHostClient.cs | 2 +- .../Api/UnitTestingRemoteHostClient.cs | 8 ------ .../Remote/Core/RemoteProcessConfiguration.cs | 22 +++++++++++++++ .../Remote/Core/ServiceDescriptors.cs | 20 ++++++++----- .../Remote/Core/ServiceHubRemoteHostClient.cs | 14 ++++------ .../RemoteAssetSynchronizationService.cs | 2 +- 13 files changed, 68 insertions(+), 84 deletions(-) rename src/{Workspaces/Remote/Core => EditorFeatures/Core/Remote}/RemoteHostOptions.cs (67%) rename src/{Workspaces/Remote/Core => EditorFeatures/Core/Remote}/SolutionChecksumUpdater.cs (100%) delete mode 100644 src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode_Options.cs create mode 100644 src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs diff --git a/src/Workspaces/Remote/Core/RemoteHostOptions.cs b/src/EditorFeatures/Core/Remote/RemoteHostOptions.cs similarity index 67% rename from src/Workspaces/Remote/Core/RemoteHostOptions.cs rename to src/EditorFeatures/Core/Remote/RemoteHostOptions.cs index 17f6a21e1c774..d97aef64a012a 100644 --- a/src/Workspaces/Remote/Core/RemoteHostOptions.cs +++ b/src/EditorFeatures/Core/Remote/RemoteHostOptions.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options.Providers; @@ -35,11 +33,6 @@ internal sealed class RemoteHostOptions : IOptionProvider FeatureName, nameof(OOP64Bit), defaultValue: true, storageLocation: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OOP64Bit))); - // use Server GC for 64-bit OOP - public static readonly Option2 OOPServerGC = new( - FeatureName, nameof(OOPServerGC), defaultValue: false, - storageLocation: new LocalUserProfileStorageLocation(LocalRegistryPath + nameof(OOPServerGC))); - public static readonly Option2 OOPServerGCFeatureFlag = new( FeatureName, nameof(OOPServerGCFeatureFlag), defaultValue: false, new FeatureFlagStorageLocation("Roslyn.OOPServerGC")); @@ -52,7 +45,6 @@ internal sealed class RemoteHostOptions : IOptionProvider ImmutableArray IOptionProvider.Options { get; } = ImmutableArray.Create( SolutionChecksumMonitorBackOffTimeSpanInMS, OOP64Bit, - OOPServerGC, OOPServerGCFeatureFlag, OOPCoreClrFeatureFlag); @@ -61,21 +53,5 @@ internal sealed class RemoteHostOptions : IOptionProvider public RemoteHostOptions() { } - - public static bool IsServiceHubProcessServerGC(IGlobalOptionService globalOptions) - => globalOptions.GetOption(OOPServerGC) || globalOptions.GetOption(OOPServerGCFeatureFlag); - - /// - /// Determines whether ServiceHub out-of-process execution is enabled for Roslyn. - /// - public static bool IsUsingServiceHubOutOfProcess(IGlobalOptionService globalOptions) - => Environment.Is64BitOperatingSystem && globalOptions.GetOption(OOP64Bit); - - public static bool IsServiceHubProcessCoreClr(IGlobalOptionService globalOptions) - => globalOptions.GetOption(OOPCoreClrFeatureFlag); - - public static bool IsCurrentProcessRunningOnCoreClr() - => !RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") && - !RuntimeInformation.FrameworkDescription.StartsWith(".NET Native"); } } diff --git a/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs b/src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs similarity index 100% rename from src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs rename to src/EditorFeatures/Core/Remote/SolutionChecksumUpdater.cs diff --git a/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs b/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs index 280ed5877add2..9ef7d6e85938c 100644 --- a/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/Remote/VisualStudioRemoteHostClientProvider.cs @@ -54,7 +54,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) { // We don't want to bring up the OOP process in a VS cloud environment client instance // Avoids proffering brokered services on the client instance. - if (!RemoteHostOptions.IsUsingServiceHubOutOfProcess(_globalOptions) || + if (!_globalOptions.GetOption(RemoteHostOptions.OOP64Bit) || workspaceServices.Workspace is not VisualStudioWorkspace || workspaceServices.GetRequiredService().IsCloudEnvironmentClient()) { @@ -99,8 +99,12 @@ private VisualStudioRemoteHostClientProvider( var brokeredServiceContainer = await _vsServiceProvider.GetServiceAsync().ConfigureAwait(false); var serviceBroker = brokeredServiceContainer.GetFullAccessServiceBroker(); + var configuration = + (_globalOptions.GetOption(RemoteHostOptions.OOPCoreClrFeatureFlag) ? RemoteProcessConfiguration.Core : 0) | + (_globalOptions.GetOption(RemoteHostOptions.OOPServerGCFeatureFlag) ? RemoteProcessConfiguration.ServerGC : 0); + // VS AsyncLazy does not currently support cancellation: - var client = await ServiceHubRemoteHostClient.CreateAsync(_services, _globalOptions, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); + var client = await ServiceHubRemoteHostClient.CreateAsync(_services, configuration, _listenerProvider, serviceBroker, _callbackDispatchers, CancellationToken.None).ConfigureAwait(false); // proffer in-proc brokered services: _ = brokeredServiceContainer.Proffer(SolutionAssetProvider.ServiceDescriptor, (_, _, _, _) => ValueTaskFactory.FromResult(new SolutionAssetProvider(_services))); diff --git a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj index 2dd33442b401e..efa1ddfddfdd6 100644 --- a/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj +++ b/src/VisualStudio/Core/Def/Microsoft.VisualStudio.LanguageServices.csproj @@ -64,7 +64,6 @@ - diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs index ff51b0672e50c..ec0b8b1dd46b6 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.cs +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode.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.Threading; @@ -12,23 +10,31 @@ namespace Roslyn.VisualStudio.DiagnosticsWindow.OptionsPages { - internal sealed partial class ForceLowMemoryMode + internal sealed class ForceLowMemoryMode { - private readonly IOptionService _optionService; - private MemoryHogger _hogger; + private const string FeatureName = "ForceLowMemoryMode"; + + public static readonly Option2 Enabled = new(FeatureName, "Enabled", defaultValue: false, + storageLocation: new LocalUserProfileStorageLocation(@"Roslyn\ForceLowMemoryMode\Enabled")); + + public static readonly Option2 SizeInMegabytes = new(FeatureName, "SizeInMegabytes", defaultValue: 500, + storageLocation: new LocalUserProfileStorageLocation(@"Roslyn\ForceLowMemoryMode\SizeInMegabytes")); + + private readonly IGlobalOptionService _globalOptions; + private MemoryHogger? _hogger; - public ForceLowMemoryMode(IOptionService optionService) + public ForceLowMemoryMode(IGlobalOptionService globalOptions) { - _optionService = optionService; + _globalOptions = globalOptions; - optionService.OptionChanged += Options_OptionChanged; + globalOptions.OptionChanged += Options_OptionChanged; RefreshFromSettings(); } private void Options_OptionChanged(object sender, OptionChangedEventArgs e) { - if (e.Option.Feature == nameof(ForceLowMemoryMode)) + if (e.Option.Feature == FeatureName) { RefreshFromSettings(); } @@ -36,7 +42,7 @@ private void Options_OptionChanged(object sender, OptionChangedEventArgs e) private void RefreshFromSettings() { - var enabled = _optionService.GetOption(Enabled); + var enabled = _globalOptions.GetOption(Enabled); if (_hogger != null) { @@ -47,7 +53,7 @@ private void RefreshFromSettings() if (enabled) { _hogger = new MemoryHogger(); - _ = _hogger.PopulateAndMonitorAsync(_optionService.GetOption(SizeInMegabytes)); + _ = _hogger.PopulateAndMonitorAsync(_globalOptions.GetOption(SizeInMegabytes)); } } diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode_Options.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode_Options.cs deleted file mode 100644 index a529713238c60..0000000000000 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryMode_Options.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. - -#nullable disable - -using Microsoft.CodeAnalysis.Options; - -namespace Roslyn.VisualStudio.DiagnosticsWindow.OptionsPages -{ - internal sealed partial class ForceLowMemoryMode - { - public static readonly Option2 Enabled = new(nameof(ForceLowMemoryMode), nameof(Enabled), defaultValue: false, - storageLocation: new LocalUserProfileStorageLocation(@"Roslyn\ForceLowMemoryMode\Enabled")); - - public static readonly Option2 SizeInMegabytes = new(nameof(ForceLowMemoryMode), nameof(SizeInMegabytes), defaultValue: 500, - storageLocation: new LocalUserProfileStorageLocation(@"Roslyn\ForceLowMemoryMode\SizeInMegabytes")); - } -} diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindowPackage.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindowPackage.cs index ab77128bea674..6d91c44f6f5d0 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindowPackage.cs +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/VisualStudioDiagnosticsWindowPackage.cs @@ -81,9 +81,10 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke Assumes.Present(menuCommandService); _threadingContext = componentModel.GetService(); + var globalOptions = componentModel.GetService(); _workspace = componentModel.GetService(); - _ = new ForceLowMemoryMode(_workspace.Services.GetService()); + _ = new ForceLowMemoryMode(globalOptions); // Add our command handlers for menu (commands must exist in the .vsct file) if (menuCommandService is OleMenuCommandService mcs) @@ -95,7 +96,6 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke } // set logger at start up - var globalOptions = componentModel.GetService(); PerformanceLoggersPage.SetLoggers(globalOptions, _threadingContext, _workspace.Services); } #endregion diff --git a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs index d9ffbca4b7003..7aba10a48ba24 100644 --- a/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs +++ b/src/Workspaces/CoreTestUtilities/Remote/InProcRemostHostClient.cs @@ -64,7 +64,7 @@ public RemoteWorkspace GetRemoteWorkspace() public override RemoteServiceConnection CreateConnection(object? callbackTarget) where T : class { - var descriptor = ServiceDescriptors.Instance.GetServiceDescriptor(typeof(T), isRemoteHostServerGC: GCSettings.IsServerGC, isRemoteHostCoreClr: RemoteHostOptions.IsCurrentProcessRunningOnCoreClr()); + var descriptor = ServiceDescriptors.Instance.GetServiceDescriptor(typeof(T), RemoteProcessConfiguration.ServerGC | (ServiceDescriptors.IsCurrentProcessRunningOnCoreClr() ? RemoteProcessConfiguration.Core : 0)); var callbackDispatcher = (descriptor.ClientInterface != null) ? _callbackDispatchers.GetDispatcher(typeof(T)) : null; return new BrokeredServiceConnection( diff --git a/src/Workspaces/Remote/Core/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostClient.cs b/src/Workspaces/Remote/Core/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostClient.cs index b031bf104c4c2..d74c0d32d26ac 100644 --- a/src/Workspaces/Remote/Core/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostClient.cs +++ b/src/Workspaces/Remote/Core/ExternalAccess/UnitTesting/Api/UnitTestingRemoteHostClient.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api @@ -33,13 +32,6 @@ internal UnitTestingRemoteHostClient(ServiceHubRemoteHostClient client, UnitTest return new UnitTestingRemoteHostClient((ServiceHubRemoteHostClient)client, serviceDescriptors, callbackDispatchers); } - [Obsolete("Use UnitTestingGlobalOptions.IsServiceHubProcessCoreClr instead")] - public static bool IsServiceHubProcessCoreClr(HostWorkspaceServices services) - { - var optionServices = services.GetRequiredService(); - return optionServices.GetOption(RemoteHostOptions.OOPCoreClrFeatureFlag); - } - public UnitTestingRemoteServiceConnectionWrapper CreateConnection(object? callbackTarget) where TService : class => new(_client.CreateConnection(_serviceDescriptors.UnderlyingObject, _callbackDispatchers, callbackTarget)); diff --git a/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs b/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs new file mode 100644 index 0000000000000..aac8916b583d2 --- /dev/null +++ b/src/Workspaces/Remote/Core/RemoteProcessConfiguration.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.CodeAnalysis.Remote +{ + [Flags] + internal enum RemoteProcessConfiguration + { + /// + /// Remote host runs on .NET 6+. + /// + Core = 1, + + /// + /// Remote host uses server GC. + /// + ServerGC = 1 << 1 + } +} diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index 339d2fd811c94..f1388af0864f2 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Runtime; +using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CodeLens; @@ -120,18 +121,23 @@ internal static string GetSimpleName(Type serviceInterface) return (descriptor64, descriptor64ServerGC, descriptorCoreClr64, descriptorCoreClr64ServerGC); } + public static bool IsCurrentProcessRunningOnCoreClr() + => !RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") && + !RuntimeInformation.FrameworkDescription.StartsWith(".NET Native"); + public ServiceDescriptor GetServiceDescriptorForServiceFactory(Type serviceType) - => GetServiceDescriptor(serviceType, isRemoteHostServerGC: GCSettings.IsServerGC, isRemoteHostCoreClr: RemoteHostOptions.IsCurrentProcessRunningOnCoreClr()); + => GetServiceDescriptor(serviceType, RemoteProcessConfiguration.ServerGC | (IsCurrentProcessRunningOnCoreClr() ? RemoteProcessConfiguration.Core : 0)); - public ServiceDescriptor GetServiceDescriptor(Type serviceType, bool isRemoteHostServerGC, bool isRemoteHostCoreClr) + public ServiceDescriptor GetServiceDescriptor(Type serviceType, RemoteProcessConfiguration configuration) { var (descriptor64, descriptor64ServerGC, descriptorCoreClr64, descriptorCoreClr64ServerGC) = _descriptors[serviceType]; - return (isRemoteHostServerGC, isRemoteHostCoreClr) switch + return (configuration & (RemoteProcessConfiguration.Core | RemoteProcessConfiguration.ServerGC)) switch { - (false, false) => descriptor64, - (false, true) => descriptorCoreClr64, - (true, false) => descriptor64ServerGC, - (true, true) => descriptorCoreClr64ServerGC, + 0 => descriptor64, + RemoteProcessConfiguration.Core => descriptorCoreClr64, + RemoteProcessConfiguration.ServerGC => descriptor64ServerGC, + RemoteProcessConfiguration.Core | RemoteProcessConfiguration.ServerGC => descriptorCoreClr64ServerGC, + _ => throw ExceptionUtilities.Unreachable }; } diff --git a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs index 3569e9de6c292..73769d50adacd 100644 --- a/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs +++ b/src/Workspaces/Remote/Core/ServiceHubRemoteHostClient.cs @@ -32,12 +32,11 @@ internal sealed partial class ServiceHubRemoteHostClient : RemoteHostClient private readonly IRemoteHostClientShutdownCancellationService? _shutdownCancellationService; private readonly IRemoteServiceCallbackDispatcherProvider _callbackDispatcherProvider; - private readonly bool _isRemoteHostServerGC; - private readonly bool _isRemoteHostCoreClr; + public readonly RemoteProcessConfiguration Configuration; private ServiceHubRemoteHostClient( HostWorkspaceServices services, - IGlobalOptionService globalOptions, + RemoteProcessConfiguration configuration, ServiceBrokerClient serviceBrokerClient, HubClient hubClient, IRemoteServiceCallbackDispatcherProvider callbackDispatcherProvider) @@ -53,13 +52,12 @@ private ServiceHubRemoteHostClient( _assetStorage = services.GetRequiredService().AssetStorage; _errorReportingService = services.GetService(); _shutdownCancellationService = services.GetService(); - _isRemoteHostServerGC = RemoteHostOptions.IsServiceHubProcessServerGC(globalOptions); - _isRemoteHostCoreClr = RemoteHostOptions.IsServiceHubProcessCoreClr(globalOptions); + Configuration = configuration; } public static async Task CreateAsync( HostWorkspaceServices services, - IGlobalOptionService globalOptions, + RemoteProcessConfiguration configuration, AsynchronousOperationListenerProvider listenerProvider, IServiceBroker serviceBroker, RemoteServiceCallbackDispatcherRegistry callbackDispatchers, @@ -74,7 +72,7 @@ public static async Task CreateAsync( var hubClient = new HubClient("ManagedLanguage.IDE.RemoteHostClient"); - var client = new ServiceHubRemoteHostClient(services, globalOptions, serviceBrokerClient, hubClient, callbackDispatchers); + var client = new ServiceHubRemoteHostClient(services, configuration, serviceBrokerClient, hubClient, callbackDispatchers); var syntaxTreeConfigurationService = services.GetService(); if (syntaxTreeConfigurationService != null) @@ -104,7 +102,7 @@ public override RemoteServiceConnection CreateConnection(object? callbackT /// internal RemoteServiceConnection CreateConnection(ServiceDescriptors descriptors, IRemoteServiceCallbackDispatcherProvider callbackDispatcherProvider, object? callbackTarget) where T : class { - var descriptor = descriptors.GetServiceDescriptor(typeof(T), _isRemoteHostServerGC, _isRemoteHostCoreClr); + var descriptor = descriptors.GetServiceDescriptor(typeof(T), Configuration); var callbackDispatcher = (descriptor.ClientInterface != null) ? callbackDispatcherProvider.GetDispatcher(typeof(T)) : null; return new BrokeredServiceConnection( diff --git a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs index 6f8a33d9e34ba..b410618048d0c 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AssetSynchronization/RemoteAssetSynchronizationService.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Remote { /// - /// This service is used by the to proactively update the solution snapshot in + /// This service is used by the SolutionChecksumUpdater to proactively update the solution snapshot in /// the out-of-process workspace. We do this to limit the amount of time required to synchronize a solution over after an edit /// once a feature is asking for a snapshot. /// From 02603da53bc1ef505c74c6fa7c94a948b738c27a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 12:29:43 -0800 Subject: [PATCH 072/187] Remove potential partial snapshot in codefixservice --- .../Core/Portable/CodeFixes/CodeFixService.cs | 32 +++++++++++++------ .../Extensions/ILanguageMetadataExtensions.cs | 8 ++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 1c186eab3a7d8..3d5b6711644b2 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -40,12 +40,11 @@ internal partial class CodeFixService : ICodeFixService private readonly IDiagnosticAnalyzerService _diagnosticService; private readonly ImmutableArray> _fixers; - private readonly Dictionary>> _fixersPerLanguageMap; + private readonly ImmutableDictionary>> _fixersPerLanguageMap; private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new(); // Shared by project fixers and workspace fixers. - private readonly Lazy> _lazyFixerToMetadataMap; private readonly ConditionalWeakTable _analyzerReferenceToFixersMap = new(); private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeFixProvider = r => new ProjectCodeFixProvider(r); private readonly ImmutableDictionary>> _configurationProvidersMap; @@ -56,6 +55,7 @@ internal partial class CodeFixService : ICodeFixService private ImmutableDictionary> _fixerToFixableIdsMap = ImmutableDictionary>.Empty; private ImmutableDictionary _fixAllProviderMap = ImmutableDictionary.Empty; + private ImmutableDictionary _fixerToMetadataMap = ImmutableDictionary.Empty; [ImportingConstructor] [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] @@ -71,13 +71,9 @@ public CodeFixService( _fixers = fixers.ToImmutableArray(); _fixersPerLanguageMap = _fixers.ToPerLanguageMapWithMultipleLanguages(); - _lazyFixerToMetadataMap = new(() => fixers.Where(service => service.IsValueCreated).ToImmutableDictionary(service => service.Value, service => service.Metadata)); - _configurationProvidersMap = GetConfigurationProvidersPerLanguageMap(configurationProviders); } - private ImmutableDictionary FixerToMetadataMap => _lazyFixerToMetadataMap.Value; - public async Task GetMostSevereFixableDiagnosticAsync( Document document, TextSpan range, CancellationToken cancellationToken) { @@ -433,7 +429,7 @@ await AppendFixesOrConfigurationsAsync( getFixes: dxs => { var fixerName = fixer.GetType().Name; - FixerToMetadataMap.TryGetValue(fixer, out var fixerMetadata); + var fixerMetadata = TryGetMetadata(fixer); using (addOperationScope(fixerName)) using (RoslynEventSource.LogInformationalBlock(FunctionId.CodeFixes_GetCodeFixesAsync, fixerName, cancellationToken)) @@ -468,6 +464,24 @@ await AppendFixesOrConfigurationsAsync( } } + private CodeChangeProviderMetadata? TryGetMetadata(CodeFixProvider fixer) + { + return ImmutableInterlocked.GetOrAdd( + ref _fixerToMetadataMap, + fixer, + static (fixer, fixers) => + { + foreach (var lazy in fixers) + { + if (lazy.IsValueCreated && lazy.Value == fixer) + return lazy.Metadata; + } + + return null; + }, + _fixers); + } + private static async Task> GetCodeFixesAsync( Document document, TextSpan span, CodeFixProvider fixer, CodeChangeProviderMetadata? fixerMetadata, CodeActionOptions options, ImmutableArray diagnostics, @@ -611,7 +625,7 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) } // If the fix provider supports fix all occurrences, then get the corresponding FixAllProviderInfo and fix all context. - var fixAllProviderInfo = extensionManager.PerformFunction(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null); + var fixAllProviderInfo = extensionManager.PerformFunction(fixer, () => ImmutableInterlocked.GetOrAdd(ref _fixAllProviderMap, fixer, FixAllProviderInfo.Create), defaultValue: null); FixAllState? fixAllState = null; var supportedScopes = ImmutableArray.Empty; @@ -890,7 +904,7 @@ private static ImmutableDictionary GetConfigurationFixProviders(List> languageKindAndFixers) + static ImmutableArray GetConfigurationFixProviders(ImmutableArray> languageKindAndFixers) { using var builderDisposer = ArrayBuilder.GetInstance(out var builder); var orderedLanguageKindAndFixers = ExtensionOrderer.Order(languageKindAndFixers); diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs index b67e08139188d..2693c87039496 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ILanguageMetadataExtensions.cs @@ -41,10 +41,10 @@ public static ImmutableDictionary new KeyValuePair>>(kvp.Key, kvp.Value.ToImmutableAndFree())).ToImmutableDictionary(); } - public static Dictionary>> ToPerLanguageMapWithMultipleLanguages(this IEnumerable> services) + public static ImmutableDictionary>> ToPerLanguageMapWithMultipleLanguages(this IEnumerable> services) where TMetadata : ILanguagesMetadata { - var map = new Dictionary>>(); + using var _ = PooledDictionary>>.GetInstance(out var map); foreach (var service in services) { @@ -52,13 +52,13 @@ public static Dictionary>> ToPerLanguag { if (!string.IsNullOrEmpty(language)) { - var list = map.GetOrAdd(language, _ => new List>()); + var list = map.GetOrAdd(language, _ => ArrayBuilder>.GetInstance()); list.Add(service); } } } - return map; + return map.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableAndFree()); } } } From b89ec3d4bde3ebffea2a820dd8742b9050df115f Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 16 Feb 2022 14:00:38 -0800 Subject: [PATCH 073/187] Move VisualStudioProject.BatchingDocumentCollection to it's own file This is just a direct invocation of the refactoring; no other work was done. --- ...tudioProject.BatchingDocumentCollection.cs | 623 ++++++++++++++++++ .../ProjectSystem/VisualStudioProject.cs | 603 +---------------- 2 files changed, 624 insertions(+), 602 deletions(-) create mode 100644 src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs new file mode 100644 index 0000000000000..fa83aa1b49ec3 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.BatchingDocumentCollection.cs @@ -0,0 +1,623 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +{ + internal sealed partial class VisualStudioProject + { + /// + /// Helper class to manage collections of source-file like things; this exists just to avoid duplicating all the logic for regular source files + /// and additional files. + /// + /// This class should be free-threaded, and any synchronization is done via . + /// This class is otherwise free to operate on private members of if needed. + private sealed class BatchingDocumentCollection + { + private readonly VisualStudioProject _project; + + /// + /// The map of file paths to the underlying . This document may exist in or has been + /// pushed to the actual workspace. + /// + private readonly Dictionary _documentPathsToDocumentIds = new(StringComparer.OrdinalIgnoreCase); + + /// + /// A map of explicitly-added "always open" and their associated . This does not contain + /// any regular files that have been open. + /// + private IBidirectionalMap _sourceTextContainersToDocumentIds = BidirectionalMap.Empty; + + /// + /// The map of to whose got added into + /// + private readonly Dictionary _documentIdToDynamicFileInfoProvider = new(); + + /// + /// The current list of documents that are to be added in this batch. + /// + private readonly ImmutableArray.Builder _documentsAddedInBatch = ImmutableArray.CreateBuilder(); + + /// + /// The current list of documents that are being removed in this batch. Once the document is in this list, it is no longer in . + /// + private readonly List _documentsRemovedInBatch = new(); + + /// + /// The current list of document file paths that will be ordered in a batch. + /// + private ImmutableList? _orderedDocumentsInBatch = null; + + private readonly Func _documentAlreadyInWorkspace; + private readonly Action _documentAddAction; + private readonly Action _documentRemoveAction; + private readonly Func _documentTextLoaderChangedAction; + private readonly WorkspaceChangeKind _documentChangedWorkspaceKind; + + public BatchingDocumentCollection(VisualStudioProject project, + Func documentAlreadyInWorkspace, + Action documentAddAction, + Action documentRemoveAction, + Func documentTextLoaderChangedAction, + WorkspaceChangeKind documentChangedWorkspaceKind) + { + _project = project; + _documentAlreadyInWorkspace = documentAlreadyInWorkspace; + _documentAddAction = documentAddAction; + _documentRemoveAction = documentRemoveAction; + _documentTextLoaderChangedAction = documentTextLoaderChangedAction; + _documentChangedWorkspaceKind = documentChangedWorkspaceKind; + } + + public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, ImmutableArray folders) + { + if (string.IsNullOrEmpty(fullPath)) + { + throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); + } + + var documentId = DocumentId.CreateNewId(_project.Id, fullPath); + var textLoader = new FileTextLoader(fullPath, defaultEncoding: null); + var documentInfo = DocumentInfo.Create( + documentId, + FileNameUtilities.GetFileName(fullPath), + folders: folders.IsDefault ? null : folders, + sourceCodeKind: sourceCodeKind, + loader: textLoader, + filePath: fullPath, + isGenerated: false); + + using (_project._gate.DisposableWait()) + { + if (_documentPathsToDocumentIds.ContainsKey(fullPath)) + { + throw new ArgumentException($"'{fullPath}' has already been added to this project.", nameof(fullPath)); + } + + // If we have an ordered document ids batch, we need to add the document id to the end of it as well. + _orderedDocumentsInBatch = _orderedDocumentsInBatch?.Add(documentId); + + _documentPathsToDocumentIds.Add(fullPath, documentId); + _project._documentFileWatchingTokens.Add(documentId, _project._documentFileChangeContext.EnqueueWatchingFile(fullPath)); + + if (_project._activeBatchScopes > 0) + { + _documentsAddedInBatch.Add(documentInfo); + } + else + { + _project._workspace.ApplyChangeToWorkspace(w => _documentAddAction(w, documentInfo)); + _project._workspace.QueueCheckForFilesBeingOpen(ImmutableArray.Create(fullPath)); + } + } + + return documentId; + } + + public DocumentId AddTextContainer(SourceTextContainer textContainer, string fullPath, SourceCodeKind sourceCodeKind, ImmutableArray folders, bool designTimeOnly, IDocumentServiceProvider? documentServiceProvider) + { + if (textContainer == null) + { + throw new ArgumentNullException(nameof(textContainer)); + } + + var documentId = DocumentId.CreateNewId(_project.Id, fullPath); + var textLoader = new SourceTextLoader(textContainer, fullPath); + var documentInfo = DocumentInfo.Create( + documentId, + FileNameUtilities.GetFileName(fullPath), + folders: folders.NullToEmpty(), + sourceCodeKind: sourceCodeKind, + loader: textLoader, + filePath: fullPath, + isGenerated: false, + designTimeOnly: designTimeOnly, + documentServiceProvider: documentServiceProvider); + + using (_project._gate.DisposableWait()) + { + if (_sourceTextContainersToDocumentIds.ContainsKey(textContainer)) + { + throw new ArgumentException($"{nameof(textContainer)} is already added to this project.", nameof(textContainer)); + } + + if (fullPath != null) + { + if (_documentPathsToDocumentIds.ContainsKey(fullPath)) + { + throw new ArgumentException($"'{fullPath}' has already been added to this project."); + } + + _documentPathsToDocumentIds.Add(fullPath, documentId); + } + + _sourceTextContainersToDocumentIds = _sourceTextContainersToDocumentIds.Add(textContainer, documentInfo.Id); + + if (_project._activeBatchScopes > 0) + { + _documentsAddedInBatch.Add(documentInfo); + } + else + { + _project._workspace.ApplyChangeToWorkspace(w => + { + _project._workspace.AddDocumentToDocumentsNotFromFiles_NoLock(documentInfo.Id); + _documentAddAction(w, documentInfo); + w.OnDocumentOpened(documentInfo.Id, textContainer); + }); + } + } + + return documentId; + } + + public void AddDynamicFile_NoLock(IDynamicFileInfoProvider fileInfoProvider, DynamicFileInfo fileInfo, ImmutableArray folders) + { + Debug.Assert(_project._gate.CurrentCount == 0); + + var documentInfo = CreateDocumentInfoFromFileInfo(fileInfo, folders.NullToEmpty()); + + // Generally, DocumentInfo.FilePath can be null, but we always have file paths for dynamic files. + Contract.ThrowIfNull(documentInfo.FilePath); + var documentId = documentInfo.Id; + + var filePath = documentInfo.FilePath; + if (_documentPathsToDocumentIds.ContainsKey(filePath)) + { + throw new ArgumentException($"'{filePath}' has already been added to this project.", nameof(filePath)); + } + + // If we have an ordered document ids batch, we need to add the document id to the end of it as well. + _orderedDocumentsInBatch = _orderedDocumentsInBatch?.Add(documentId); + + _documentPathsToDocumentIds.Add(filePath, documentId); + + _documentIdToDynamicFileInfoProvider.Add(documentId, fileInfoProvider); + + if (_project._eventSubscriptionTracker.Add(fileInfoProvider)) + { + // subscribe to the event when we use this provider the first time + fileInfoProvider.Updated += _project.OnDynamicFileInfoUpdated; + } + + if (_project._activeBatchScopes > 0) + { + _documentsAddedInBatch.Add(documentInfo); + } + else + { + // right now, assumption is dynamically generated file can never be opened in editor + _project._workspace.ApplyChangeToWorkspace(w => _documentAddAction(w, documentInfo)); + } + } + + public IDynamicFileInfoProvider RemoveDynamicFile_NoLock(string fullPath) + { + Debug.Assert(_project._gate.CurrentCount == 0); + + if (string.IsNullOrEmpty(fullPath)) + { + throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); + } + + if (!_documentPathsToDocumentIds.TryGetValue(fullPath, out var documentId) || + !_documentIdToDynamicFileInfoProvider.TryGetValue(documentId, out var fileInfoProvider)) + { + throw new ArgumentException($"'{fullPath}' is not a dynamic file of this project."); + } + + _documentIdToDynamicFileInfoProvider.Remove(documentId); + + RemoveFileInternal(documentId, fullPath); + + return fileInfoProvider; + } + + public void RemoveFile(string fullPath) + { + if (string.IsNullOrEmpty(fullPath)) + { + throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); + } + + using (_project._gate.DisposableWait()) + { + if (!_documentPathsToDocumentIds.TryGetValue(fullPath, out var documentId)) + { + throw new ArgumentException($"'{fullPath}' is not a source file of this project."); + } + + _project._documentFileChangeContext.StopWatchingFile(_project._documentFileWatchingTokens[documentId]); + _project._documentFileWatchingTokens.Remove(documentId); + + RemoveFileInternal(documentId, fullPath); + } + } + + private void RemoveFileInternal(DocumentId documentId, string fullPath) + { + _orderedDocumentsInBatch = _orderedDocumentsInBatch?.Remove(documentId); + _documentPathsToDocumentIds.Remove(fullPath); + + // There are two cases: + // + // 1. This file is actually been pushed to the workspace, and we need to remove it (either + // as a part of the active batch or immediately) + // 2. It hasn't been pushed yet, but is contained in _documentsAddedInBatch + if (_documentAlreadyInWorkspace(_project._workspace.CurrentSolution, documentId)) + { + if (_project._activeBatchScopes > 0) + { + _documentsRemovedInBatch.Add(documentId); + } + else + { + _project._workspace.ApplyChangeToWorkspace(w => _documentRemoveAction(w, documentId)); + } + } + else + { + for (var i = 0; i < _documentsAddedInBatch.Count; i++) + { + if (_documentsAddedInBatch[i].Id == documentId) + { + _documentsAddedInBatch.RemoveAt(i); + break; + } + } + } + } + + public void RemoveTextContainer(SourceTextContainer textContainer) + { + if (textContainer == null) + { + throw new ArgumentNullException(nameof(textContainer)); + } + + using (_project._gate.DisposableWait()) + { + if (!_sourceTextContainersToDocumentIds.TryGetValue(textContainer, out var documentId)) + { + throw new ArgumentException($"{nameof(textContainer)} is not a text container added to this project."); + } + + _sourceTextContainersToDocumentIds = _sourceTextContainersToDocumentIds.RemoveKey(textContainer); + + // if the TextContainer had a full path provided, remove it from the map. + var entry = _documentPathsToDocumentIds.Where(kv => kv.Value == documentId).FirstOrDefault(); + if (entry.Key != null) + { + _documentPathsToDocumentIds.Remove(entry.Key); + } + + // There are two cases: + // + // 1. This file is actually been pushed to the workspace, and we need to remove it (either + // as a part of the active batch or immediately) + // 2. It hasn't been pushed yet, but is contained in _documentsAddedInBatch + if (_project._workspace.CurrentSolution.GetDocument(documentId) != null) + { + if (_project._activeBatchScopes > 0) + { + _documentsRemovedInBatch.Add(documentId); + } + else + { + _project._workspace.ApplyChangeToWorkspace(w => + { + // Just pass null for the filePath, since this document is immediately being removed + // anyways -- whatever we set won't really be read since the next change will + // come through. + // TODO: Can't we just remove the document without closing it? + w.OnDocumentClosed(documentId, new SourceTextLoader(textContainer, filePath: null)); + _documentRemoveAction(w, documentId); + _project._workspace.RemoveDocumentToDocumentsNotFromFiles_NoLock(documentId); + }); + } + } + else + { + for (var i = 0; i < _documentsAddedInBatch.Count; i++) + { + if (_documentsAddedInBatch[i].Id == documentId) + { + _documentsAddedInBatch.RemoveAt(i); + break; + } + } + } + } + } + + public bool ContainsFile(string fullPath) + { + if (string.IsNullOrEmpty(fullPath)) + { + throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); + } + + using (_project._gate.DisposableWait()) + { + return _documentPathsToDocumentIds.ContainsKey(fullPath); + } + } + + public async ValueTask ProcessRegularFileChangesAsync(ImmutableArray filePaths) + { + using (await _project._gate.DisposableWaitAsync().ConfigureAwait(false)) + { + // If our project has already been removed, this is a stale notification, and we can disregard. + if (_project.HasBeenRemoved) + { + return; + } + + var documentsToChange = ArrayBuilder<(DocumentId, TextLoader)>.GetInstance(filePaths.Length); + + foreach (var filePath in filePaths) + { + if (_documentPathsToDocumentIds.TryGetValue(filePath, out var documentId)) + { + // We create file watching prior to pushing the file to the workspace in batching, so it's + // possible we might see a file change notification early. In this case, toss it out. Since + // all adds/removals of documents for this project happen under our lock, it's safe to do this + // check without taking the main workspace lock. We don't have to check for documents removed in + // the batch, since those have already been removed out of _documentPathsToDocumentIds. + if (!_documentsAddedInBatch.Any(d => d.Id == documentId)) + { + documentsToChange.Add((documentId, new FileTextLoader(filePath, defaultEncoding: null))); + } + } + } + + // Nothing actually matched, so we're done + if (documentsToChange.Count == 0) + { + return; + } + + await _project._workspace.ApplyBatchChangeToWorkspaceMaybeAsync(useAsync: true, s => + { + var accumulator = new SolutionChangeAccumulator(s); + + foreach (var (documentId, textLoader) in documentsToChange) + { + if (!s.Workspace.IsDocumentOpen(documentId)) + { + accumulator.UpdateSolutionForDocumentAction( + _documentTextLoaderChangedAction(accumulator.Solution, documentId, textLoader), + _documentChangedWorkspaceKind, + SpecializedCollections.SingletonEnumerable(documentId)); + } + } + + return accumulator; + }).ConfigureAwait(false); + + documentsToChange.Free(); + } + } + + /// + /// Process file content changes + /// + /// filepath given from project system + /// filepath used in workspace. it might be different than projectSystemFilePath + public void ProcessDynamicFileChange(string projectSystemFilePath, string workspaceFilePath) + { + using (_project._gate.DisposableWait()) + { + // If our project has already been removed, this is a stale notification, and we can disregard. + if (_project.HasBeenRemoved) + { + return; + } + + if (_documentPathsToDocumentIds.TryGetValue(workspaceFilePath, out var documentId)) + { + // We create file watching prior to pushing the file to the workspace in batching, so it's + // possible we might see a file change notification early. In this case, toss it out. Since + // all adds/removals of documents for this project happen under our lock, it's safe to do this + // check without taking the main workspace lock. We don't have to check for documents removed in + // the batch, since those have already been removed out of _documentPathsToDocumentIds. + if (_documentsAddedInBatch.Any(d => d.Id == documentId)) + { + return; + } + + Contract.ThrowIfFalse(_documentIdToDynamicFileInfoProvider.TryGetValue(documentId, out var fileInfoProvider)); + + _project._workspace.ApplyChangeToWorkspace(w => + { + if (w.IsDocumentOpen(documentId)) + { + return; + } + + // we do not expect JTF to be used around this code path. and contract of fileInfoProvider is it being real free-threaded + // meaning it can't use JTF to go back to UI thread. + // so, it is okay for us to call regular ".Result" on a task here. + var fileInfo = fileInfoProvider.GetDynamicFileInfoAsync( + _project.Id, _project._filePath, projectSystemFilePath, CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); + + // Right now we're only supporting dynamic files as actual source files, so it's OK to call GetDocument here + var document = w.CurrentSolution.GetRequiredDocument(documentId); + + var documentInfo = DocumentInfo.Create( + document.Id, + document.Name, + document.Folders, + document.SourceCodeKind, + loader: fileInfo.TextLoader, + document.FilePath, + document.State.Attributes.IsGenerated, + document.State.Attributes.DesignTimeOnly, + documentServiceProvider: fileInfo.DocumentServiceProvider); + + w.OnDocumentReloaded(documentInfo); + }); + } + } + } + + public void ReorderFiles(ImmutableArray filePaths) + { + if (filePaths.IsEmpty) + { + throw new ArgumentOutOfRangeException("The specified files are empty.", nameof(filePaths)); + } + + using (_project._gate.DisposableWait()) + { + if (_documentPathsToDocumentIds.Count != filePaths.Length) + { + throw new ArgumentException("The specified files do not equal the project document count.", nameof(filePaths)); + } + + var documentIds = ImmutableList.CreateBuilder(); + + foreach (var filePath in filePaths) + { + if (_documentPathsToDocumentIds.TryGetValue(filePath, out var documentId)) + { + documentIds.Add(documentId); + } + else + { + throw new InvalidOperationException($"The file '{filePath}' does not exist in the project."); + } + } + + if (_project._activeBatchScopes > 0) + { + _orderedDocumentsInBatch = documentIds.ToImmutable(); + } + else + { + _project._workspace.ApplyChangeToWorkspace(_project.Id, solution => solution.WithProjectDocumentsOrder(_project.Id, documentIds.ToImmutable())); + } + } + } + + internal void UpdateSolutionForBatch( + SolutionChangeAccumulator solutionChanges, + ImmutableArray.Builder documentFileNamesAdded, + List<(DocumentId documentId, SourceTextContainer textContainer)> documentsToOpen, + Func, Solution> addDocuments, + WorkspaceChangeKind addDocumentChangeKind, + Func, Solution> removeDocuments, + WorkspaceChangeKind removeDocumentChangeKind) + { + // Document adding... + solutionChanges.UpdateSolutionForDocumentAction( + newSolution: addDocuments(solutionChanges.Solution, _documentsAddedInBatch.ToImmutable()), + changeKind: addDocumentChangeKind, + documentIds: _documentsAddedInBatch.Select(d => d.Id)); + + foreach (var documentInfo in _documentsAddedInBatch) + { + Contract.ThrowIfNull(documentInfo.FilePath, "We shouldn't be adding documents without file paths."); + documentFileNamesAdded.Add(documentInfo.FilePath); + + if (_sourceTextContainersToDocumentIds.TryGetKey(documentInfo.Id, out var textContainer)) + { + documentsToOpen.Add((documentInfo.Id, textContainer)); + } + } + + ClearAndZeroCapacity(_documentsAddedInBatch); + + // Document removing... + solutionChanges.UpdateSolutionForRemovedDocumentAction(removeDocuments(solutionChanges.Solution, _documentsRemovedInBatch.ToImmutableArray()), + removeDocumentChangeKind, + _documentsRemovedInBatch); + + ClearAndZeroCapacity(_documentsRemovedInBatch); + + // Update project's order of documents. + if (_orderedDocumentsInBatch != null) + { + solutionChanges.UpdateSolutionForProjectAction( + _project.Id, + solutionChanges.Solution.WithProjectDocumentsOrder(_project.Id, _orderedDocumentsInBatch)); + _orderedDocumentsInBatch = null; + } + } + + private DocumentInfo CreateDocumentInfoFromFileInfo(DynamicFileInfo fileInfo, ImmutableArray folders) + { + Contract.ThrowIfTrue(folders.IsDefault); + + // we use this file path for editorconfig. + var filePath = fileInfo.FilePath; + + var name = FileNameUtilities.GetFileName(filePath); + var documentId = DocumentId.CreateNewId(_project.Id, filePath); + + var textLoader = fileInfo.TextLoader; + var documentServiceProvider = fileInfo.DocumentServiceProvider; + + return DocumentInfo.Create( + documentId, + name, + folders: folders, + sourceCodeKind: fileInfo.SourceCodeKind, + loader: textLoader, + filePath: filePath, + isGenerated: false, + designTimeOnly: true, + documentServiceProvider: documentServiceProvider); + } + + private sealed class SourceTextLoader : TextLoader + { + private readonly SourceTextContainer _textContainer; + private readonly string? _filePath; + + public SourceTextLoader(SourceTextContainer textContainer, string? filePath) + { + _textContainer = textContainer; + _filePath = filePath; + } + + public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) + => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); + } + } + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs index a477703d61ba2..2f2373de209ce 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; @@ -24,7 +23,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem { - internal sealed class VisualStudioProject + internal sealed partial class VisualStudioProject { private static readonly ImmutableArray s_defaultMetadataReferenceProperties = ImmutableArray.Create(default(MetadataReferenceProperties)); @@ -1305,605 +1304,5 @@ private static void ClearAndZeroCapacity(ImmutableArray.Builder list) list.Clear(); list.Capacity = 0; } - - /// - /// Helper class to manage collections of source-file like things; this exists just to avoid duplicating all the logic for regular source files - /// and additional files. - /// - /// This class should be free-threaded, and any synchronization is done via . - /// This class is otherwise free to operate on private members of if needed. - private sealed class BatchingDocumentCollection - { - private readonly VisualStudioProject _project; - - /// - /// The map of file paths to the underlying . This document may exist in or has been - /// pushed to the actual workspace. - /// - private readonly Dictionary _documentPathsToDocumentIds = new(StringComparer.OrdinalIgnoreCase); - - /// - /// A map of explicitly-added "always open" and their associated . This does not contain - /// any regular files that have been open. - /// - private IBidirectionalMap _sourceTextContainersToDocumentIds = BidirectionalMap.Empty; - - /// - /// The map of to whose got added into - /// - private readonly Dictionary _documentIdToDynamicFileInfoProvider = new(); - - /// - /// The current list of documents that are to be added in this batch. - /// - private readonly ImmutableArray.Builder _documentsAddedInBatch = ImmutableArray.CreateBuilder(); - - /// - /// The current list of documents that are being removed in this batch. Once the document is in this list, it is no longer in . - /// - private readonly List _documentsRemovedInBatch = new(); - - /// - /// The current list of document file paths that will be ordered in a batch. - /// - private ImmutableList? _orderedDocumentsInBatch = null; - - private readonly Func _documentAlreadyInWorkspace; - private readonly Action _documentAddAction; - private readonly Action _documentRemoveAction; - private readonly Func _documentTextLoaderChangedAction; - private readonly WorkspaceChangeKind _documentChangedWorkspaceKind; - - public BatchingDocumentCollection(VisualStudioProject project, - Func documentAlreadyInWorkspace, - Action documentAddAction, - Action documentRemoveAction, - Func documentTextLoaderChangedAction, - WorkspaceChangeKind documentChangedWorkspaceKind) - { - _project = project; - _documentAlreadyInWorkspace = documentAlreadyInWorkspace; - _documentAddAction = documentAddAction; - _documentRemoveAction = documentRemoveAction; - _documentTextLoaderChangedAction = documentTextLoaderChangedAction; - _documentChangedWorkspaceKind = documentChangedWorkspaceKind; - } - - public DocumentId AddFile(string fullPath, SourceCodeKind sourceCodeKind, ImmutableArray folders) - { - if (string.IsNullOrEmpty(fullPath)) - { - throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); - } - - var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new FileTextLoader(fullPath, defaultEncoding: null); - var documentInfo = DocumentInfo.Create( - documentId, - FileNameUtilities.GetFileName(fullPath), - folders: folders.IsDefault ? null : folders, - sourceCodeKind: sourceCodeKind, - loader: textLoader, - filePath: fullPath, - isGenerated: false); - - using (_project._gate.DisposableWait()) - { - if (_documentPathsToDocumentIds.ContainsKey(fullPath)) - { - throw new ArgumentException($"'{fullPath}' has already been added to this project.", nameof(fullPath)); - } - - // If we have an ordered document ids batch, we need to add the document id to the end of it as well. - _orderedDocumentsInBatch = _orderedDocumentsInBatch?.Add(documentId); - - _documentPathsToDocumentIds.Add(fullPath, documentId); - _project._documentFileWatchingTokens.Add(documentId, _project._documentFileChangeContext.EnqueueWatchingFile(fullPath)); - - if (_project._activeBatchScopes > 0) - { - _documentsAddedInBatch.Add(documentInfo); - } - else - { - _project._workspace.ApplyChangeToWorkspace(w => _documentAddAction(w, documentInfo)); - _project._workspace.QueueCheckForFilesBeingOpen(ImmutableArray.Create(fullPath)); - } - } - - return documentId; - } - - public DocumentId AddTextContainer(SourceTextContainer textContainer, string fullPath, SourceCodeKind sourceCodeKind, ImmutableArray folders, bool designTimeOnly, IDocumentServiceProvider? documentServiceProvider) - { - if (textContainer == null) - { - throw new ArgumentNullException(nameof(textContainer)); - } - - var documentId = DocumentId.CreateNewId(_project.Id, fullPath); - var textLoader = new SourceTextLoader(textContainer, fullPath); - var documentInfo = DocumentInfo.Create( - documentId, - FileNameUtilities.GetFileName(fullPath), - folders: folders.NullToEmpty(), - sourceCodeKind: sourceCodeKind, - loader: textLoader, - filePath: fullPath, - isGenerated: false, - designTimeOnly: designTimeOnly, - documentServiceProvider: documentServiceProvider); - - using (_project._gate.DisposableWait()) - { - if (_sourceTextContainersToDocumentIds.ContainsKey(textContainer)) - { - throw new ArgumentException($"{nameof(textContainer)} is already added to this project.", nameof(textContainer)); - } - - if (fullPath != null) - { - if (_documentPathsToDocumentIds.ContainsKey(fullPath)) - { - throw new ArgumentException($"'{fullPath}' has already been added to this project."); - } - - _documentPathsToDocumentIds.Add(fullPath, documentId); - } - - _sourceTextContainersToDocumentIds = _sourceTextContainersToDocumentIds.Add(textContainer, documentInfo.Id); - - if (_project._activeBatchScopes > 0) - { - _documentsAddedInBatch.Add(documentInfo); - } - else - { - _project._workspace.ApplyChangeToWorkspace(w => - { - _project._workspace.AddDocumentToDocumentsNotFromFiles_NoLock(documentInfo.Id); - _documentAddAction(w, documentInfo); - w.OnDocumentOpened(documentInfo.Id, textContainer); - }); - } - } - - return documentId; - } - - public void AddDynamicFile_NoLock(IDynamicFileInfoProvider fileInfoProvider, DynamicFileInfo fileInfo, ImmutableArray folders) - { - Debug.Assert(_project._gate.CurrentCount == 0); - - var documentInfo = CreateDocumentInfoFromFileInfo(fileInfo, folders.NullToEmpty()); - - // Generally, DocumentInfo.FilePath can be null, but we always have file paths for dynamic files. - Contract.ThrowIfNull(documentInfo.FilePath); - var documentId = documentInfo.Id; - - var filePath = documentInfo.FilePath; - if (_documentPathsToDocumentIds.ContainsKey(filePath)) - { - throw new ArgumentException($"'{filePath}' has already been added to this project.", nameof(filePath)); - } - - // If we have an ordered document ids batch, we need to add the document id to the end of it as well. - _orderedDocumentsInBatch = _orderedDocumentsInBatch?.Add(documentId); - - _documentPathsToDocumentIds.Add(filePath, documentId); - - _documentIdToDynamicFileInfoProvider.Add(documentId, fileInfoProvider); - - if (_project._eventSubscriptionTracker.Add(fileInfoProvider)) - { - // subscribe to the event when we use this provider the first time - fileInfoProvider.Updated += _project.OnDynamicFileInfoUpdated; - } - - if (_project._activeBatchScopes > 0) - { - _documentsAddedInBatch.Add(documentInfo); - } - else - { - // right now, assumption is dynamically generated file can never be opened in editor - _project._workspace.ApplyChangeToWorkspace(w => _documentAddAction(w, documentInfo)); - } - } - - public IDynamicFileInfoProvider RemoveDynamicFile_NoLock(string fullPath) - { - Debug.Assert(_project._gate.CurrentCount == 0); - - if (string.IsNullOrEmpty(fullPath)) - { - throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); - } - - if (!_documentPathsToDocumentIds.TryGetValue(fullPath, out var documentId) || - !_documentIdToDynamicFileInfoProvider.TryGetValue(documentId, out var fileInfoProvider)) - { - throw new ArgumentException($"'{fullPath}' is not a dynamic file of this project."); - } - - _documentIdToDynamicFileInfoProvider.Remove(documentId); - - RemoveFileInternal(documentId, fullPath); - - return fileInfoProvider; - } - - public void RemoveFile(string fullPath) - { - if (string.IsNullOrEmpty(fullPath)) - { - throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); - } - - using (_project._gate.DisposableWait()) - { - if (!_documentPathsToDocumentIds.TryGetValue(fullPath, out var documentId)) - { - throw new ArgumentException($"'{fullPath}' is not a source file of this project."); - } - - _project._documentFileChangeContext.StopWatchingFile(_project._documentFileWatchingTokens[documentId]); - _project._documentFileWatchingTokens.Remove(documentId); - - RemoveFileInternal(documentId, fullPath); - } - } - - private void RemoveFileInternal(DocumentId documentId, string fullPath) - { - _orderedDocumentsInBatch = _orderedDocumentsInBatch?.Remove(documentId); - _documentPathsToDocumentIds.Remove(fullPath); - - // There are two cases: - // - // 1. This file is actually been pushed to the workspace, and we need to remove it (either - // as a part of the active batch or immediately) - // 2. It hasn't been pushed yet, but is contained in _documentsAddedInBatch - if (_documentAlreadyInWorkspace(_project._workspace.CurrentSolution, documentId)) - { - if (_project._activeBatchScopes > 0) - { - _documentsRemovedInBatch.Add(documentId); - } - else - { - _project._workspace.ApplyChangeToWorkspace(w => _documentRemoveAction(w, documentId)); - } - } - else - { - for (var i = 0; i < _documentsAddedInBatch.Count; i++) - { - if (_documentsAddedInBatch[i].Id == documentId) - { - _documentsAddedInBatch.RemoveAt(i); - break; - } - } - } - } - - public void RemoveTextContainer(SourceTextContainer textContainer) - { - if (textContainer == null) - { - throw new ArgumentNullException(nameof(textContainer)); - } - - using (_project._gate.DisposableWait()) - { - if (!_sourceTextContainersToDocumentIds.TryGetValue(textContainer, out var documentId)) - { - throw new ArgumentException($"{nameof(textContainer)} is not a text container added to this project."); - } - - _sourceTextContainersToDocumentIds = _sourceTextContainersToDocumentIds.RemoveKey(textContainer); - - // if the TextContainer had a full path provided, remove it from the map. - var entry = _documentPathsToDocumentIds.Where(kv => kv.Value == documentId).FirstOrDefault(); - if (entry.Key != null) - { - _documentPathsToDocumentIds.Remove(entry.Key); - } - - // There are two cases: - // - // 1. This file is actually been pushed to the workspace, and we need to remove it (either - // as a part of the active batch or immediately) - // 2. It hasn't been pushed yet, but is contained in _documentsAddedInBatch - if (_project._workspace.CurrentSolution.GetDocument(documentId) != null) - { - if (_project._activeBatchScopes > 0) - { - _documentsRemovedInBatch.Add(documentId); - } - else - { - _project._workspace.ApplyChangeToWorkspace(w => - { - // Just pass null for the filePath, since this document is immediately being removed - // anyways -- whatever we set won't really be read since the next change will - // come through. - // TODO: Can't we just remove the document without closing it? - w.OnDocumentClosed(documentId, new SourceTextLoader(textContainer, filePath: null)); - _documentRemoveAction(w, documentId); - _project._workspace.RemoveDocumentToDocumentsNotFromFiles_NoLock(documentId); - }); - } - } - else - { - for (var i = 0; i < _documentsAddedInBatch.Count; i++) - { - if (_documentsAddedInBatch[i].Id == documentId) - { - _documentsAddedInBatch.RemoveAt(i); - break; - } - } - } - } - } - - public bool ContainsFile(string fullPath) - { - if (string.IsNullOrEmpty(fullPath)) - { - throw new ArgumentException($"{nameof(fullPath)} isn't a valid path.", nameof(fullPath)); - } - - using (_project._gate.DisposableWait()) - { - return _documentPathsToDocumentIds.ContainsKey(fullPath); - } - } - - public async ValueTask ProcessRegularFileChangesAsync(ImmutableArray filePaths) - { - using (await _project._gate.DisposableWaitAsync().ConfigureAwait(false)) - { - // If our project has already been removed, this is a stale notification, and we can disregard. - if (_project.HasBeenRemoved) - { - return; - } - - var documentsToChange = ArrayBuilder<(DocumentId, TextLoader)>.GetInstance(filePaths.Length); - - foreach (var filePath in filePaths) - { - if (_documentPathsToDocumentIds.TryGetValue(filePath, out var documentId)) - { - // We create file watching prior to pushing the file to the workspace in batching, so it's - // possible we might see a file change notification early. In this case, toss it out. Since - // all adds/removals of documents for this project happen under our lock, it's safe to do this - // check without taking the main workspace lock. We don't have to check for documents removed in - // the batch, since those have already been removed out of _documentPathsToDocumentIds. - if (!_documentsAddedInBatch.Any(d => d.Id == documentId)) - { - documentsToChange.Add((documentId, new FileTextLoader(filePath, defaultEncoding: null))); - } - } - } - - // Nothing actually matched, so we're done - if (documentsToChange.Count == 0) - { - return; - } - - await _project._workspace.ApplyBatchChangeToWorkspaceMaybeAsync(useAsync: true, s => - { - var accumulator = new SolutionChangeAccumulator(s); - - foreach (var (documentId, textLoader) in documentsToChange) - { - if (!s.Workspace.IsDocumentOpen(documentId)) - { - accumulator.UpdateSolutionForDocumentAction( - _documentTextLoaderChangedAction(accumulator.Solution, documentId, textLoader), - _documentChangedWorkspaceKind, - SpecializedCollections.SingletonEnumerable(documentId)); - } - } - - return accumulator; - }).ConfigureAwait(false); - - documentsToChange.Free(); - } - } - - /// - /// Process file content changes - /// - /// filepath given from project system - /// filepath used in workspace. it might be different than projectSystemFilePath - public void ProcessDynamicFileChange(string projectSystemFilePath, string workspaceFilePath) - { - using (_project._gate.DisposableWait()) - { - // If our project has already been removed, this is a stale notification, and we can disregard. - if (_project.HasBeenRemoved) - { - return; - } - - if (_documentPathsToDocumentIds.TryGetValue(workspaceFilePath, out var documentId)) - { - // We create file watching prior to pushing the file to the workspace in batching, so it's - // possible we might see a file change notification early. In this case, toss it out. Since - // all adds/removals of documents for this project happen under our lock, it's safe to do this - // check without taking the main workspace lock. We don't have to check for documents removed in - // the batch, since those have already been removed out of _documentPathsToDocumentIds. - if (_documentsAddedInBatch.Any(d => d.Id == documentId)) - { - return; - } - - Contract.ThrowIfFalse(_documentIdToDynamicFileInfoProvider.TryGetValue(documentId, out var fileInfoProvider)); - - _project._workspace.ApplyChangeToWorkspace(w => - { - if (w.IsDocumentOpen(documentId)) - { - return; - } - - // we do not expect JTF to be used around this code path. and contract of fileInfoProvider is it being real free-threaded - // meaning it can't use JTF to go back to UI thread. - // so, it is okay for us to call regular ".Result" on a task here. - var fileInfo = fileInfoProvider.GetDynamicFileInfoAsync( - _project.Id, _project._filePath, projectSystemFilePath, CancellationToken.None).WaitAndGetResult_CanCallOnBackground(CancellationToken.None); - - // Right now we're only supporting dynamic files as actual source files, so it's OK to call GetDocument here - var document = w.CurrentSolution.GetRequiredDocument(documentId); - - var documentInfo = DocumentInfo.Create( - document.Id, - document.Name, - document.Folders, - document.SourceCodeKind, - loader: fileInfo.TextLoader, - document.FilePath, - document.State.Attributes.IsGenerated, - document.State.Attributes.DesignTimeOnly, - documentServiceProvider: fileInfo.DocumentServiceProvider); - - w.OnDocumentReloaded(documentInfo); - }); - } - } - } - - public void ReorderFiles(ImmutableArray filePaths) - { - if (filePaths.IsEmpty) - { - throw new ArgumentOutOfRangeException("The specified files are empty.", nameof(filePaths)); - } - - using (_project._gate.DisposableWait()) - { - if (_documentPathsToDocumentIds.Count != filePaths.Length) - { - throw new ArgumentException("The specified files do not equal the project document count.", nameof(filePaths)); - } - - var documentIds = ImmutableList.CreateBuilder(); - - foreach (var filePath in filePaths) - { - if (_documentPathsToDocumentIds.TryGetValue(filePath, out var documentId)) - { - documentIds.Add(documentId); - } - else - { - throw new InvalidOperationException($"The file '{filePath}' does not exist in the project."); - } - } - - if (_project._activeBatchScopes > 0) - { - _orderedDocumentsInBatch = documentIds.ToImmutable(); - } - else - { - _project._workspace.ApplyChangeToWorkspace(_project.Id, solution => solution.WithProjectDocumentsOrder(_project.Id, documentIds.ToImmutable())); - } - } - } - - internal void UpdateSolutionForBatch( - SolutionChangeAccumulator solutionChanges, - ImmutableArray.Builder documentFileNamesAdded, - List<(DocumentId documentId, SourceTextContainer textContainer)> documentsToOpen, - Func, Solution> addDocuments, - WorkspaceChangeKind addDocumentChangeKind, - Func, Solution> removeDocuments, - WorkspaceChangeKind removeDocumentChangeKind) - { - // Document adding... - solutionChanges.UpdateSolutionForDocumentAction( - newSolution: addDocuments(solutionChanges.Solution, _documentsAddedInBatch.ToImmutable()), - changeKind: addDocumentChangeKind, - documentIds: _documentsAddedInBatch.Select(d => d.Id)); - - foreach (var documentInfo in _documentsAddedInBatch) - { - Contract.ThrowIfNull(documentInfo.FilePath, "We shouldn't be adding documents without file paths."); - documentFileNamesAdded.Add(documentInfo.FilePath); - - if (_sourceTextContainersToDocumentIds.TryGetKey(documentInfo.Id, out var textContainer)) - { - documentsToOpen.Add((documentInfo.Id, textContainer)); - } - } - - ClearAndZeroCapacity(_documentsAddedInBatch); - - // Document removing... - solutionChanges.UpdateSolutionForRemovedDocumentAction(removeDocuments(solutionChanges.Solution, _documentsRemovedInBatch.ToImmutableArray()), - removeDocumentChangeKind, - _documentsRemovedInBatch); - - ClearAndZeroCapacity(_documentsRemovedInBatch); - - // Update project's order of documents. - if (_orderedDocumentsInBatch != null) - { - solutionChanges.UpdateSolutionForProjectAction( - _project.Id, - solutionChanges.Solution.WithProjectDocumentsOrder(_project.Id, _orderedDocumentsInBatch)); - _orderedDocumentsInBatch = null; - } - } - - private DocumentInfo CreateDocumentInfoFromFileInfo(DynamicFileInfo fileInfo, ImmutableArray folders) - { - Contract.ThrowIfTrue(folders.IsDefault); - - // we use this file path for editorconfig. - var filePath = fileInfo.FilePath; - - var name = FileNameUtilities.GetFileName(filePath); - var documentId = DocumentId.CreateNewId(_project.Id, filePath); - - var textLoader = fileInfo.TextLoader; - var documentServiceProvider = fileInfo.DocumentServiceProvider; - - return DocumentInfo.Create( - documentId, - name, - folders: folders, - sourceCodeKind: fileInfo.SourceCodeKind, - loader: textLoader, - filePath: filePath, - isGenerated: false, - designTimeOnly: true, - documentServiceProvider: documentServiceProvider); - } - - private sealed class SourceTextLoader : TextLoader - { - private readonly SourceTextContainer _textContainer; - private readonly string? _filePath; - - public SourceTextLoader(SourceTextContainer textContainer, string? filePath) - { - _textContainer = textContainer; - _filePath = filePath; - } - - public override Task LoadTextAndVersionAsync(Workspace workspace, DocumentId documentId, CancellationToken cancellationToken) - => Task.FromResult(TextAndVersion.Create(_textContainer.CurrentText, VersionStamp.Create(), _filePath)); - } - } } } From 8f7ebbffc9009ad290db46a8fbdd1cb2d843b880 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Mon, 7 Feb 2022 12:21:36 -0800 Subject: [PATCH 074/187] Try to reuse previously parsed trees when changing parse options If you change your solution configuration from debug to release or vice versa, this will usually trigger a reparse of the entire solution because the DEBUG directive is being defined or undefined. However, most source files out there probably don't have any #ifs in them anywhere, so in that case we can just reuse the tree. This change aims for simple rather than fully optimal at this point; this is partly because right now this change is hard to measure the full benefits of -- CPS projects destroy and recreate new projects when there's a configuration change. Alongside this change the project system will be fixing that as a part of https://github.com/dotnet/project-system/issues/7541 which we hope will make this actually work well. But I don't want to invest in a bunch of fancy code that may not move the needle much in practice until we've got the end-to-end working first. Possible improvements include: 1. Right now we don't try to differentiate between different types of directives in a tree; we'll reparse files containing #if, but also will reparse files containing #nullable directives. 2. We don't make any attempt to determine if a preprocessor directive is changing, but won't impact the file in question. For example, if switching to debug means we're defining DEBUG, but the file is only doing something like #if NETCORE, we still don't need to reparse the file. But getting that fully right can be a bit tricky. 3. Right now we're only using the root to check for directives if we already have one in hand. That might not be available if the tree is a recoverable tree, but we could still cache the directive bit separately. We can look at addressing some or all of these once we have the CPS issue fixed. Until then, I'm keeping this simple. --- ...reeFactoryService.RecoverableSyntaxTree.cs | 15 ++++- .../CSharpSyntaxTreeFactoryService.cs | 10 +++ ...ryService.AbstractRecoverableSyntaxRoot.cs | 32 ++++++++-- .../AbstractSyntaxTreeFactoryService.cs | 5 +- .../ISyntaxTreeFactoryService.cs | 6 ++ .../Workspace/Solution/DocumentState.cs | 51 ++++++++++++--- .../Workspace/Solution/ProjectState.cs | 5 +- .../CoreTest/SolutionTests/SolutionTests.cs | 62 +++++++++++++++++++ ...reeFactoryService.RecoverableSyntaxTree.vb | 17 ++++- .../VisualBasicSyntaxTreeFactoryService.vb | 9 +++ 10 files changed, 192 insertions(+), 20 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs index ffe41a3d1a79b..f59e8a6d93c59 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.RecoverableSyntaxTree.cs @@ -67,7 +67,8 @@ internal static SyntaxTree CreateRecoverableTree( options, text, encoding, - root.FullSpan.Length)); + root.FullSpan.Length, + root.ContainsDirectives)); } public override string FilePath @@ -145,6 +146,8 @@ public override SyntaxReference GetReference(SyntaxNode node) CompilationUnitSyntax IRecoverableSyntaxTree.CloneNodeAsRoot(CompilationUnitSyntax root) => CloneNodeAsRoot(root); + public bool ContainsDirectives => _info.ContainsDirectives; + public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions options) { if (ReferenceEquals(_info.Options, options) && this.TryGetRoot(out var oldRoot) && ReferenceEquals(root, oldRoot)) @@ -164,6 +167,16 @@ public override SyntaxTree WithFilePath(string path) return new RecoverableSyntaxTree(this, _info.WithFilePath(path)); } + + public SyntaxTree WithOptions(ParseOptions parseOptions) + { + if (ReferenceEquals(_info.Options, parseOptions)) + { + return this; + } + + return new RecoverableSyntaxTree(this, _info.WithOptions(parseOptions)); + } } } } diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs index 631875792f4b7..5fe13d5036925 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.cs @@ -59,6 +59,16 @@ public override ParseOptions TryParsePdbParseOptions(IReadOnlyDictionary TextSource; public readonly Encoding Encoding; public readonly int Length; + public readonly bool ContainsDirectives; public SyntaxTreeInfo( string filePath, ParseOptions options, ValueSource textSource, Encoding encoding, - int length) + int length, + bool containsDirectives) { FilePath = filePath ?? string.Empty; Options = options; TextSource = textSource; Encoding = encoding; Length = length; + ContainsDirectives = containsDirectives; } internal bool TryGetText([NotNullWhen(true)] out SourceText? text) @@ -62,7 +65,8 @@ internal SyntaxTreeInfo WithFilePath(string path) Options, TextSource, Encoding, - Length); + Length, + ContainsDirectives); } internal SyntaxTreeInfo WithOptionsAndLength(ParseOptions options, int length) @@ -72,7 +76,19 @@ internal SyntaxTreeInfo WithOptionsAndLength(ParseOptions options, int length) options, TextSource, Encoding, - length); + length, + ContainsDirectives); + } + + internal SyntaxTreeInfo WithOptions(ParseOptions options) + { + return new SyntaxTreeInfo( + FilePath, + options, + TextSource, + Encoding, + Length, + ContainsDirectives); } } @@ -161,10 +177,16 @@ private TRoot RecoverRoot(Stream stream, CancellationToken cancellationToken) } } - internal interface IRecoverableSyntaxTree where TRoot : SyntaxNode + internal interface IRecoverableSyntaxTree : IRecoverableSyntaxTree where TRoot : SyntaxNode + { + TRoot CloneNodeAsRoot(TRoot root); + } + + internal interface IRecoverableSyntaxTree { string FilePath { get; } - TRoot CloneNodeAsRoot(TRoot root); + bool ContainsDirectives { get; } + SyntaxTree WithOptions(ParseOptions parseOptions); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs index 01d122829c740..339942f3898c5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/AbstractSyntaxTreeFactoryService.cs @@ -29,12 +29,13 @@ public AbstractSyntaxTreeFactoryService(HostLanguageServices languageServices) } public abstract ParseOptions GetDefaultParseOptions(); + public abstract ParseOptions GetDefaultParseOptionsWithLatestLanguageVersion(); + public abstract bool OptionsDifferOnlyByPreprocessorDirectives(ParseOptions options1, ParseOptions options2); + public abstract ParseOptions TryParsePdbParseOptions(IReadOnlyDictionary metadata); public abstract SyntaxTree CreateSyntaxTree(string filePath, ParseOptions options, Encoding encoding, SyntaxNode root); public abstract SyntaxTree ParseSyntaxTree(string filePath, ParseOptions options, SourceText text, CancellationToken cancellationToken); public abstract SyntaxTree CreateRecoverableTree(ProjectId cacheKey, string filePath, ParseOptions options, ValueSource text, Encoding encoding, SyntaxNode root); public abstract SyntaxNode DeserializeNodeFrom(Stream stream, CancellationToken cancellationToken); - public abstract ParseOptions GetDefaultParseOptionsWithLatestLanguageVersion(); - public abstract ParseOptions TryParsePdbParseOptions(IReadOnlyDictionary metadata); public virtual bool CanCreateRecoverableTree(SyntaxNode root) => root.FullSpan.Length > _minimumLengthForRecoverableTree; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs index 6d28ad2ee289f..d4e0f83d04d95 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/SyntaxTreeFactory/ISyntaxTreeFactoryService.cs @@ -22,6 +22,12 @@ internal interface ISyntaxTreeFactoryService : ILanguageService ParseOptions TryParsePdbParseOptions(IReadOnlyDictionary compilationOptionsMetadata); + /// + /// Returns true if the two options differ only by preprocessor directives; this allows for us to reuse trees + /// if they don't have preprocessor directives in them. + /// + bool OptionsDifferOnlyByPreprocessorDirectives(ParseOptions options1, ParseOptions options2); + // new tree from root node SyntaxTree CreateSyntaxTree(string? filePath, ParseOptions options, Encoding? encoding, SyntaxNode root); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index b0de6908c51d5..25f43770af544 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -79,6 +79,7 @@ public DocumentState( } } + [MemberNotNullWhen(true, nameof(_treeSource))] internal bool SupportsSyntaxTree => _treeSource != null; @@ -325,11 +326,11 @@ public bool HasContentChanged(DocumentState oldState) public bool HasTextChanged(DocumentState oldState) => HasTextChanged(oldState, ignoreUnchangeableDocument: false); - public DocumentState UpdateParseOptions(ParseOptions options) + public DocumentState UpdateParseOptions(ParseOptions options, bool onlyPreprocessorDirectiveChange) { var originalSourceKind = this.SourceCodeKind; - var newState = this.SetParseOptions(options); + var newState = this.SetParseOptions(options, onlyPreprocessorDirectiveChange); if (newState.SourceCodeKind != originalSourceKind) { newState = newState.UpdateSourceCodeKind(originalSourceKind); @@ -338,7 +339,7 @@ public DocumentState UpdateParseOptions(ParseOptions options) return newState; } - private DocumentState SetParseOptions(ParseOptions options) + private DocumentState SetParseOptions(ParseOptions options, bool onlyPreprocessorDirectiveChange) { if (options == null) { @@ -350,12 +351,42 @@ private DocumentState SetParseOptions(ParseOptions options) throw new InvalidOperationException(); } - var newTreeSource = CreateLazyFullyParsedTree( - TextAndVersionSource, - Id.ProjectId, - GetSyntaxTreeFilePath(Attributes), - options, - _languageServices); + ValueSource? newTreeSource = null; + + // Optimization: if we are only changing preprocessor directives, and we've already parsed the existing tree and it didn't have + // any, we can avoid a reparse since the tree will be parsed the same. + if (onlyPreprocessorDirectiveChange && + _treeSource.TryGetValue(out var existingTreeAndVersion)) + { + var existingTree = existingTreeAndVersion.Tree; + SyntaxTree? newTree = null; + + if (existingTree is IRecoverableSyntaxTree recoverableTree && + !recoverableTree.ContainsDirectives) + { + // It's a recoverable tree, so we can try to reuse without even having to need the root + newTree = recoverableTree.WithOptions(options); + } + else if (existingTree.TryGetRoot(out var existingRoot) && !existingRoot.ContainsDirectives) + { + var treeFactory = _languageServices.GetRequiredService(); + newTree = treeFactory.CreateSyntaxTree(FilePath, options, existingTree.Encoding, existingRoot); + } + + if (newTree is not null) + newTreeSource = new ConstantValueSource(TreeAndVersion.Create(newTree, existingTreeAndVersion.Version)); + } + + // If we weren't able to reuse in a smart way, just reparse + if (newTreeSource is null) + { + newTreeSource = CreateLazyFullyParsedTree( + TextAndVersionSource, + Id.ProjectId, + GetSyntaxTreeFilePath(Attributes), + options, + _languageServices); + } return new DocumentState( LanguageServices, @@ -375,7 +406,7 @@ public DocumentState UpdateSourceCodeKind(SourceCodeKind kind) return this; } - return this.SetParseOptions(this.ParseOptions.WithKind(kind)); + return this.SetParseOptions(this.ParseOptions.WithKind(kind), onlyPreprocessorDirectiveChange: false); } // TODO: https://github.com/dotnet/roslyn/issues/37125 diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index c2015dfa3cdfe..d36dd5f83160d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -547,9 +547,12 @@ public ProjectState WithParseOptions(ParseOptions options) return this; } + var onlyPreprocessorDirectiveChange = ParseOptions != null && + _languageServices.SyntaxTreeFactory!.OptionsDifferOnlyByPreprocessorDirectives(options, ParseOptions); + return With( projectInfo: ProjectInfo.WithParseOptions(options).WithVersion(Version.GetNewerVersion()), - documentStates: DocumentStates.UpdateStates(static (state, options) => state.UpdateParseOptions(options), options)); + documentStates: DocumentStates.UpdateStates((state, options) => state.UpdateParseOptions(options, onlyPreprocessorDirectiveChange), options)); } public static bool IsSameLanguage(ProjectState project1, ProjectState project2) diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 17de5fb0d7910..7f08cb81dddcf 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -740,6 +740,68 @@ public void WithProjectParseOptions() Assert.Throws(() => solution.WithProjectParseOptions(ProjectId.CreateNewId(), options)); } + [Fact] + public async Task ChangingLanguageVersionReparses() + { + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + + using var workspace = CreateWorkspace(); + var document = workspace.CurrentSolution + .AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp) + .AddDocument(documentId, "Test.cs", "// File") + .GetRequiredDocument(documentId); + + var oldTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.Equal(document.Project.ParseOptions, oldTree.Options); + + document = document.Project.WithParseOptions(new CSharpParseOptions(languageVersion: CS.LanguageVersion.CSharp1)).GetRequiredDocument(documentId); + + var newTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.Equal(document.Project.ParseOptions, newTree.Options); + + Assert.False(oldTree.GetRoot().IsIncrementallyIdenticalTo(newTree.GetRoot())); + } + + [Theory] + [InlineData("#if DEBUG", false, LanguageNames.CSharp, false)] + [InlineData("#if DEBUG", false, LanguageNames.CSharp, true)] + [InlineData("// File", true, LanguageNames.CSharp, false)] + [InlineData("// File", true, LanguageNames.CSharp, true)] + [InlineData("#if DEBUG", false, LanguageNames.VisualBasic, false)] + [InlineData("#if DEBUG", false, LanguageNames.VisualBasic, true)] + [InlineData("' File", true, LanguageNames.VisualBasic, false)] + [InlineData("' File", true, LanguageNames.VisualBasic, true)] + public async Task ChangingPreprocessorDirectivesMayReparse(string source, bool expectReuse, string languageName, bool useRecoverableTrees) + { + var projectId = ProjectId.CreateNewId(); + var documentId = DocumentId.CreateNewId(projectId); + + using var workspace = useRecoverableTrees ? CreateWorkspaceWithRecoverableSyntaxTreesAndWeakCompilations() : CreateWorkspace(); + var document = workspace.CurrentSolution + .AddProject(projectId, "proj1", "proj1.dll", languageName) + .AddDocument(documentId, "Test", source) + .GetRequiredDocument(documentId); + + var oldTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.Equal(document.Project.ParseOptions, oldTree.Options); + + ParseOptions newOptions = + languageName == LanguageNames.CSharp ? new CSharpParseOptions(preprocessorSymbols: new[] { "DEBUG" }) + : new VisualBasicParseOptions(preprocessorSymbols: new KeyValuePair[] { new("DEBUG", null) }); + + document = document.Project.WithParseOptions(newOptions).GetRequiredDocument(documentId); + + var newTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + + Assert.Equal(document.Project.ParseOptions, newTree.Options); + + Assert.Equal(expectReuse, oldTree.GetRoot().IsIncrementallyIdenticalTo(newTree.GetRoot())); + } + [Fact] public void WithProjectReferences() { diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb index 60fdd8aee9362..d2dea9bdbed45 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.RecoverableSyntaxTree.vb @@ -61,7 +61,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic options, text, encoding, - root.FullSpan.Length)) + root.FullSpan.Length, + root.ContainsDirectives)) End Function Public Overrides ReadOnly Property FilePath As String Implements IRecoverableSyntaxTree(Of CompilationUnitSyntax).FilePath @@ -142,6 +143,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return CloneNodeAsRoot(root) End Function + Public ReadOnly Property ContainsDirectives As Boolean Implements IRecoverableSyntaxTree.ContainsDirectives + Get + Return _info.ContainsDirectives + End Get + End Property + Public Overrides Function WithRootAndOptions(root As SyntaxNode, options As ParseOptions) As SyntaxTree Dim oldRoot As VisualBasicSyntaxNode = Nothing If _info.Options Is options AndAlso TryGetRoot(oldRoot) AndAlso root Is oldRoot Then @@ -158,6 +165,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return New RecoverableSyntaxTree(Me, _info.WithFilePath(path)) End Function + + Public Function WithOptions(parseOptions As ParseOptions) As SyntaxTree Implements IRecoverableSyntaxTree.WithOptions + If _info.Options Is parseOptions Then + Return Me + End If + + Return New RecoverableSyntaxTree(Me, _info.WithOptions(parseOptions)) + End Function End Class End Class End Class diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb index 516b42105a088..fda6820f6b8c2 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.vb @@ -63,6 +63,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return New VisualBasicParseOptions(languageVersion:=langVersion, preprocessorSymbols:=preprocessorSymbols) End Function + Public Overrides Function OptionsDifferOnlyByPreprocessorDirectives(options1 As ParseOptions, options2 As ParseOptions) As Boolean + Dim vbOptions1 = DirectCast(options1, VisualBasicParseOptions) + Dim vbOptions2 = DirectCast(options2, VisualBasicParseOptions) + + ' The easy way to figure out if these only differ by a single field is to update one with the preprocessor symbols of the + ' other, and then do an equality check from there; this is future proofed if another value is ever added. + Return vbOptions1.WithPreprocessorSymbols(vbOptions2.PreprocessorSymbols) = vbOptions2 + End Function + Public Overloads Overrides Function GetDefaultParseOptionsWithLatestLanguageVersion() As ParseOptions Return _parseOptionsWithLatestLanguageVersion End Function From 07b1ca289d2da49ae2f01360b49216f2a3c2ce21 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 16 Feb 2022 14:26:15 -0800 Subject: [PATCH 075/187] Update breaking changes document with 17.0 and 17.1 fixes (#59245) --- .../Compiler Breaking Changes - DotNet 6.md | 38 +++++++++++++++---- .../Compiler Breaking Changes - DotNet 7.md | 31 ++++++++++++--- 2 files changed, 56 insertions(+), 13 deletions(-) diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md index 478fca0892c81..c58cc754a93ff 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 6.md @@ -1,6 +1,7 @@ ## This document lists known breaking changes in Roslyn in C# 10.0 which will be introduced with .NET 6. -1. Beginning with C# 10.0, null suppression operator is no longer allowed in patterns. +1. Beginning with C# 10.0, null suppression operator is no longer allowed in patterns. + ```csharp void M(object o) { @@ -8,7 +9,7 @@ } ``` -2. In C# 10, lambda expressions and method groups with inferred type are implicitly convertible to `System.MulticastDelegate`, and bases classes and interfaces of `System.MulticastDelegate` including `object`, +2. In C# 10, lambda expressions and method groups with inferred type are implicitly convertible to `System.MulticastDelegate`, and bases classes and interfaces of `System.MulticastDelegate` including `object`, and lambda expressions and method groups are implicitly convertible to `System.Linq.Expressions.Expression` and `System.Linq.Expressions.LambdaExpression`. These are _function_type_conversions_. @@ -92,7 +93,7 @@ These are _function_type_conversions_. } ``` -3. In C#10, a lambda expression with inferred type may contribute an argument type that affects overload resolution. +3. In C#10, a lambda expression with inferred type may contribute an argument type that affects overload resolution. ```csharp using System; @@ -109,18 +110,38 @@ These are _function_type_conversions_. } ``` -4. In Visual Studio 17.1, `struct` type declarations with field initializers must include an explicitly declared constructor. Additionally, all fields must be definitely assigned in `struct` instance constructors that do not have a `: this()` initializer so any previously unassigned fields must be assigned from the added constructor or from field initializers. +4. In Visual Studio 17.0 servicing, an error is reported in a `record struct` with a primary constructor if an explicit constructor has a `this()` initializer that invokes the implicit parameterless constructor. See [roslyn#58339](https://github.com/dotnet/roslyn/pull/58339). - For instance, the following results in an error in 17.1: + For instance, the following results in an error: ```csharp - struct S + record struct R(int X, int Y) + { + // error CS8982: A constructor declared in a 'record struct' with parameter list must have a 'this' + // initializer that calls the primary constructor or an explicitly declared constructor. + public R(int x) : this() { X = x; Y = 0; } + } + ``` + + The error could be resolved by invoking the primary constructor (as below) from the `this()` initializer, or by declaring a parameterless constructor that invokes the primary constructor. + ```csharp + record struct R(int X, int Y) { - int X = 1; // error: struct with field initializers must include an explicitly declared constructor + public R(int x) : this(x, 0) { } // ok + } + ``` + +5. In Visual Studio 17.0 servicing, if a `struct` type declaration with no constructors includes initializers for some but not all fields, the compiler will report an error that all fields must be assigned. See [roslyn#57925](https://github.com/dotnet/roslyn/pull/57925). + + For instance, the following results in an error: + ```csharp + struct S // error CS0171: Field 'S.Y' must be fully assigned before control is returned to the caller + { + int X = 1; int Y; } ``` - The error could be resolved by adding a constructor and assigning the other field. + For compatibility with 17.1 (see [#6](#6)), the error should be resolved by adding a constructor and assigning the other field. ```csharp struct S { @@ -129,3 +150,4 @@ These are _function_type_conversions_. public S() { Y = 0; } // ok } ``` + diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index acfba8bddc569..c152a61951ad2 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -1,6 +1,6 @@ ## This document lists known breaking changes in Roslyn after .NET 6 all the way to .NET 7. -1. In Visual Studio 17.1, the contextual keyword `var` cannot be used as an explicit lambda return type. +1. In Visual Studio 17.1, the contextual keyword `var` cannot be used as an explicit lambda return type. ```csharp using System; @@ -13,7 +13,7 @@ class var { } ``` -2. In Visual Studio 17.1, indexers that take an interpolated string handler and require the receiver as an input for the constructor cannot be used in an object initializer. +2. In Visual Studio 17.1, indexers that take an interpolated string handler and require the receiver as an input for the constructor cannot be used in an object initializer. ```cs using System.Runtime.CompilerServices; @@ -35,7 +35,7 @@ } ``` -3. In Visual Studio 17.1, `ref`/`ref readonly`/`in`/`out` are not allowed to be used on return/parameters of a method attributed with `UnmanagedCallersOnly`. +3. In Visual Studio 17.1, `ref`/`ref readonly`/`in`/`out` are not allowed to be used on return/parameters of a method attributed with `UnmanagedCallersOnly`. https://github.com/dotnet/roslyn/issues/57025 ```cs @@ -56,7 +56,7 @@ https://github.com/dotnet/roslyn/issues/57025 static void M5(out int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'. ``` -4. Beginning with C# 11.0, `Length` and `Count` properties on countable and indexable types +4. Beginning with C# 11.0, `Length` and `Count` properties on countable and indexable types are assumed to be non-negative for purpose of subsumption and exhaustiveness analysis of patterns and switches. Those types can be used with implicit Index indexer and list patterns. @@ -67,7 +67,7 @@ Those types can be used with implicit Index indexer and list patterns. } ``` -5. Starting with Visual Studio 17.1, format specifiers in interpolated strings can not contain curly braces (either `{` or `}`). In previous versions `{{` was interpreted as an escaped `{` and `}}` was interpreted as an escaped `}` char in the format specifier. Now the first `}` char in a format specifier ends the interpolation, and any `{` char is an error. +5. Starting with Visual Studio 17.1, format specifiers in interpolated strings can not contain curly braces (either `{` or `}`). In previous versions `{{` was interpreted as an escaped `{` and `}}` was interpreted as an escaped `}` char in the format specifier. Now the first `}` char in a format specifier ends the interpolation, and any `{` char is an error. https://github.com/dotnet/roslyn/issues/57750 ```csharp @@ -77,3 +77,24 @@ https://github.com/dotnet/roslyn/issues/57750 //prints now: "{C}" - not "{X}}" ``` + +6. In Visual Studio 17.1, `struct` type declarations with field initializers must include an explicitly declared constructor. Additionally, all fields must be definitely assigned in `struct` instance constructors that do not have a `: this()` initializer so any previously unassigned fields must be assigned from the added constructor or from field initializers. See [csharplang#5552](https://github.com/dotnet/csharplang/issues/5552), [roslyn#58581](https://github.com/dotnet/roslyn/pull/58581). + + For instance, the following results in an error in 17.1: + ```csharp + struct S + { + int X = 1; // error CS8983: A 'struct' with field initializers must include an explicitly declared constructor. + int Y; + } + ``` + + The error could be resolved by adding a constructor and assigning the other field. + ```csharp + struct S + { + int X = 1; + int Y; + public S() { Y = 0; } // ok + } + ``` From 131c9dc53a150b797f7d3dd6846652236c0dca81 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 14:29:20 -0800 Subject: [PATCH 076/187] Keep things in order --- .../Core/Portable/CodeFixes/CodeFixService.cs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 3091c6e3785b7..20073a32c1690 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -397,8 +397,8 @@ private async Task AppendFixesAsync( allFixers.RemoveDuplicates(); // Now, sort the fixers so that the ones that are ordered before others get their chance to run first. - if (TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) - allFixers.Sort(new FixerComparer(fixersForLanguage.Value)); + if (allFixers.Count >= 2 && TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) + allFixers.Sort(new FixerComparer(allFixers, fixersForLanguage.Value)); var extensionManager = document.Project.Solution.Workspace.Services.GetService(); @@ -986,13 +986,30 @@ private ImmutableDictionary> Compu private sealed class FixerComparer : IComparer { + private readonly Dictionary _fixerToIndex; private readonly ImmutableDictionary _priorityMap; - public FixerComparer(ImmutableDictionary priorityMap) - => _priorityMap = priorityMap; + public FixerComparer( + ArrayBuilder allFixers, + ImmutableDictionary priorityMap) + { + _fixerToIndex = allFixers.Select((fixer, index) => (fixer, index)).ToDictionary(t => t.fixer, t => t.index); + _priorityMap = priorityMap; + } public int Compare([AllowNull] CodeFixProvider x, [AllowNull] CodeFixProvider y) - => GetValue(x!).CompareTo(GetValue(y!)); + { + Contract.ThrowIfNull(x); + Contract.ThrowIfNull(y); + + // If the fixers specify an explicit ordering between each other, then respect that. + var comparison = GetValue(x).CompareTo(GetValue(y)); + if (comparison != 0) + return comparison; + + // Otherwise, keep things in the same order that they were in the list (i.e. keep things stable). + return _fixerToIndex[x] - _fixerToIndex[y]; + } private int GetValue(CodeFixProvider provider) => _priorityMap.TryGetValue(provider, out var value) ? value : int.MaxValue; From 80e0afaa412a0b5a513863f79fbfede5e27b6342 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 14:38:46 -0800 Subject: [PATCH 077/187] fixup --- .../Core/Portable/CodeFixes/CodeFixService.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 20073a32c1690..33e706389048a 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -394,8 +394,6 @@ private async Task AppendFixesAsync( } } - allFixers.RemoveDuplicates(); - // Now, sort the fixers so that the ones that are ordered before others get their chance to run first. if (allFixers.Count >= 2 && TryGetWorkspaceFixersPriorityMap(document, out var fixersForLanguage)) allFixers.Sort(new FixerComparer(allFixers, fixersForLanguage.Value)); @@ -472,9 +470,14 @@ void AddAllFixers( TextSpan range, List diagnostics) { - allFixers.AddRange(fixers); foreach (var fixer in fixers) + { + if (allFixers.Contains(fixer)) + continue; + + allFixers.Add(fixer); fixerToRangesAndDiagnostics.GetOrAdd(fixer, static _ => new()).Add((range, diagnostics)); + } } } @@ -1003,16 +1006,15 @@ public int Compare([AllowNull] CodeFixProvider x, [AllowNull] CodeFixProvider y) Contract.ThrowIfNull(y); // If the fixers specify an explicit ordering between each other, then respect that. - var comparison = GetValue(x).CompareTo(GetValue(y)); - if (comparison != 0) - return comparison; + if (_priorityMap.TryGetValue(x, out var xOrder) && + _priorityMap.TryGetValue(y, out var yOrder)) + { + return xOrder - yOrder; + } // Otherwise, keep things in the same order that they were in the list (i.e. keep things stable). return _fixerToIndex[x] - _fixerToIndex[y]; } - - private int GetValue(CodeFixProvider provider) - => _priorityMap.TryGetValue(provider, out var value) ? value : int.MaxValue; } } } From ba7e694b973757a13879f6552732292c1052c540 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 14:39:50 -0800 Subject: [PATCH 078/187] fixup --- src/Features/Core/Portable/CodeFixes/CodeFixService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 33e706389048a..dce6786c37190 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -1009,7 +1009,9 @@ public int Compare([AllowNull] CodeFixProvider x, [AllowNull] CodeFixProvider y) if (_priorityMap.TryGetValue(x, out var xOrder) && _priorityMap.TryGetValue(y, out var yOrder)) { - return xOrder - yOrder; + var comparison = xOrder - yOrder; + if (comparison != 0) + return comparison; } // Otherwise, keep things in the same order that they were in the list (i.e. keep things stable). From de39539f4c64f3efb97816a5683e6b4dd22e81aa Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 14:41:32 -0800 Subject: [PATCH 079/187] Docs --- src/Features/Core/Portable/CodeFixes/CodeFixService.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 3d5b6711644b2..b01d7489c236b 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -477,6 +477,9 @@ await AppendFixesOrConfigurationsAsync( return lazy.Metadata; } + // Note: it feels very strange that we could ever not find a fixer in our list. However, this + // occurs in testing scenarios. I'm not sure if the tests represent a bogus potential input, or if + // this is something that can actually occur in practice and we want to keep working. return null; }, _fixers); From 3e68dd2790f26bbe4a754521d12ed8eac3e246c0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 14:45:26 -0800 Subject: [PATCH 080/187] Simplify code --- .../Core/Portable/CodeFixes/CodeFixService.cs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index b01d7489c236b..a6361e79dbb6b 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -42,7 +42,7 @@ internal partial class CodeFixService : ICodeFixService private readonly ImmutableArray> _fixers; private readonly ImmutableDictionary>> _fixersPerLanguageMap; - private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new(); + private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new(); // Shared by project fixers and workspace fixers. private readonly ConditionalWeakTable _analyzerReferenceToFixersMap = new(); @@ -857,7 +857,7 @@ private ImmutableDictionary>>(() => { - var mutableMap = new Dictionary>(); + using var _ = PooledDictionary>.GetInstance(out var mutableMap); foreach (var lazyFixer in lazyFixers) { @@ -873,18 +873,12 @@ private ImmutableDictionary new List()); + var list = mutableMap.GetOrAdd(id, static _ => ArrayBuilder.GetInstance()); list.Add(fixer); } } - var immutableMap = ImmutableDictionary.CreateBuilder>(); - foreach (var (diagnosticId, fixers) in mutableMap) - { - immutableMap.Add(diagnosticId, fixers.AsImmutableOrEmpty()); - } - - return immutableMap.ToImmutable(); + return mutableMap.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableAndFree()); }, isThreadSafe: true); fixerMap = fixerMap.Add(diagnosticId, lazyMap); @@ -949,19 +943,19 @@ private ImmutableDictionary> GetProjectFixers(Project project) + private ImmutableDictionary> GetProjectFixers(Project project) { // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive return project.Solution.Workspace.Kind == WorkspaceKind.Interactive - ? ImmutableDictionary>.Empty + ? ImmutableDictionary>.Empty : _projectFixersMap.GetValue(project.AnalyzerReferences, _ => ComputeProjectFixers(project)); } - private ImmutableDictionary> ComputeProjectFixers(Project project) + private ImmutableDictionary> ComputeProjectFixers(Project project) { var extensionManager = project.Solution.Workspace.Services.GetService(); - var builder = ImmutableDictionary.CreateBuilder>(); + using var _ = PooledDictionary>.GetInstance(out var builder); foreach (var reference in project.AnalyzerReferences) { var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); @@ -973,13 +967,13 @@ private ImmutableDictionary> ComputeProjectF if (string.IsNullOrWhiteSpace(id)) continue; - var list = builder.GetOrAdd(id, static _ => new List()); + var list = builder.GetOrAdd(id, static _ => ArrayBuilder.GetInstance()); list.Add(fixer); } } } - return builder.ToImmutable(); + return builder.ToImmutableDictionary(kvp => kvp.Key, kvp.Value.ToImmutableAndFree()); } } } From 731c9acdd56f6a041bf46ee6dd4d8a00250f5cdb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 14:45:42 -0800 Subject: [PATCH 081/187] Simplify code --- src/Features/Core/Portable/CodeFixes/CodeFixService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index a6361e79dbb6b..34083b2e6f709 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -973,7 +973,7 @@ private ImmutableDictionary> Compu } } - return builder.ToImmutableDictionary(kvp => kvp.Key, kvp.Value.ToImmutableAndFree()); + return builder.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableAndFree()); } } } From e245600caf468b60b5b0866b263b4a9c48a6dd06 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 15:00:24 -0800 Subject: [PATCH 082/187] Simplify --- .../Core/Portable/CodeFixes/CodeFixService.cs | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index dce6786c37190..23439340bacbf 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -959,32 +959,24 @@ private ImmutableDictionary> Compu var extensionManager = project.Solution.Workspace.Services.GetService(); using var _ = PooledDictionary>.GetInstance(out var builder); - try + foreach (var reference in project.AnalyzerReferences) { - foreach (var reference in project.AnalyzerReferences) + var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); + foreach (var fixer in projectCodeFixerProvider.GetExtensions(project.Language)) { - var projectCodeFixerProvider = _analyzerReferenceToFixersMap.GetValue(reference, _createProjectCodeFixProvider); - foreach (var fixer in projectCodeFixerProvider.GetExtensions(project.Language)) + var fixableIds = this.GetFixableDiagnosticIds(fixer, extensionManager); + foreach (var id in fixableIds) { - var fixableIds = this.GetFixableDiagnosticIds(fixer, extensionManager); - foreach (var id in fixableIds) - { - if (string.IsNullOrWhiteSpace(id)) - continue; + if (string.IsNullOrWhiteSpace(id)) + continue; - var list = builder.GetOrAdd(id, static _ => ArrayBuilder.GetInstance()); - list.Add(fixer); - } + var list = builder.GetOrAdd(id, static _ => ArrayBuilder.GetInstance()); + list.Add(fixer); } } - - return builder.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutable()); - } - finally - { - foreach (var kvp in builder) - kvp.Value.Free(); } + + return builder.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableAndFree()); } private sealed class FixerComparer : IComparer From b452e2713ffa5df79059a97f53b0f453fc30e864 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 16 Feb 2022 15:17:10 -0800 Subject: [PATCH 083/187] Fix typos (#59237) --- .../Declarations/SingleTypeDeclaration.cs | 2 +- .../Portable/Parser/Lexer_StringLiteral.cs | 2 +- .../CSharp/Test/CommandLine/CommandLineTests.cs | 2 +- .../Emit/EditAndContinue/EditAndContinueTests.cs | 2 +- .../CodeGenMethodGroupConversionCachingTests.cs | 4 ++-- .../Semantic/FlowAnalysis/RegionAnalysisTests.cs | 4 ++-- .../Test/Semantic/Semantics/DelegateTypeTests.cs | 16 ++++++++-------- .../Test/Semantic/Semantics/RecordStructTests.cs | 2 +- .../AdditionalSourcesCollectionTests.cs | 2 +- .../Core/Portable/Compilation/Compilation.cs | 2 +- .../RebuildTest/DeterministicKeyBuilderTests.cs | 2 +- src/Compilers/Shared/BuildServerConnection.cs | 2 +- .../IOperation/IOperationTests_IArgument.vb | 2 +- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs index c7ec229bc4ba1..3c2a209adf7e8 100644 --- a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs @@ -26,7 +26,7 @@ internal sealed class SingleTypeDeclaration : SingleNamespaceOrTypeDeclaration /// through a using alias in the file. For example /// using X = System.Runtime.CompilerServices.TypeForwardedToAttribute or /// [TypeForwardedToAttribute]. Can be used to avoid having to go back to source - /// to retrieve attributes whtn there is no chance they would bind to attribute of interest. + /// to retrieve attributes when there is no chance they would bind to attribute of interest. /// public QuickAttributes QuickAttributes { get; } diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer_StringLiteral.cs b/src/Compilers/CSharp/Portable/Parser/Lexer_StringLiteral.cs index 92ff794273d83..c3955e0eb6a55 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer_StringLiteral.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer_StringLiteral.cs @@ -708,7 +708,7 @@ private bool IsAtEndOfMultiLineRawLiteral(InterpolatedStringKind kind, int start /// /// Returns if the quote was an end delimiter and lexing of the contents of the - /// interpolated string literal should stop. If it was an end delimeter it will not be consumed. If it is + /// interpolated string literal should stop. If it was an end delimiter it will not be consumed. If it is /// content and should not terminate the string then it will be consumed by this method. /// private bool IsEndDelimiterOtherwiseConsume(InterpolatedStringKind kind, int startingQuoteCount) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 206f936c3a5ff..906f531d66a1e 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -1411,7 +1411,7 @@ public void ModuleManifest() } // The following test is failing in the Linux Debug test leg of CI. - // This issus is being tracked by https://github.com/dotnet/roslyn/issues/58077 + // This issue is being tracked by https://github.com/dotnet/roslyn/issues/58077 [ConditionalFact(typeof(WindowsOrMacOSOnly))] public void ArgumentParsing() { diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index cd7b7e55ad9f2..efd4dea68e945 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -1622,7 +1622,7 @@ static unsafe void F() where U : unmanaged } [Fact] - public void Lambda_SynthesizedDeletage_06() + public void Lambda_SynthesizedDelegate_06() { var source0 = MarkedSource( @"class C diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 74c56ba932660..3952de8208ec4 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -252,7 +252,7 @@ .maxstack 4 } [Fact] - public void Not_InExpressionLamba0() + public void Not_InExpressionLambda0() { var source = @" using System; @@ -329,7 +329,7 @@ .locals init (System.Linq.Expressions.ParameterExpression V_0) } [Fact] - public void Not_InExpressionLamba1() + public void Not_InExpressionLambda1() { var source = @" using System; diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs index e2423f12d9a39..4d78afa4814d8 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs @@ -2618,7 +2618,7 @@ public static void M(C? c) #endregion - #region "constructor initalizer" + #region "constructor initializer" [Fact] public void TestDataFlowsInCtorInitPublicApi() { @@ -2757,7 +2757,7 @@ class C } #endregion - #region "primary constructor initalizer" + #region "primary constructor initializer" [Fact] public void TestDataFlowsInPrimaryCtorInitPublicApi() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index a8145b8774058..e9ad802ad31d1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -3264,9 +3264,9 @@ interface IRouteBuilder } static class AppBuilderExtensions { - public static IAppBuilder Map(this IAppBuilder app, PathSring path, Action callback) + public static IAppBuilder Map(this IAppBuilder app, PathString path, Action callback) { - Console.WriteLine(""AppBuilderExtensions.Map(this IAppBuilder app, PathSring path, Action callback)""); + Console.WriteLine(""AppBuilderExtensions.Map(this IAppBuilder app, PathString path, Action callback)""); return app; } } @@ -3278,20 +3278,20 @@ public static IRouteBuilder Map(this IRouteBuilder routes, string path, Delegate return routes; } } -struct PathSring +struct PathString { - public PathSring(string? path) + public PathString(string? path) { Path = path; } public string? Path { get; } - public static implicit operator PathSring(string? s) => new PathSring(s); - public static implicit operator string?(PathSring path) => path.Path; + public static implicit operator PathString(string? s) => new PathString(s); + public static implicit operator string?(PathString path) => path.Path; }"; var expectedOutput = -@"AppBuilderExtensions.Map(this IAppBuilder app, PathSring path, Action callback) -AppBuilderExtensions.Map(this IAppBuilder app, PathSring path, Action callback) +@"AppBuilderExtensions.Map(this IAppBuilder app, PathString path, Action callback) +AppBuilderExtensions.Map(this IAppBuilder app, PathString path, Action callback) "; CompileAndVerify(source, parseOptions: TestOptions.Regular9, expectedOutput: expectedOutput); CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: expectedOutput); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs index 1597eacea2b99..cd06e44641f70 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordStructTests.cs @@ -3766,7 +3766,7 @@ record struct A(int I, string S); } [Fact] - public void Deconstruct_WihtNonReadOnlyGetter_GeneratedAsNonReadOnly() + public void Deconstruct_WithNonReadOnlyGetter_GeneratedAsNonReadOnly() { var src = @" record struct A(int I, string S) diff --git a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/AdditionalSourcesCollectionTests.cs b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/AdditionalSourcesCollectionTests.cs index e081af04cf4ed..33bb3d667ad8a 100644 --- a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/AdditionalSourcesCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/AdditionalSourcesCollectionTests.cs @@ -143,7 +143,7 @@ public void Hint_Name_Must_Be_Unique(string hintName1, string hintName2) } [Fact] - public void Hint_Name_Must_Be_Unique_When_Combining_Soruces() + public void Hint_Name_Must_Be_Unique_When_Combining_Sources() { AdditionalSourcesCollection asc = new AdditionalSourcesCollection(".cs"); asc.Add("hintName1", SourceText.From("", Encoding.UTF8)); diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index c19334290734e..98a6a4ba981c1 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -175,7 +175,7 @@ protected static IReadOnlyDictionary SyntaxTreeCommonFeatures(IE /// /// /// Manipulation of strong name keys: strong name keys are read "on demand" by the compiler - /// and both normal compilation and this key can have non-determinstic output if they are + /// and both normal compilation and this key can have non-deterministic output if they are /// manipulated at the correct point in program execution. That is an existing limitation /// of compilation that is tracked by https://github.com/dotnet/roslyn/issues/57940 /// diff --git a/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs index b32004b10968a..cba991d69f205 100644 --- a/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs +++ b/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs @@ -342,7 +342,7 @@ JObject getValue(bool deterministic) } /// - /// Disabling determinism should mean all calls to GetDeteriministicKey return different values. + /// Disabling determinism should mean all calls to GetDeterministicKey return different values. /// [Fact] public void CompilationOptionsDeterministicOff() diff --git a/src/Compilers/Shared/BuildServerConnection.cs b/src/Compilers/Shared/BuildServerConnection.cs index 692156a5775bb..5ca8f328d0c77 100644 --- a/src/Compilers/Shared/BuildServerConnection.cs +++ b/src/Compilers/Shared/BuildServerConnection.cs @@ -723,7 +723,7 @@ internal bool TryLockFile() // file here, then the other thread unlocks and deletes the file, and then we // acquire the lock on our file handle - but the actual file is already deleted. // To close this race, we verify that the file does in fact still exist now that - // we have successfull acquired the locked FileStream. (Note that this check is + // we have successfully acquired the locked FileStream. (Note that this check is // safe because we cannot race with an other attempt to create the file since we // hold the guard, and after the FileStream constructor returned we can no race // with file deletion because we hold the lock.) diff --git a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IArgument.vb b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IArgument.vb index f0688530ba51f..133910c9098f5 100644 --- a/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IArgument.vb +++ b/src/Compilers/VisualBasic/Test/IOperation/IOperation/IOperationTests_IArgument.vb @@ -873,7 +873,7 @@ BC30587: Named argument cannot match a ParamArray parameter. - Public Sub Error_NamedArgumenNotExist() + Public Sub Error_NamedArgumentNotExist() Dim source = Date: Wed, 16 Feb 2022 15:24:36 -0800 Subject: [PATCH 084/187] Fix --- ...mpletionCommandHandlerTests_DateAndTime.vb | 19 ++++++++++++++ .../DateAndTimeLanguageDetector.cs | 2 +- .../Completion/CompletionTests.cs | 25 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb index f9a203bd1b557..85e47de9db1bc 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb @@ -112,6 +112,25 @@ class c End Using End Function + + Public Async Function ExplicitInvokeNotInGuid(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + Await state.AssertNoCompletionSession() + End Using + End Function + Public Async Function ExplicitInvokeDateComment(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( diff --git a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs index 715f3875ba89c..4a462ae5ffb5b 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeLanguageDetector.cs @@ -140,7 +140,7 @@ protected override bool IsArgumentToWellKnownAPI( return true; } - return true; + return false; } private static string? GetNameOfInvokedExpression(ISyntaxFacts syntaxFacts, SyntaxNode invokedExpression) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs index 67bcb053fd123..a3974eaaaacb5 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs @@ -302,6 +302,31 @@ void M() AssertJsonEquals(expected, results.Items.First()); } + [Fact] + public async Task TestGetDateAndTimeCompletionOnGuid() + { + var markup = +@"using System; +class A +{ + void M() + { + Guid.NewGuid().ToString(""{|caret:|}); + } +}"; + using var testLspServer = await CreateTestLspServerAsync(markup); + var completionParams = CreateCompletionParams( + testLspServer.GetLocations("caret").Single(), + invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, + triggerCharacter: "\"", + triggerKind: LSP.CompletionTriggerKind.TriggerCharacter); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); + Assert.Null(results); + } + [Fact] [WorkItem(50964, "https://github.com/dotnet/roslyn/issues/50964")] public async Task TestGetRegexCompletionsAsync() From 364d16bae842a9d37734775bceda638d15c482e3 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 15:24:50 -0800 Subject: [PATCH 085/187] Fix DateTime completion appearing innapropriately --- .../CSharpCompletionCommandHandlerTests_DateAndTime.vb | 1 + .../ProtocolUnitTests/Completion/CompletionTests.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb index 85e47de9db1bc..988df7b0961be 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb @@ -113,6 +113,7 @@ class c End Function + Public Async Function ExplicitInvokeNotInGuid(showCompletionInArgumentLists As Boolean) As Task Using state = TestStateFactory.CreateCSharpTestState( Date: Wed, 16 Feb 2022 15:33:54 -0800 Subject: [PATCH 086/187] Handle binary AdditionalTexts in the AdditionalTextComparer. (#59578) --- .../AdditionalTextComparer.cs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Compilers/Core/Portable/InternalUtilities/AdditionalTextComparer.cs b/src/Compilers/Core/Portable/InternalUtilities/AdditionalTextComparer.cs index fe7ed89f21174..ee7f130986333 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/AdditionalTextComparer.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/AdditionalTextComparer.cs @@ -2,11 +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.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; +using System.IO; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -32,8 +32,15 @@ public bool Equals(AdditionalText? x, AdditionalText? y) return false; } - var xText = x.GetText(); - var yText = y.GetText(); + var xText = GetTextOrNullIfBinary(x); + var yText = GetTextOrNullIfBinary(y); + + // If xText and yText are both null, then the additional text is observably not changed + // and can be treated as equal. + if (xText is null && yText is null) + { + return true; + } if (xText is null || yText is null || xText.Length != yText.Length) { @@ -48,5 +55,18 @@ public int GetHashCode(AdditionalText obj) return Hash.Combine(PathUtilities.Comparer.GetHashCode(obj.Path), ByteSequenceComparer.GetHashCode(obj.GetText()?.GetChecksum() ?? ImmutableArray.Empty)); } + + private static SourceText? GetTextOrNullIfBinary(AdditionalText text) + { + try + { + return text.GetText(); + } + catch (InvalidDataException) + { + // InvalidDataException is thrown when the underlying text is binary + return null; + } + } } } From 83389eef1d10753fc0453791e28336f33ff56859 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 16 Feb 2022 15:47:32 -0800 Subject: [PATCH 087/187] [LSP] Use runtime type arguments for streamjsonrpc request handling (#59510) * Allow LSP request handlers to provide their type arguments for serialization at runtime and modify tests to use streamjsonrpc instead of directly accessing the queue. --- .../AbstractInProcLanguageClient.cs | 3 +- .../CodeActions/CodeActionResolveHandler.cs | 3 +- .../CodeActions/CodeActionsHandler.cs | 3 +- .../CodeActions/CodeActionsHandlerProvider.cs | 6 +- .../CodeActions/RunCodeActionHandler.cs | 1 + .../Handlers/Completion/CompletionHandler.cs | 3 +- .../Completion/CompletionHandlerProvider.cs | 5 +- .../Completion/CompletionResolveHandler.cs | 3 +- .../Handlers/Hover/HoverHandler.cs | 6 +- .../References/FindAllReferencesHandler.cs | 6 +- .../References/FindImplementationsHandler.cs | 6 +- .../Handlers/Rename/RenameHandler.cs | 6 +- .../Symbols/DocumentSymbolsHandler.cs | 6 +- .../Symbols/WorkspaceSymbolsHandler.cs | 6 +- .../VisualStudioInProcLanguageServer.cs | 105 ----- .../AbstractLanguageServerProtocolTests.cs | 168 +++++--- .../AbstractRequestDispatcherFactory.cs | 4 +- .../AbstractStatelessRequestHandler.cs | 2 - .../AbstractExecuteWorkspaceCommandHandler.cs | 2 - .../Commands/ProvidesCommandAttribute.cs | 4 +- .../Definitions/GoToDefinitionHandler.cs | 6 +- .../Definitions/GoToTypeDefinitionHandler.cs | 6 +- .../AbstractPullDiagnosticHandler.cs | 2 - .../DocumentPullDiagnosticHandler.cs | 3 +- .../DocumentPullDiagnosticHandlerProvider.cs | 4 +- ...alDocumentPullDiagnosticHandlerProvider.cs | 3 +- ...erimentalDocumentPullDiagnosticsHandler.cs | 3 +- ...lWorkspacePullDiagnosticHandlerProvider.cs | 3 +- ...rimentalWorkspacePullDiagnosticsHandler.cs | 3 +- .../WorkspacePullDiagnosticHandler.cs | 3 +- .../WorkspacePullDiagnosticHandlerProvider.cs | 4 +- .../DocumentChanges/DidChangeHandler.cs | 6 +- .../DocumentChanges/DidCloseHandler.cs | 6 +- .../Handler/DocumentChanges/DidOpenHandler.cs | 6 +- ...xportLspRequestHandlerProviderAttribute.cs | 30 +- .../FoldingRanges/FoldingRangesHandler.cs | 6 +- .../Formatting/FormatDocumentHandler.cs | 6 +- .../Formatting/FormatDocumentOnTypeHandler.cs | 6 +- .../Formatting/FormatDocumentRangeHandler.cs | 6 +- .../Highlights/DocumentHighlightHandler.cs | 6 +- .../Protocol/Handler/IRequestHandler.cs | 5 - .../InlineCompletionsHandler.cs | 6 +- ...sMethodAttribute.cs => MethodAttribute.cs} | 9 +- .../OnAutoInsert/OnAutoInsertHandler.cs | 6 +- .../GetTextDocumentWithContextHandler.cs | 6 +- .../RequestHandlerProviderMetadataView.cs | 32 +- .../SemanticTokensRangeHandler.cs | 8 +- .../SignatureHelp/SignatureHelpHandler.cs | 6 +- .../Protocol/LanguageServerTarget.cs | 378 ++++++------------ .../Protocol/ProtocolConstants.cs | 2 + .../Protocol/RequestDispatcher.cs | 105 +++-- .../Protocol/RequestDispatcherFactory.cs | 2 +- .../CodeActions/CodeActionResolveTests.cs | 5 +- .../CodeActions/CodeActionsTests.cs | 8 +- .../CodeActions/RunCodeActionsTests.cs | 2 +- .../Completion/CompletionResolveTests.cs | 86 ++-- .../Completion/CompletionTests.cs | 87 ++-- .../Definitions/GoToDefinitionTests.cs | 2 +- .../Definitions/GoToTypeDefinitionTests.cs | 2 +- .../Diagnostics/PullDiagnosticTests.cs | 104 ++--- .../DocumentChangesTests.LinkedDocuments.cs | 49 +-- .../DocumentChanges/DocumentChangesTests.cs | 8 +- .../FoldingRanges/FoldingRangesTests.cs | 2 +- .../Formatting/FormatDocumentOnTypeTests.cs | 2 +- .../Formatting/FormatDocumentRangeTests.cs | 2 - .../Formatting/FormatDocumentTests.cs | 2 +- .../Highlights/DocumentHighlightTests.cs | 2 +- .../ProtocolUnitTests/Hover/HoverTests.cs | 43 +- .../InlineCompletionsTests.cs | 4 +- .../LanguageServerTargetTests.cs | 91 ++--- .../LspMiscellaneousFilesWorkspaceTests.cs | 2 +- .../OnAutoInsert/OnAutoInsertTests.cs | 2 +- .../Ordering/FailingMutatingRequestHandler.cs | 34 +- .../Ordering/FailingRequestHandler.cs | 33 +- .../LongRunningNonMutatingRequestHandler.cs | 28 +- .../Ordering/MutatingRequestHandler.cs | 34 +- .../NonLSPSolutionRequestHandlerProvider.cs | 31 +- .../Ordering/NonMutatingRequestHandler.cs | 35 +- .../Ordering/RequestOrderingTests.cs | 35 +- .../Ordering/TestResponse.cs | 2 +- .../GetTextDocumentWithContextHandlerTests.cs | 2 +- .../FindAllReferencesHandlerTests.cs | 30 +- .../References/FindImplementationsTests.cs | 2 +- .../ProtocolUnitTests/Rename/RenameTests.cs | 3 +- .../AbstractSemanticTokensTests.cs | 13 +- .../SignatureHelp/SignatureHelpTests.cs | 2 +- .../Symbols/DocumentSymbolsTests.cs | 37 +- .../Symbols/WorkspaceSymbolsTests.cs | 2 +- .../Workspaces/LspWorkspaceManagerTests.cs | 4 +- .../CodeActions/CodeActionsHandlerProvider.cs | 7 +- .../Commands/CreateEventCommandHandler.cs | 1 + .../CreateEventCommandHandlerProvider.cs | 4 +- .../Handler/Completion/CompletionHandler.cs | 5 +- .../Completion/CompletionResolveHandler.cs | 6 +- .../Definitions/GoToDefinitionHandler.cs | 7 +- .../DocumentPullDiagnosticHandler.cs | 7 +- .../WorkspacePullDiagnosticHandler.cs | 7 +- .../FoldingRanges/FoldingRangesHandler.cs | 6 +- .../Formatting/FormatDocumentHandler.cs | 6 +- .../Formatting/FormatDocumentOnTypeHandler.cs | 6 +- .../Formatting/FormatDocumentRangeHandler.cs | 6 +- .../Handler/Hover/HoverHandler.cs | 6 +- .../OnAutoInsert/OnAutoInsertHandler.cs | 6 +- .../OnTypeRename/OnTypeRenameHandler.cs | 6 +- .../XamlRequestDispatcherFactory.cs | 28 +- src/VisualStudio/Xaml/Impl/StringConstants.cs | 2 + 106 files changed, 793 insertions(+), 1186 deletions(-) delete mode 100644 src/EditorFeatures/Core/Implementation/LanguageServer/VisualStudioInProcLanguageServer.cs rename src/Features/LanguageServer/Protocol/Handler/{ProvidesMethodAttribute.cs => MethodAttribute.cs} (61%) diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/AbstractInProcLanguageClient.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/AbstractInProcLanguageClient.cs index 07451e43cb3e0..a9c711ae3a3c0 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/AbstractInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/AbstractInProcLanguageClient.cs @@ -198,11 +198,12 @@ public ILanguageServerTarget Create( ICapabilitiesProvider capabilitiesProvider, ILspLogger logger) { - return new VisualStudioInProcLanguageServer( + return new LanguageServerTarget( _requestDispatcherFactory, jsonRpc, capabilitiesProvider, _lspWorkspaceRegistrationService, + lspMiscellaneousFilesWorkspace: null, GlobalOptions, _listenerProvider, logger, diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs index c1f6506a0e1d8..66a3f2f7d29e2 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs @@ -32,6 +32,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// EditorFeatures references in are removed. /// See https://github.com/dotnet/roslyn/issues/55142 /// + [Method(LSP.Methods.CodeActionResolveName)] internal class CodeActionResolveHandler : IRequestHandler { private readonly CodeActionsCache _codeActionsCache; @@ -51,8 +52,6 @@ public CodeActionResolveHandler( _globalOptions = globalOptions; } - public string Method => LSP.Methods.CodeActionResolveName; - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs index 4ee968b6db505..1553c5424a8ac 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs @@ -25,6 +25,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// EditorFeatures references in are removed. /// See https://github.com/dotnet/roslyn/issues/55142 /// + [Method(LSP.Methods.TextDocumentCodeActionName)] internal class CodeActionsHandler : IRequestHandler { private readonly CodeActionsCache _codeActionsCache; @@ -34,8 +35,6 @@ internal class CodeActionsHandler : IRequestHandler LSP.Methods.TextDocumentCodeActionName; - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandlerProvider.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandlerProvider.cs index 1c5a2bd63f08a..b090b667351c1 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandlerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandlerProvider.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; using Microsoft.CodeAnalysis.Options; -using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -20,10 +19,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// Exports all the code action handlers together to ensure they /// share the same code actions cache state. /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentCodeActionName)] - [ProvidesMethod(LSP.Methods.CodeActionResolveName)] - [ProvidesCommand(CodeActionsHandler.RunCodeActionCommandName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(CodeActionsHandler), typeof(CodeActionResolveHandler), typeof(RunCodeActionHandler)), Shared] internal class CodeActionsHandlerProvider : AbstractRequestHandlerProvider { private readonly ICodeFixService _codeFixService; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs index 60412280af531..4a0224579340c 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs @@ -30,6 +30,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// UI thread dependencies are resolved and references are removed. /// See https://github.com/dotnet/roslyn/issues/55142 /// + [Command(CodeActionsHandler.RunCodeActionCommandName)] internal class RunCodeActionHandler : AbstractExecuteWorkspaceCommandHandler { private readonly CodeActionsCache _codeActionsCache; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs index 1555f67c15d53..e313f23fb32d8 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs @@ -31,6 +31,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// references to VS Icon types are removed. /// See https://github.com/dotnet/roslyn/issues/55142 /// + [Method(LSP.Methods.TextDocumentCompletionName)] internal class CompletionHandler : IRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -39,8 +40,6 @@ internal class CompletionHandler : IRequestHandler LSP.Methods.TextDocumentCompletionName; - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandlerProvider.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandlerProvider.cs index aafc5da24a766..8ca41b394a45b 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandlerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandlerProvider.cs @@ -11,13 +11,10 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.Completion; using Microsoft.CodeAnalysis.Options; -using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentCompletionName)] - [ProvidesMethod(LSP.Methods.TextDocumentCompletionResolveName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(CompletionHandler), typeof(CompletionResolveHandler)), Shared] internal class CompletionHandlerProvider : AbstractRequestHandlerProvider { private readonly IEnumerable> _completionProviders; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs index 5918dc976d3a4..99fd0edef7d2c 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionResolveHandler.cs @@ -25,13 +25,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// references to VS icon types and classified text runs are removed. /// See https://github.com/dotnet/roslyn/issues/55142 /// + [Method(LSP.Methods.TextDocumentCompletionResolveName)] internal sealed class CompletionResolveHandler : IRequestHandler { private readonly CompletionListCache _completionListCache; private readonly IGlobalOptionService _globalOptions; - public string Method => LSP.Methods.TextDocumentCompletionResolveName; - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs index 388a2f00d07a3..e0974a32da07b 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Hover/HoverHandler.cs @@ -27,8 +27,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// no longer references VS icon or classified text run types. /// See https://github.com/dotnet/roslyn/issues/55142 /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentHoverName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(HoverHandler)), Shared] + [Method(Methods.TextDocumentHoverName)] internal sealed class HoverHandler : AbstractStatelessRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -40,8 +40,6 @@ public HoverHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public override string Method => Methods.TextDocumentHoverName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindAllReferencesHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindAllReferencesHandler.cs index 858a994a40d2e..f9b8ee6bffc5f 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindAllReferencesHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindAllReferencesHandler.cs @@ -26,8 +26,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// we no longer reference VS classified text runs. /// See https://github.com/dotnet/roslyn/issues/55142 /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentReferencesName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FindAllReferencesHandler)), Shared] + [Method(LSP.Methods.TextDocumentReferencesName)] internal class FindAllReferencesHandler : AbstractStatelessRequestHandler { private readonly IMetadataAsSourceFileService _metadataAsSourceFileService; @@ -46,8 +46,6 @@ public FindAllReferencesHandler( _globalOptions = globalOptions; } - public override string Method => LSP.Methods.TextDocumentReferencesName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindImplementationsHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindImplementationsHandler.cs index f999ac967aa35..47c812558a823 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindImplementationsHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/References/FindImplementationsHandler.cs @@ -15,8 +15,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentImplementationName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FindImplementationsHandler)), Shared] + [Method(LSP.Methods.TextDocumentImplementationName)] internal sealed class FindImplementationsHandler : AbstractStatelessRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -28,8 +28,6 @@ public FindImplementationsHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public override string Method => LSP.Methods.TextDocumentImplementationName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Rename/RenameHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Rename/RenameHandler.cs index 64ef0497596c5..1fe3f331c93ac 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Rename/RenameHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Rename/RenameHandler.cs @@ -22,8 +22,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// we no longer reference the /// See https://github.com/dotnet/roslyn/issues/55142 /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentRenameName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(RenameHandler)), Shared] + [Method(LSP.Methods.TextDocumentRenameName)] internal class RenameHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -32,8 +32,6 @@ public RenameHandler() { } - public override string Method => LSP.Methods.TextDocumentRenameName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs index 16f75e7f2c34e..73e4395b05d32 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/DocumentSymbolsHandler.cs @@ -23,12 +23,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// TODO - This must be moved to the MS.CA.LanguageServer.Protocol project once /// we no longer reference VS icon types. /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentDocumentSymbolName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(DocumentSymbolsHandler)), Shared] + [Method(Methods.TextDocumentDocumentSymbolName)] internal class DocumentSymbolsHandler : AbstractStatelessRequestHandler { - public override string Method => Methods.TextDocumentDocumentSymbolName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs index 70c84edb1d134..e40947f5bb1f4 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Symbols/WorkspaceSymbolsHandler.cs @@ -20,8 +20,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// TODO - This must be moved to the MS.CA.LanguageServer.Protocol project once /// we no longer reference VS icon types. /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.WorkspaceSymbolName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(WorkspaceSymbolsHandler)), Shared] + [Method(Methods.WorkspaceSymbolName)] internal class WorkspaceSymbolsHandler : AbstractStatelessRequestHandler { private static readonly IImmutableSet s_supportedKinds = @@ -52,8 +52,6 @@ public WorkspaceSymbolsHandler( _threadingContext = threadingContext; } - public override string Method => Methods.WorkspaceSymbolName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/VisualStudioInProcLanguageServer.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/VisualStudioInProcLanguageServer.cs deleted file mode 100644 index 15b75347b2645..0000000000000 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/VisualStudioInProcLanguageServer.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.LanguageServer; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Roslyn.Utilities; -using StreamJsonRpc; - -using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient -{ - /// - /// Implementation of that also supports - /// VS LSP extension methods. - /// - internal class VisualStudioInProcLanguageServer : LanguageServerTarget - { - private readonly ImmutableArray _supportedLanguages; - - internal VisualStudioInProcLanguageServer( - AbstractRequestDispatcherFactory requestDispatcherFactory, - JsonRpc jsonRpc, - ICapabilitiesProvider capabilitiesProvider, - LspWorkspaceRegistrationService workspaceRegistrationService, - IGlobalOptionService globalOptions, - IAsynchronousOperationListenerProvider listenerProvider, - ILspLogger logger, - ImmutableArray supportedLanguages, - string? clientName, - WellKnownLspServerKinds serverKind) - : base(requestDispatcherFactory, jsonRpc, capabilitiesProvider, workspaceRegistrationService, lspMiscellaneousFilesWorkspace: null, globalOptions, listenerProvider, logger, supportedLanguages, clientName, serverKind) - { - _supportedLanguages = supportedLanguages; - } - - public override Task InitializedAsync() - { - return Task.CompletedTask; - } - - [JsonRpcMethod(VSInternalMethods.DocumentPullDiagnosticName, UseSingleObjectParameterDeserialization = true)] - public Task GetDocumentPullDiagnosticsAsync(VSInternalDocumentDiagnosticsParams diagnosticsParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync( - Queue, VSInternalMethods.DocumentPullDiagnosticName, - diagnosticsParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(VSInternalMethods.WorkspacePullDiagnosticName, UseSingleObjectParameterDeserialization = true)] - public Task GetWorkspacePullDiagnosticsAsync(VSInternalWorkspaceDiagnosticsParams diagnosticsParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync( - Queue, VSInternalMethods.WorkspacePullDiagnosticName, - diagnosticsParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(VSMethods.GetProjectContextsName, UseSingleObjectParameterDeserialization = true)] - public Task GetProjectContextsAsync(VSGetProjectContextsParams textDocumentWithContextParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, VSMethods.GetProjectContextsName, - textDocumentWithContextParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(VSInternalMethods.OnAutoInsertName, UseSingleObjectParameterDeserialization = true)] - public Task GetDocumentOnAutoInsertAsync(VSInternalDocumentOnAutoInsertParams autoInsertParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, VSInternalMethods.OnAutoInsertName, - autoInsertParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentLinkedEditingRangeName, UseSingleObjectParameterDeserialization = true)] - public Task GetLinkedEditingRangesAsync(LinkedEditingRangeParams renameParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentLinkedEditingRangeName, - renameParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(VSInternalMethods.TextDocumentInlineCompletionName, UseSingleObjectParameterDeserialization = true)] - public Task GetInlineCompletionsAsync(VSInternalInlineCompletionRequest request, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, VSInternalMethods.TextDocumentInlineCompletionName, - request, _clientCapabilities, ClientName, cancellationToken); - } - } -} diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index bacaddd1f8a5d..10e916934d8a3 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -5,12 +5,14 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editor.Implementation.LanguageClient; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Editor.UnitTests; @@ -25,9 +27,11 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; +using Nerdbank.Streams; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Roslyn.Utilities; +using StreamJsonRpc; using Xunit; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -93,6 +97,8 @@ protected class OrderLocations : Comparer protected virtual TestComposition Composition => s_composition; + protected static LSP.ClientCapabilities CapabilitiesWithVSExtensions => new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }; + /// /// Asserts two objects are equivalent by converting to JSON and ignoring whitespace. /// @@ -179,7 +185,7 @@ protected static LSP.TextDocumentIdentifier CreateTextDocumentIdentifier(Uri uri if (projectContext != null) { documentIdentifier.ProjectContext = - new LSP.VSProjectContext { Id = ProtocolConversions.ProjectIdToProjectContextId(projectContext) }; + new LSP.VSProjectContext { Id = ProtocolConversions.ProjectIdToProjectContextId(projectContext), Label = projectContext.DebugName!, Kind = LSP.VSProjectKind.CSharp }; } return documentIdentifier; @@ -239,9 +245,9 @@ protected static LSP.CompletionParams CreateCompletionParams( { TextEdit = textEdit, InsertText = insertText, - FilterText = filterText ?? label, + FilterText = filterText, Label = label, - SortText = sortText ?? label, + SortText = sortText, InsertTextFormat = LSP.InsertTextFormat.Plaintext, Kind = kind, Data = JObject.FromObject(new CompletionResolveData() @@ -277,25 +283,25 @@ private protected static CodeActionResolveData CreateCodeActionResolveData(strin /// /// Creates an LSP server backed by a workspace instance with a solution containing the markup. /// - protected Task CreateTestLspServerAsync(string markup) - => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.CSharp); + protected Task CreateTestLspServerAsync(string markup, LSP.ClientCapabilities? clientCapabilities = null) + => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.CSharp, clientCapabilities); - protected Task CreateVisualBasicTestLspServerAsync(string markup) - => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.VisualBasic); + protected Task CreateVisualBasicTestLspServerAsync(string markup, LSP.ClientCapabilities? clientCapabilities = null) + => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.VisualBasic, clientCapabilities); - protected Task CreateMultiProjectLspServerAsync(string xmlMarkup) - => CreateTestLspServerAsync(TestWorkspace.Create(xmlMarkup, composition: Composition), WellKnownLspServerKinds.AlwaysActiveVSLspServer); + protected Task CreateMultiProjectLspServerAsync(string xmlMarkup, LSP.ClientCapabilities? clientCapabilities = null) + => CreateTestLspServerAsync(TestWorkspace.Create(xmlMarkup, composition: Composition), clientCapabilities, WellKnownLspServerKinds.AlwaysActiveVSLspServer); /// /// Creates an LSP server backed by a workspace instance with a solution containing the specified documents. /// - protected Task CreateTestLspServerAsync(string[] markups) - => CreateTestLspServerAsync(markups, LanguageNames.CSharp); + protected Task CreateTestLspServerAsync(string[] markups, LSP.ClientCapabilities? clientCapabilities = null) + => CreateTestLspServerAsync(markups, LanguageNames.CSharp, clientCapabilities); - private protected Task CreateTestLspServerAsync(string markup, WellKnownLspServerKinds serverKind) - => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.CSharp, serverKind); + private protected Task CreateTestLspServerAsync(string markup, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind) + => CreateTestLspServerAsync(new string[] { markup }, LanguageNames.CSharp, clientCapabilities, serverKind); - private Task CreateTestLspServerAsync(string[] markups, string languageName, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) + private Task CreateTestLspServerAsync(string[] markups, string languageName, LSP.ClientCapabilities? clientCapabilities, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) { var workspace = languageName switch { @@ -304,10 +310,10 @@ private Task CreateTestLspServerAsync(string[] markups, string la _ => throw new ArgumentException($"language name {languageName} is not valid for a test workspace"), }; - return CreateTestLspServerAsync(workspace, serverKind); + return CreateTestLspServerAsync(workspace, clientCapabilities, serverKind); } - private static async Task CreateTestLspServerAsync(TestWorkspace workspace, WellKnownLspServerKinds serverKind) + private static async Task CreateTestLspServerAsync(TestWorkspace workspace, LSP.ClientCapabilities? clientCapabilities, WellKnownLspServerKinds serverKind) { var solution = workspace.CurrentSolution; @@ -323,10 +329,14 @@ private static async Task CreateTestLspServerAsync(TestWorkspace // created by the initial test steps. This can interfere with the expected test state. await WaitForWorkspaceOperationsAsync(workspace); - return new TestLspServer(workspace, serverKind); + return await TestLspServer.CreateAsync(workspace, clientCapabilities ?? new LSP.ClientCapabilities(), serverKind); } - protected async Task CreateXmlTestLspServerAsync(string xmlContent, string? workspaceKind = null) + private protected async Task CreateXmlTestLspServerAsync( + string xmlContent, + string? workspaceKind = null, + LSP.ClientCapabilities? clientCapabilities = null, + WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) { var workspace = TestWorkspace.Create(XElement.Parse(xmlContent), openDocuments: false, composition: Composition, workspaceKind: workspaceKind); @@ -334,7 +344,7 @@ protected async Task CreateXmlTestLspServerAsync(string xmlConten // Otherwise we could have a race where workspace change events triggered by creation are changing the state // created by the initial test steps. This can interfere with the expected test state. await WaitForWorkspaceOperationsAsync(workspace); - return new TestLspServer(workspace); + return await TestLspServer.CreateAsync(workspace, clientCapabilities ?? new LSP.ClientCapabilities(), serverKind); } /// @@ -396,20 +406,6 @@ static LSP.Location ConvertTextSpanWithTextToLocation(TextSpan span, SourceText } } - private static RequestDispatcher CreateRequestDispatcher(TestWorkspace workspace, WellKnownLspServerKinds serverKind) - { - var factory = workspace.ExportProvider.GetExportedValue(); - return factory.CreateRequestDispatcher(ProtocolConstants.RoslynLspLanguages, serverKind); - } - - private static RequestExecutionQueue CreateRequestQueue(TestWorkspace workspace, WellKnownLspServerKinds serverKind) - { - var registrationService = workspace.GetService(); - var globalOptions = workspace.GetService(); - var lspMiscFilesWorkspace = new LspMiscellaneousFilesWorkspace(NoOpLspLogger.Instance); - return new RequestExecutionQueue(NoOpLspLogger.Instance, registrationService, lspMiscFilesWorkspace, globalOptions, ProtocolConstants.RoslynLspLanguages, serverKind); - } - private static string GetDocumentFilePathFromName(string documentName) => "C:\\" + documentName; @@ -455,16 +451,27 @@ private static LSP.DidCloseTextDocumentParams CreateDidCloseTextDocumentParams(U public sealed class TestLspServer : IDisposable { public readonly TestWorkspace TestWorkspace; - private readonly RequestDispatcher _requestDispatcher; - private readonly RequestExecutionQueue _executionQueue; private readonly Dictionary> _locations; + private readonly LanguageServerTarget _languageServer; + private readonly JsonRpc _clientRpc; - internal TestLspServer(TestWorkspace testWorkspace, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) + public LSP.ClientCapabilities ClientCapabilities { get; } + + private TestLspServer(TestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) { TestWorkspace = testWorkspace; + ClientCapabilities = clientCapabilities; _locations = GetAnnotatedLocations(testWorkspace, testWorkspace.CurrentSolution); - _requestDispatcher = CreateRequestDispatcher(testWorkspace, serverKind); - _executionQueue = CreateRequestQueue(testWorkspace, serverKind); + + var (clientStream, serverStream) = FullDuplexStream.CreatePair(); + _languageServer = CreateLanguageServer(serverStream, serverStream, TestWorkspace, serverKind); + + _clientRpc = new JsonRpc(new HeaderDelimitedMessageHandler(clientStream, clientStream, CreateJsonMessageFormatter())) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; + + _clientRpc.StartListening(); var workspaceWaiter = GetWorkspaceWaiter(testWorkspace); Assert.False(workspaceWaiter.HasPendingWork); @@ -474,11 +481,60 @@ internal TestLspServer(TestWorkspace testWorkspace, WellKnownLspServerKinds serv GetManagerAccessor().ResetLspSolutions(); } - public Task ExecuteRequestAsync(string methodName, RequestType request, LSP.ClientCapabilities clientCapabilities, - string? clientName, CancellationToken cancellationToken) where RequestType : class + private static JsonMessageFormatter CreateJsonMessageFormatter() { - return _requestDispatcher.ExecuteRequestAsync( - _executionQueue, methodName, request, clientCapabilities, clientName, cancellationToken); + var messageFormatter = new JsonMessageFormatter(); + LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(messageFormatter.JsonSerializer); + return messageFormatter; + } + + internal static async Task CreateAsync(TestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind) + { + var server = new TestLspServer(testWorkspace, clientCapabilities, serverKind); + + await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams + { + Capabilities = clientCapabilities, + }, CancellationToken.None); + + return server; + } + + private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace, WellKnownLspServerKinds serverKind) + { + var dispatcherFactory = workspace.ExportProvider.GetExportedValue(); + var listenerProvider = workspace.ExportProvider.GetExportedValue(); + var lspWorkspaceRegistrationService = workspace.ExportProvider.GetExportedValue(); + var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); + + var jsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(outputStream, inputStream, CreateJsonMessageFormatter())) + { + ExceptionStrategy = ExceptionProcessing.ISerializable, + }; + + var globalOptions = workspace.GetService(); + + var languageServer = new LanguageServerTarget( + dispatcherFactory, + jsonRpc, + capabilitiesProvider, + lspWorkspaceRegistrationService, + new LspMiscellaneousFilesWorkspace(NoOpLspLogger.Instance), + globalOptions, + listenerProvider, + NoOpLspLogger.Instance, + ProtocolConstants.RoslynLspLanguages, + clientName: null, + serverKind); + + jsonRpc.StartListening(); + return languageServer; + } + + public async Task ExecuteRequestAsync(string methodName, RequestType request, CancellationToken cancellationToken) where RequestType : class + { + var result = await _clientRpc.InvokeWithParameterObjectAsync(methodName, request, cancellationToken: cancellationToken).ConfigureAwait(false); + return result; } public async Task OpenDocumentAsync(Uri documentUri, string? text = null) @@ -492,8 +548,7 @@ public async Task OpenDocumentAsync(Uri documentUri, string? text = null) } var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString()); - await ExecuteRequestAsync(LSP.Methods.TextDocumentDidOpenName, - didOpenParams, new LSP.ClientCapabilities(), null, CancellationToken.None); + await ExecuteRequestAsync(LSP.Methods.TextDocumentDidOpenName, didOpenParams, CancellationToken.None); } public Task InsertTextAsync(Uri documentUri, params (int Line, int Column, string Text)[] changes) @@ -510,8 +565,7 @@ public Task ReplaceTextAsync(Uri documentUri, params (LSP.Range Range, string Te var didChangeParams = CreateDidChangeTextDocumentParams( documentUri, changes.Select(change => (change.Range, change.Text)).ToImmutableArray()); - return ExecuteRequestAsync(LSP.Methods.TextDocumentDidChangeName, - didChangeParams, new LSP.ClientCapabilities(), clientName: null, CancellationToken.None); + return ExecuteRequestAsync(LSP.Methods.TextDocumentDidChangeName, didChangeParams, CancellationToken.None); } public Task DeleteTextAsync(Uri documentUri, params (int StartLine, int StartColumn, int EndLine, int EndColumn)[] changes) @@ -526,26 +580,34 @@ public Task DeleteTextAsync(Uri documentUri, params (int StartLine, int StartCol public Task CloseDocumentAsync(Uri documentUri) { var didCloseParams = CreateDidCloseTextDocumentParams(documentUri); - return ExecuteRequestAsync(LSP.Methods.TextDocumentDidCloseName, - didCloseParams, new LSP.ClientCapabilities(), null, CancellationToken.None); + return ExecuteRequestAsync(LSP.Methods.TextDocumentDidCloseName, didCloseParams, CancellationToken.None); } public IList GetLocations(string locationName) => _locations[locationName]; public Solution GetCurrentSolution() => TestWorkspace.CurrentSolution; - internal RequestExecutionQueue.TestAccessor GetQueueAccessor() => _executionQueue.GetTestAccessor(); + internal RequestExecutionQueue.TestAccessor GetQueueAccessor() => _languageServer.GetTestAccessor().GetQueueAccessor(); - internal RequestDispatcher.TestAccessor GetDispatcherAccessor() => _requestDispatcher.GetTestAccessor(); + internal RequestDispatcher.TestAccessor GetDispatcherAccessor() => _languageServer.GetTestAccessor().GetDispatcherAccessor(); - internal LspWorkspaceManager.TestAccessor GetManagerAccessor() => _executionQueue.GetTestAccessor().GetLspWorkspaceManager().GetTestAccessor(); + internal LspWorkspaceManager.TestAccessor GetManagerAccessor() => _languageServer.GetTestAccessor().GetManagerAccessor(); - internal LspWorkspaceManager GetManager() => _executionQueue.GetTestAccessor().GetLspWorkspaceManager(); + internal LspWorkspaceManager GetManager() => _languageServer.GetTestAccessor().GetQueueAccessor().GetLspWorkspaceManager(); + + internal LanguageServerTarget.TestAccessor GetServerAccessor() => _languageServer.GetTestAccessor(); public void Dispose() { + // Some tests manually call shutdown, so avoid calling shutdown twice if already called. + if (!_languageServer.HasShutdownStarted) + { + _languageServer.GetTestAccessor().ShutdownServer(); + } + + _languageServer.GetTestAccessor().ExitServer(); TestWorkspace.Dispose(); - _executionQueue.Shutdown(); + _clientRpc.Dispose(); } } } diff --git a/src/Features/LanguageServer/Protocol/AbstractRequestDispatcherFactory.cs b/src/Features/LanguageServer/Protocol/AbstractRequestDispatcherFactory.cs index 28a2cc4d88769..a99d9fb90c1cf 100644 --- a/src/Features/LanguageServer/Protocol/AbstractRequestDispatcherFactory.cs +++ b/src/Features/LanguageServer/Protocol/AbstractRequestDispatcherFactory.cs @@ -25,9 +25,9 @@ protected AbstractRequestDispatcherFactory(IEnumerable - public virtual RequestDispatcher CreateRequestDispatcher(ImmutableArray supportedLanguages, WellKnownLspServerKinds serverKind) + public virtual RequestDispatcher CreateRequestDispatcher(WellKnownLspServerKinds serverKind) { - return new RequestDispatcher(_requestHandlerProviders, supportedLanguages, serverKind); + return new RequestDispatcher(_requestHandlerProviders, serverKind); } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/AbstractStatelessRequestHandler.cs b/src/Features/LanguageServer/Protocol/Handler/AbstractStatelessRequestHandler.cs index f387fe0c853cd..0a6a28e50855f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/AbstractStatelessRequestHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/AbstractStatelessRequestHandler.cs @@ -16,8 +16,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal abstract class AbstractStatelessRequestHandler : AbstractRequestHandlerProvider, IRequestHandler { - public abstract string Method { get; } - public abstract bool MutatesSolutionState { get; } public abstract bool RequiresLSPSolution { get; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs index bbdd98ada1af9..cb3b812c1dd0f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Commands/AbstractExecuteWorkspaceCommandHandler.cs @@ -10,8 +10,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Commands { internal abstract class AbstractExecuteWorkspaceCommandHandler : IRequestHandler { - public string Method => GetRequestNameForCommandName(Command); - public abstract string Command { get; } public abstract bool MutatesSolutionState { get; } diff --git a/src/Features/LanguageServer/Protocol/Handler/Commands/ProvidesCommandAttribute.cs b/src/Features/LanguageServer/Protocol/Handler/Commands/ProvidesCommandAttribute.cs index cac027f582700..807fae56d473a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Commands/ProvidesCommandAttribute.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Commands/ProvidesCommandAttribute.cs @@ -9,9 +9,9 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Commands { [MetadataAttribute] [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal class ProvidesCommandAttribute : ProvidesMethodAttribute + internal class CommandAttribute : MethodAttribute { - public ProvidesCommandAttribute(string command) : base(AbstractExecuteWorkspaceCommandHandler.GetRequestNameForCommandName(command)) + public CommandAttribute(string command) : base(AbstractExecuteWorkspaceCommandHandler.GetRequestNameForCommandName(command)) { } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs index 619c6841f2928..48d8bdc01505e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToDefinitionHandler.cs @@ -12,8 +12,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentDefinitionName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(GoToDefinitionHandler)), Shared] + [Method(LSP.Methods.TextDocumentDefinitionName)] internal class GoToDefinitionHandler : AbstractGoToDefinitionHandler { [ImportingConstructor] @@ -22,8 +22,6 @@ public GoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileSe { } - public override string Method => LSP.Methods.TextDocumentDefinitionName; - public override Task HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) => GetDefinitionAsync(request, typeOnly: false, context, cancellationToken); } diff --git a/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs index 4b41a555f22ee..15270054a4d05 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Definitions/GoToTypeDefinitionHandler.cs @@ -12,8 +12,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentTypeDefinitionName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(GoToTypeDefinitionHandler)), Shared] + [Method(LSP.Methods.TextDocumentTypeDefinitionName)] internal class GoToTypeDefinitionHandler : AbstractGoToDefinitionHandler { [ImportingConstructor] @@ -22,8 +22,6 @@ public GoToTypeDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFi { } - public override string Method => LSP.Methods.TextDocumentTypeDefinitionName; - public override Task HandleRequestAsync(LSP.TextDocumentPositionParams request, RequestContext context, CancellationToken cancellationToken) => GetDefinitionAsync(request, typeOnly: true, context, cancellationToken); } diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index 897e27f637198..e20349a85299a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -83,8 +83,6 @@ protected record struct PreviousResult(string PreviousResultId, TextDocumentIden /// private long _nextDocumentResultId; - public abstract string Method { get; } - public bool MutatesSolutionState => false; public bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index 013c475be2abc..7b0305c504e59 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -13,12 +13,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { + [Method(VSInternalMethods.DocumentPullDiagnosticName)] internal class DocumentPullDiagnosticHandler : AbstractPullDiagnosticHandler { private readonly IDiagnosticAnalyzerService _analyzerService; - public override string Method => VSInternalMethods.DocumentPullDiagnosticName; - public DocumentPullDiagnosticHandler( WellKnownLspServerKinds serverKind, IDiagnosticService diagnosticService, diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandlerProvider.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandlerProvider.cs index e6bb96df420cd..1515c2ece2561 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandlerProvider.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandlerProvider.cs @@ -7,12 +7,10 @@ using System.Composition; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(VSInternalMethods.DocumentPullDiagnosticName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(DocumentPullDiagnosticHandler)), Shared] internal class DocumentPullDiagnosticHandlerProvider : AbstractRequestHandlerProvider { private readonly IDiagnosticService _diagnosticService; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticHandlerProvider.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticHandlerProvider.cs index f0773bd99cfb1..c85ed44ae7475 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticHandlerProvider.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticHandlerProvider.cs @@ -10,8 +10,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental; -[ExportRoslynLanguagesLspRequestHandlerProvider, Shared] -[ProvidesMethod(ExperimentalMethods.TextDocumentDiagnostic)] +[ExportRoslynLanguagesLspRequestHandlerProvider(typeof(ExperimentalDocumentPullDiagnosticsHandler)), Shared] internal class ExperimentalDocumentPullDiagnosticHandlerProvider : AbstractRequestHandlerProvider { private readonly IDiagnosticService _diagnosticService; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs index b7d48945e5727..7cfa1da7c5d21 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs @@ -20,6 +20,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental // See https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/common/proposed.diagnostics.md#textDocument_diagnostic using DocumentDiagnosticPartialReport = SumType, DocumentDiagnosticPartialResult>; +[Method(ExperimentalMethods.TextDocumentDiagnostic)] internal class ExperimentalDocumentPullDiagnosticsHandler : AbstractPullDiagnosticHandler { private readonly IDiagnosticAnalyzerService _analyzerService; @@ -33,8 +34,6 @@ public ExperimentalDocumentPullDiagnosticsHandler( _analyzerService = analyzerService; } - public override string Method => ExperimentalMethods.TextDocumentDiagnostic; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentDiagnosticParams diagnosticsParams) => diagnosticsParams.TextDocument; protected override DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticHandlerProvider.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticHandlerProvider.cs index df64fc655442d..eec7567c41db3 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticHandlerProvider.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticHandlerProvider.cs @@ -10,8 +10,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental; -[ExportRoslynLanguagesLspRequestHandlerProvider, Shared] -[ProvidesMethod(ExperimentalMethods.WorkspaceDiagnostic)] +[ExportRoslynLanguagesLspRequestHandlerProvider(typeof(ExperimentalWorkspacePullDiagnosticsHandler)), Shared] internal class ExperimentalWorkspacePullDiagnosticHandlerProvider : AbstractRequestHandlerProvider { private readonly IDiagnosticService _diagnosticService; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs index 312352a779b1c..fca7b5194834f 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalWorkspacePullDiagnosticsHandler.cs @@ -16,6 +16,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental using WorkspaceDocumentDiagnosticReport = SumType; +[Method(ExperimentalMethods.WorkspaceDiagnostic)] internal class ExperimentalWorkspacePullDiagnosticsHandler : AbstractPullDiagnosticHandler { private readonly IDiagnosticAnalyzerService _analyzerService; @@ -29,8 +30,6 @@ public ExperimentalWorkspacePullDiagnosticsHandler( _analyzerService = analyzerService; } - public override string Method => ExperimentalMethods.WorkspaceDiagnostic; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(WorkspaceDiagnosticParams diagnosticsParams) => null; protected override DiagnosticTag[] ConvertTags(DiagnosticData diagnosticData) diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index dda4dcee6b9ff..eca33b4f9e7a7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -16,10 +16,9 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { + [Method(VSInternalMethods.WorkspacePullDiagnosticName)] internal class WorkspacePullDiagnosticHandler : AbstractPullDiagnosticHandler { - public override string Method => VSInternalMethods.WorkspacePullDiagnosticName; - public WorkspacePullDiagnosticHandler(WellKnownLspServerKinds serverKind, IDiagnosticService diagnosticService) : base(serverKind, diagnosticService) { diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandlerProvider.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandlerProvider.cs index 92502896f43bd..14eb6d9170703 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandlerProvider.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandlerProvider.cs @@ -7,12 +7,10 @@ using System.Composition; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics { - [ExportRoslynLanguagesLspRequestHandlerProvider(), Shared] - [ProvidesMethod(VSInternalMethods.WorkspacePullDiagnosticName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(WorkspacePullDiagnosticHandler)), Shared] internal class WorkspacePullDiagnosticHandlerProvider : AbstractRequestHandlerProvider { private readonly IDiagnosticService _diagnosticService; diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs index e3a125802546c..edd50acb7fcc2 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs @@ -13,8 +13,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentDidChangeName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(DidChangeHandler)), Shared] + [Method(LSP.Methods.TextDocumentDidChangeName)] internal class DidChangeHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -23,8 +23,6 @@ public DidChangeHandler() { } - public override string Method => LSP.Methods.TextDocumentDidChangeName; - public override bool MutatesSolutionState => true; public override bool RequiresLSPSolution => false; diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs index eb179bc93bc94..c9b073e199aad 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidCloseHandler.cs @@ -12,8 +12,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentDidCloseName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(DidCloseHandler)), Shared] + [Method(LSP.Methods.TextDocumentDidCloseName)] internal class DidCloseHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -22,8 +22,6 @@ public DidCloseHandler() { } - public override string Method => LSP.Methods.TextDocumentDidCloseName; - public override bool MutatesSolutionState => true; public override bool RequiresLSPSolution => false; diff --git a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs index 376e4cadd0670..540c79b492e63 100644 --- a/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs @@ -13,8 +13,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.DocumentChanges { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentDidOpenName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(DidOpenHandler)), Shared] + [Method(LSP.Methods.TextDocumentDidOpenName)] internal class DidOpenHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -23,8 +23,6 @@ public DidOpenHandler() { } - public override string Method => LSP.Methods.TextDocumentDidOpenName; - public override bool MutatesSolutionState => true; public override bool RequiresLSPSolution => false; diff --git a/src/Features/LanguageServer/Protocol/Handler/ExportLspRequestHandlerProviderAttribute.cs b/src/Features/LanguageServer/Protocol/Handler/ExportLspRequestHandlerProviderAttribute.cs index 515c9188c6a4b..190d6e6c63a0b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ExportLspRequestHandlerProviderAttribute.cs +++ b/src/Features/LanguageServer/Protocol/Handler/ExportLspRequestHandlerProviderAttribute.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -15,25 +16,38 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler [AttributeUsage(AttributeTargets.Class), MetadataAttribute] internal class ExportLspRequestHandlerProviderAttribute : ExportAttribute { + public Type[] HandlerTypes { get; } + /// - /// The document languages that this handler supports. + /// Exports an and specifies the contract and + /// types this provider is associated with. /// - public string[] LanguageNames { get; } - - public ExportLspRequestHandlerProviderAttribute(params string[] languageNames) : base(typeof(AbstractRequestHandlerProvider)) + /// + /// The contract name this provider is exported. Used by + /// when importing handlers to ensure that it only imports handlers that match this contract. + /// This is important to ensure that we only load relevant providers (e.g. don't load Xaml providers when creating the c# server), + /// otherwise we will get dll load RPS regressions for the + /// + /// + /// The concrete type of the provided in + /// + /// + /// Additional if + /// provides more than one handler at once. + /// + public ExportLspRequestHandlerProviderAttribute(string contractName, Type firstHandlerType, params Type[] additionalHandlerTypes) : base(contractName, typeof(AbstractRequestHandlerProvider)) { - LanguageNames = languageNames; + HandlerTypes = additionalHandlerTypes.Concat(new[] { firstHandlerType }).ToArray(); } } /// - /// Defines an easy to use subclass for ExportLspRequestHandlerProviderAttribute that contains - /// all the language names that the default Roslyn servers support. + /// Defines an easy to use subclass for ExportLspRequestHandlerProviderAttribute with the roslyn languages contract name. /// [AttributeUsage(AttributeTargets.Class), MetadataAttribute] internal class ExportRoslynLanguagesLspRequestHandlerProviderAttribute : ExportLspRequestHandlerProviderAttribute { - public ExportRoslynLanguagesLspRequestHandlerProviderAttribute() : base(ProtocolConstants.RoslynLspLanguages.ToArray()) + public ExportRoslynLanguagesLspRequestHandlerProviderAttribute(Type firstHandlerType, params Type[] additionalHandlerTypes) : base(ProtocolConstants.RoslynLspLanguagesContract, firstHandlerType, additionalHandlerTypes) { } } diff --git a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs index c35f190587167..8a128c73832cf 100644 --- a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs @@ -15,12 +15,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentFoldingRangeName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FoldingRangesHandler)), Shared] + [Method(Methods.TextDocumentFoldingRangeName)] internal sealed class FoldingRangesHandler : AbstractStatelessRequestHandler { - public override string Method => Methods.TextDocumentFoldingRangeName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs index e6ac3d4095140..4c4d5f3f3bc40 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs @@ -11,8 +11,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentFormattingName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FormatDocumentHandler)), Shared] + [Method(LSP.Methods.TextDocumentFormattingName)] internal class FormatDocumentHandler : AbstractFormatDocumentHandlerBase { [ImportingConstructor] @@ -21,8 +21,6 @@ public FormatDocumentHandler() { } - public override string Method => LSP.Methods.TextDocumentFormattingName; - public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync( diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 50fd055f82a37..003d8462a93d2 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -19,12 +19,10 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentOnTypeFormattingName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FormatDocumentOnTypeHandler)), Shared] + [Method(Methods.TextDocumentOnTypeFormattingName)] internal class FormatDocumentOnTypeHandler : AbstractStatelessRequestHandler { - public override string Method => Methods.TextDocumentOnTypeFormattingName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs index 0d6a573e01360..a83f246b48926 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -11,8 +11,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentRangeFormattingName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FormatDocumentRangeHandler)), Shared] + [Method(Methods.TextDocumentRangeFormattingName)] internal class FormatDocumentRangeHandler : AbstractFormatDocumentHandlerBase { [ImportingConstructor] @@ -21,8 +21,6 @@ public FormatDocumentRangeHandler() { } - public override string Method => Methods.TextDocumentRangeFormattingName; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync( diff --git a/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs index b4ec6691eb9e2..0f5719c1f35f1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Highlights/DocumentHighlightHandler.cs @@ -19,8 +19,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentDocumentHighlightName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(DocumentHighlightsHandler)), Shared] + [Method(Methods.TextDocumentDocumentHighlightName)] internal class DocumentHighlightsHandler : AbstractStatelessRequestHandler { private readonly IHighlightingService _highlightingService; @@ -32,8 +32,6 @@ public DocumentHighlightsHandler(IHighlightingService highlightingService) _highlightingService = highlightingService; } - public override string Method => Methods.TextDocumentDocumentHighlightName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs b/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs index 12d6d9eb004ef..987800742759b 100644 --- a/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/IRequestHandler.cs @@ -13,11 +13,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal interface IRequestHandler { - /// - /// The LSP method that this implements. - /// - string Method { get; } - /// /// Whether or not the solution state on the server is modified /// as a part of handling this request. diff --git a/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs index ffa5eb61b7eb7..4066e54f1a6b4 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs @@ -28,8 +28,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.InlineCompletions; /// /// Supports built in legacy snippets for razor scenarios. /// -[ExportRoslynLanguagesLspRequestHandlerProvider, Shared] -[ProvidesMethod(VSInternalMethods.TextDocumentInlineCompletionName)] +[ExportRoslynLanguagesLspRequestHandlerProvider(typeof(InlineCompletionsHandler)), Shared] +[Method(VSInternalMethods.TextDocumentInlineCompletionName)] internal partial class InlineCompletionsHandler : AbstractStatelessRequestHandler { /// @@ -48,8 +48,6 @@ public InlineCompletionsHandler() { } - public override string Method => VSInternalMethods.TextDocumentInlineCompletionName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/ProvidesMethodAttribute.cs b/src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs similarity index 61% rename from src/Features/LanguageServer/Protocol/Handler/ProvidesMethodAttribute.cs rename to src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs index e85e042cb6990..091829e570413 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ProvidesMethodAttribute.cs +++ b/src/Features/LanguageServer/Protocol/Handler/MethodAttribute.cs @@ -8,12 +8,15 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [MetadataAttribute] - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal class ProvidesMethodAttribute : Attribute + [AttributeUsage(AttributeTargets.Class)] + internal class MethodAttribute : Attribute { + /// + /// Contains the method that this implements. + /// public string Method { get; } - public ProvidesMethodAttribute(string method) + public MethodAttribute(string method) { Method = method; } diff --git a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index c536ad0e84e08..78655e0c7274c 100644 --- a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -24,15 +24,13 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.VSInternalMethods.OnAutoInsertName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(OnAutoInsertHandler)), Shared] + [Method(LSP.VSInternalMethods.OnAutoInsertName)] internal class OnAutoInsertHandler : AbstractStatelessRequestHandler { private readonly ImmutableArray _csharpBraceCompletionServices; private readonly ImmutableArray _visualBasicBraceCompletionServices; - public override string Method => LSP.VSInternalMethods.OnAutoInsertName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs b/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs index 6e72c9b341e8d..eb14e29a7de66 100644 --- a/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/ProjectContext/GetTextDocumentWithContextHandler.cs @@ -15,8 +15,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(VSMethods.GetProjectContextsName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(GetTextDocumentWithContextHandler)), Shared] + [Method(VSMethods.GetProjectContextsName)] internal class GetTextDocumentWithContextHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -25,8 +25,6 @@ public GetTextDocumentWithContextHandler() { } - public override string Method => VSMethods.GetProjectContextsName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestHandlerProviderMetadataView.cs b/src/Features/LanguageServer/Protocol/Handler/RequestHandlerProviderMetadataView.cs index 6970602870e36..430844dab7173 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestHandlerProviderMetadataView.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestHandlerProviderMetadataView.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; +using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { @@ -13,36 +15,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler /// internal class RequestHandlerProviderMetadataView { - public string[] LanguageNames { get; set; } - - public string[] Methods { get; set; } + public ImmutableArray HandlerTypes { get; set; } public RequestHandlerProviderMetadataView(IDictionary metadata) { - var methodMetadata = metadata["Method"]; - Methods = ConvertMetadataToArray(methodMetadata); - - var languageMetadata = metadata["LanguageNames"]; - LanguageNames = ConvertMetadataToArray(languageMetadata); - } - - /// - /// When multiple of the same attribute are defined on a class, the metadata - /// is aggregated into an array. However, when just one of the same attribute is defined, - /// the metadata is not aggregated and is just a string. - /// MEF cannot construct the metadata object when it sees just the string type with AllowMultiple = true, - /// so we override and construct it ourselves here. - /// - private static string[] ConvertMetadataToArray(object metadata) - { - if (metadata is string[] arrayData) - { - return arrayData; - } - else - { - return new string[] { (string)metadata }; - } + var handlerMetadata = (Type[])metadata[nameof(ExportLspRequestHandlerProviderAttribute.HandlerTypes)]; + HandlerTypes = handlerMetadata.ToImmutableArray(); } } } diff --git a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs index 2f9513c825157..bdd4c460a84e1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SemanticTokens/SemanticTokensRangeHandler.cs @@ -18,14 +18,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens /// /// Computes the semantic tokens for a given range. /// - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(Methods.TextDocumentSemanticTokensRangeName)] - internal sealed class SemanticTokensRangeHandler : AbstractStatelessRequestHandler + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(SemanticTokensRangeHandler)), Shared] + [Method(Methods.TextDocumentSemanticTokensRangeName)] + internal class SemanticTokensRangeHandler : AbstractStatelessRequestHandler { private readonly IGlobalOptionService _globalOptions; - public override string Method => LSP.Methods.TextDocumentSemanticTokensRangeName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs index e11f9612b3cd1..87008c6b60184 100644 --- a/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/SignatureHelp/SignatureHelpHandler.cs @@ -18,8 +18,8 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { - [ExportRoslynLanguagesLspRequestHandlerProvider, Shared] - [ProvidesMethod(LSP.Methods.TextDocumentSignatureHelpName)] + [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(SignatureHelpHandler)), Shared] + [Method(LSP.Methods.TextDocumentSignatureHelpName)] internal class SignatureHelpHandler : AbstractStatelessRequestHandler { private readonly IEnumerable> _allProviders; @@ -35,8 +35,6 @@ public SignatureHelpHandler( _globalOptions = globalOptions; } - public override string Method => LSP.Methods.TextDocumentSignatureHelpName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs b/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs index da4a8a891fcbe..3b7ae888b05b8 100644 --- a/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs +++ b/src/Features/LanguageServer/Protocol/LanguageServerTarget.cs @@ -8,7 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics.Experimental; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -23,17 +23,15 @@ internal class LanguageServerTarget : ILanguageServerTarget { private readonly ICapabilitiesProvider _capabilitiesProvider; - protected readonly IGlobalOptionService GlobalOptions; - protected readonly JsonRpc JsonRpc; - protected readonly RequestDispatcher RequestDispatcher; - protected readonly RequestExecutionQueue Queue; - protected readonly LspWorkspaceRegistrationService WorkspaceRegistrationService; - protected readonly IAsynchronousOperationListener Listener; - protected readonly ILspLogger Logger; - protected readonly string? ClientName; + private readonly JsonRpc _jsonRpc; + private readonly RequestDispatcher _requestDispatcher; + private readonly RequestExecutionQueue _queue; + private readonly IAsynchronousOperationListener _listener; + private readonly ILspLogger _logger; + private readonly string? _clientName; // Set on first LSP initialize request. - protected ClientCapabilities? _clientCapabilities; + private ClientCapabilities? _clientCapabilities; // Fields used during shutdown. private bool _shuttingDown; @@ -54,28 +52,74 @@ internal LanguageServerTarget( string? clientName, WellKnownLspServerKinds serverKind) { - GlobalOptions = globalOptions; - RequestDispatcher = requestDispatcherFactory.CreateRequestDispatcher(supportedLanguages, serverKind); + _requestDispatcher = requestDispatcherFactory.CreateRequestDispatcher(serverKind); _capabilitiesProvider = capabilitiesProvider; - WorkspaceRegistrationService = workspaceRegistrationService; - Logger = logger; + _logger = logger; - JsonRpc = jsonRpc; - JsonRpc.AddLocalRpcTarget(this); - JsonRpc.Disconnected += JsonRpc_Disconnected; + _jsonRpc = jsonRpc; + _jsonRpc.AddLocalRpcTarget(this); + _jsonRpc.Disconnected += JsonRpc_Disconnected; - Listener = listenerProvider.GetListener(FeatureAttribute.LanguageServer); - ClientName = clientName; + _listener = listenerProvider.GetListener(FeatureAttribute.LanguageServer); + _clientName = clientName; - Queue = new RequestExecutionQueue( + _queue = new RequestExecutionQueue( logger, workspaceRegistrationService, lspMiscellaneousFilesWorkspace, globalOptions, supportedLanguages, serverKind); - Queue.RequestServerShutdown += RequestExecutionQueue_Errored; + _queue.RequestServerShutdown += RequestExecutionQueue_Errored; + + var entryPointMethod = typeof(DelegatingEntryPoint).GetMethod(nameof(DelegatingEntryPoint.EntryPointAsync)); + Contract.ThrowIfNull(entryPointMethod, $"{typeof(DelegatingEntryPoint).FullName} is missing method {nameof(DelegatingEntryPoint.EntryPointAsync)}"); + + foreach (var metadata in _requestDispatcher.GetRegisteredMethods()) + { + // Instead of concretely defining methods for each LSP method, we instead dynamically construct + // the generic method info from the exported handler types. This allows us to define multiple handlers for the same method + // but different type parameters. This is a key functionality to support TS external access as we do not want to couple + // our LSP protocol version dll to theirs. + // + // We also do not use the StreamJsonRpc support for JToken as the rpc method parameters because we want + // StreamJsonRpc to do the deserialization to handle streaming requests using IProgress. + var delegatingEntryPoint = new DelegatingEntryPoint(metadata.MethodName, this); + + var genericEntryPointMethod = entryPointMethod.MakeGenericMethod(metadata.RequestType, metadata.ResponseType); + + _jsonRpc.AddLocalRpcMethod(genericEntryPointMethod, delegatingEntryPoint, new JsonRpcMethodAttribute(metadata.MethodName) { UseSingleObjectParameterDeserialization = true }); + } + } + + /// + /// Wrapper class to hold the method and properties from the + /// that the method info passed to streamjsonrpc is created from. + /// + private class DelegatingEntryPoint + { + private readonly string _method; + private readonly LanguageServerTarget _target; + + public DelegatingEntryPoint(string method, LanguageServerTarget target) + { + _method = method; + _target = target; + } + + public async Task EntryPointAsync(TRequestType requestType, CancellationToken cancellationToken) where TRequestType : class + { + Contract.ThrowIfNull(_target._clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); + var result = await _target._requestDispatcher.ExecuteRequestAsync( + _method, + requestType, + _target._clientCapabilities, + _target._clientName, + _target._queue, + cancellationToken).ConfigureAwait(false); + return result; + } } /// @@ -87,7 +131,7 @@ public Task InitializeAsync(InitializeParams initializeParams, { try { - Logger?.TraceStart("Initialize"); + _logger?.TraceStart("Initialize"); Contract.ThrowIfTrue(_clientCapabilities != null, $"{nameof(InitializeAsync)} called multiple times"); _clientCapabilities = initializeParams.Capabilities; @@ -98,7 +142,7 @@ public Task InitializeAsync(InitializeParams initializeParams, } finally { - Logger?.TraceStop("Initialize"); + _logger?.TraceStop("Initialize"); } } @@ -113,7 +157,7 @@ public Task ShutdownAsync(CancellationToken _) { try { - Logger?.TraceStart("Shutdown"); + _logger?.TraceStart("Shutdown"); ShutdownImpl(); @@ -121,11 +165,11 @@ public Task ShutdownAsync(CancellationToken _) } finally { - Logger?.TraceStop("Shutdown"); + _logger?.TraceStop("Shutdown"); } } - protected void ShutdownImpl() + private void ShutdownImpl() { Contract.ThrowIfTrue(_shuttingDown, "Shutdown has already been called."); @@ -139,7 +183,7 @@ public Task ExitAsync(CancellationToken _) { try { - Logger?.TraceStart("Exit"); + _logger?.TraceStart("Exit"); ExitImpl(); @@ -147,7 +191,7 @@ public Task ExitAsync(CancellationToken _) } finally { - Logger?.TraceStop("Exit"); + _logger?.TraceStop("Exit"); } } @@ -156,8 +200,8 @@ private void ExitImpl() try { ShutdownRequestQueue(); - JsonRpc.Disconnected -= JsonRpc_Disconnected; - JsonRpc.Dispose(); + _jsonRpc.Disconnected -= JsonRpc_Disconnected; + _jsonRpc.Dispose(); } catch (Exception e) when (FatalError.ReportAndCatch(e)) { @@ -166,239 +210,38 @@ private void ExitImpl() } } - [JsonRpcMethod(Methods.TextDocumentDefinitionName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentDefinitionAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentDefinitionName, - textDocumentPositionParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentRenameName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentRenameAsync(RenameParams renameParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentRenameName, - renameParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentReferencesName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentReferencesAsync(ReferenceParams referencesParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentReferencesName, - referencesParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentCodeActionName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentCodeActionsAsync(CodeActionParams codeActionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentCodeActionName, codeActionParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.CodeActionResolveName, UseSingleObjectParameterDeserialization = true)] - public Task ResolveCodeActionAsync(CodeAction codeAction, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.CodeActionResolveName, - codeAction, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentCompletionName, UseSingleObjectParameterDeserialization = true)] - public async Task> GetTextDocumentCompletionAsync(CompletionParams completionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - // Convert to sumtype before reporting to work around https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1107698 - return await RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentCompletionName, - completionParams, _clientCapabilities, ClientName, cancellationToken).ConfigureAwait(false); - } - - [JsonRpcMethod(Methods.TextDocumentCompletionResolveName, UseSingleObjectParameterDeserialization = true)] - public Task ResolveCompletionItemAsync(CompletionItem completionItem, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentCompletionResolveName, completionItem, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentFoldingRangeName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentFoldingRangeAsync(FoldingRangeParams textDocumentFoldingRangeParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentFoldingRangeName, textDocumentFoldingRangeParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentDocumentHighlightName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentDocumentHighlightsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentDocumentHighlightName, textDocumentPositionParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentHoverName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentDocumentHoverAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentHoverName, textDocumentPositionParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentDocumentSymbolName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentDocumentSymbolsAsync(DocumentSymbolParams documentSymbolParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentDocumentSymbolName, documentSymbolParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentFormattingName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentFormattingAsync(DocumentFormattingParams documentFormattingParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentFormattingName, documentFormattingParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentOnTypeFormattingName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentFormattingOnTypeAsync(DocumentOnTypeFormattingParams documentOnTypeFormattingParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentOnTypeFormattingName, documentOnTypeFormattingParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentImplementationName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentImplementationsAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentImplementationName, textDocumentPositionParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentRangeFormattingName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentRangeFormattingAsync(DocumentRangeFormattingParams documentRangeFormattingParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentRangeFormattingName, documentRangeFormattingParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentSignatureHelpName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentSignatureHelpAsync(TextDocumentPositionParams textDocumentPositionParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentSignatureHelpName, textDocumentPositionParams, _clientCapabilities, ClientName, cancellationToken); - } - + /// + /// Specially handle the execute workspace command method as we have to deserialize the request + /// to figure out which actually handles it. + /// [JsonRpcMethod(Methods.WorkspaceExecuteCommandName, UseSingleObjectParameterDeserialization = true)] - public Task ExecuteWorkspaceCommandAsync(ExecuteCommandParams executeCommandParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.WorkspaceExecuteCommandName, executeCommandParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.WorkspaceSymbolName, UseSingleObjectParameterDeserialization = true)] - public Task GetWorkspaceSymbolsAsync(WorkspaceSymbolParams workspaceSymbolParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.WorkspaceSymbolName, workspaceSymbolParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentSemanticTokensFullName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentSemanticTokensAsync(SemanticTokensParams semanticTokensParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentSemanticTokensFullName, - semanticTokensParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentSemanticTokensFullDeltaName, UseSingleObjectParameterDeserialization = true)] - public Task> GetTextDocumentSemanticTokensEditsAsync(SemanticTokensDeltaParams semanticTokensEditsParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync>(Queue, Methods.TextDocumentSemanticTokensFullDeltaName, - semanticTokensEditsParams, _clientCapabilities, ClientName, cancellationToken); - } - - // Note: Since a range request is always received in conjunction with a whole document request, we don't need to cache range results. - [JsonRpcMethod(Methods.TextDocumentSemanticTokensRangeName, UseSingleObjectParameterDeserialization = true)] - public Task GetTextDocumentSemanticTokensRangeAsync(SemanticTokensRangeParams semanticTokensRangeParams, CancellationToken cancellationToken) + public async Task ExecuteWorkspaceCommandAsync(LSP.ExecuteCommandParams request, CancellationToken cancellationToken) { Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); + var requestMethod = AbstractExecuteWorkspaceCommandHandler.GetRequestNameForCommandName(request.Command); - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentSemanticTokensRangeName, - semanticTokensRangeParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentDidChangeName, UseSingleObjectParameterDeserialization = true)] - public Task HandleDocumentDidChangeAsync(DidChangeTextDocumentParams didChangeParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentDidChangeName, - didChangeParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentDidOpenName, UseSingleObjectParameterDeserialization = true)] - public Task HandleDocumentDidOpenAsync(DidOpenTextDocumentParams didOpenParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentDidOpenName, - didOpenParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(Methods.TextDocumentDidCloseName, UseSingleObjectParameterDeserialization = true)] - public Task HandleDocumentDidCloseAsync(DidCloseTextDocumentParams didCloseParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - - return RequestDispatcher.ExecuteRequestAsync(Queue, Methods.TextDocumentDidCloseName, - didCloseParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(ExperimentalMethods.TextDocumentDiagnostic, UseSingleObjectParameterDeserialization = true)] - public Task?> HandleDocumentDiagnosticsAsync(DocumentDiagnosticParams documentDiagnosticParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - return RequestDispatcher.ExecuteRequestAsync?>(Queue, ExperimentalMethods.TextDocumentDiagnostic, - documentDiagnosticParams, _clientCapabilities, ClientName, cancellationToken); - } - - [JsonRpcMethod(ExperimentalMethods.WorkspaceDiagnostic, UseSingleObjectParameterDeserialization = true)] - public Task HandleWorkspaceDiagnosticsAsync(WorkspaceDiagnosticParams workspaceDiagnosticParams, CancellationToken cancellationToken) - { - Contract.ThrowIfNull(_clientCapabilities, $"{nameof(InitializeAsync)} has not been called."); - return RequestDispatcher.ExecuteRequestAsync(Queue, ExperimentalMethods.WorkspaceDiagnostic, - workspaceDiagnosticParams, _clientCapabilities, ClientName, cancellationToken); + var result = await _requestDispatcher.ExecuteRequestAsync( + requestMethod, + request, + _clientCapabilities, + _clientName, + _queue, + cancellationToken).ConfigureAwait(false); + return result; } private void ShutdownRequestQueue() { - Queue.RequestServerShutdown -= RequestExecutionQueue_Errored; + _queue.RequestServerShutdown -= RequestExecutionQueue_Errored; // if the queue requested shutdown via its event, it will have already shut itself down, but this // won't cause any problems calling it again - Queue.Shutdown(); + _queue.Shutdown(); } private void RequestExecutionQueue_Errored(object? sender, RequestShutdownEventArgs e) { // log message and shut down - Logger?.TraceWarning($"Request queue is requesting shutdown due to error: {e.Message}"); + _logger?.TraceWarning($"Request queue is requesting shutdown due to error: {e.Message}"); var message = new LogMessageParams() { @@ -406,12 +249,12 @@ private void RequestExecutionQueue_Errored(object? sender, RequestShutdownEventA Message = e.Message }; - var asyncToken = Listener.BeginAsyncOperation(nameof(RequestExecutionQueue_Errored)); + var asyncToken = _listener.BeginAsyncOperation(nameof(RequestExecutionQueue_Errored)); _errorShutdownTask = Task.Run(async () => { - Logger?.TraceInformation("Shutting down language server."); + _logger?.TraceInformation("Shutting down language server."); - await JsonRpc.NotifyWithParameterObjectAsync(Methods.WindowLogMessageName, message).ConfigureAwait(false); + await _jsonRpc.NotifyWithParameterObjectAsync(Methods.WindowLogMessageName, message).ConfigureAwait(false); ShutdownImpl(); ExitImpl(); @@ -429,7 +272,7 @@ private void JsonRpc_Disconnected(object? sender, JsonRpcDisconnectedEventArgs e return; } - Logger?.TraceWarning($"Encountered unexpected jsonrpc disconnect, Reason={e.Reason}, Description={e.Description}, Exception={e.Exception}"); + _logger?.TraceWarning($"Encountered unexpected jsonrpc disconnect, Reason={e.Reason}, Description={e.Description}, Exception={e.Exception}"); ShutdownImpl(); ExitImpl(); @@ -441,22 +284,37 @@ public async ValueTask DisposeAsync() if (_errorShutdownTask is not null) await _errorShutdownTask.ConfigureAwait(false); - if (Logger is IDisposable disposableLogger) + if (_logger is IDisposable disposableLogger) disposableLogger.Dispose(); } - internal TestAccessor GetTestAccessor() - => new TestAccessor(this.Queue); + internal TestAccessor GetTestAccessor() => new(this); internal readonly struct TestAccessor { - private readonly RequestExecutionQueue _queue; + private readonly LanguageServerTarget _server; + + internal TestAccessor(LanguageServerTarget server) + { + _server = server; + } + + internal RequestExecutionQueue.TestAccessor GetQueueAccessor() + => _server._queue.GetTestAccessor(); + + internal LspWorkspaceManager.TestAccessor GetManagerAccessor() + => _server._queue.GetTestAccessor().GetLspWorkspaceManager().GetTestAccessor(); + + internal RequestDispatcher.TestAccessor GetDispatcherAccessor() + => _server._requestDispatcher.GetTestAccessor(); + + internal JsonRpc GetServerRpc() => _server._jsonRpc; + + internal bool HasShutdownStarted() => _server.HasShutdownStarted; - public TestAccessor(RequestExecutionQueue queue) - => _queue = queue; + internal void ShutdownServer() => _server.ShutdownImpl(); - public RequestExecutionQueue.TestAccessor GetQueueAccessor() - => _queue.GetTestAccessor(); + internal void ExitServer() => _server.ExitImpl(); } } } diff --git a/src/Features/LanguageServer/Protocol/ProtocolConstants.cs b/src/Features/LanguageServer/Protocol/ProtocolConstants.cs index 9eb2b53b9981c..aa29f366061b0 100644 --- a/src/Features/LanguageServer/Protocol/ProtocolConstants.cs +++ b/src/Features/LanguageServer/Protocol/ProtocolConstants.cs @@ -11,5 +11,7 @@ internal class ProtocolConstants public const string RazorCSharp = "RazorCSharp"; public static ImmutableArray RoslynLspLanguages = ImmutableArray.Create(LanguageNames.CSharp, LanguageNames.VisualBasic, LanguageNames.FSharp); + + public const string RoslynLspLanguagesContract = "RoslynLspLanguages"; } } diff --git a/src/Features/LanguageServer/Protocol/RequestDispatcher.cs b/src/Features/LanguageServer/Protocol/RequestDispatcher.cs index 7a0f6047c2155..618c6ab65e78d 100644 --- a/src/Features/LanguageServer/Protocol/RequestDispatcher.cs +++ b/src/Features/LanguageServer/Protocol/RequestDispatcher.cs @@ -9,91 +9,126 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; -using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer { + internal readonly record struct RequestHandlerMetadata(string MethodName, Type RequestType, Type ResponseType); + /// /// Aggregates handlers for the specified languages and dispatches LSP requests /// to the appropriate handler for the request. /// internal class RequestDispatcher { - private readonly ImmutableDictionary> _requestHandlers; + private readonly ImmutableDictionary> _requestHandlers; public RequestDispatcher( + // Lazily imported handler providers to avoid instantiating providers until they are directly needed. ImmutableArray> requestHandlerProviders, - ImmutableArray languageNames, WellKnownLspServerKinds serverKind) { - _requestHandlers = CreateMethodToHandlerMap(requestHandlerProviders.Where(rh => languageNames.All(languageName => rh.Metadata.LanguageNames.Contains(languageName))), serverKind); + _requestHandlers = CreateMethodToHandlerMap(requestHandlerProviders, serverKind); } - private static ImmutableDictionary> CreateMethodToHandlerMap( - IEnumerable> requestHandlerProviders, - WellKnownLspServerKinds serverKind) + private static ImmutableDictionary> CreateMethodToHandlerMap( + IEnumerable> requestHandlerProviders, WellKnownLspServerKinds serverKind) { - var requestHandlerDictionary = ImmutableDictionary.CreateBuilder>(StringComparer.OrdinalIgnoreCase); + var requestHandlerDictionary = ImmutableDictionary.CreateBuilder>(); // Store the request handlers in a dictionary from request name to handler instance. foreach (var handlerProvider in requestHandlerProviders) { - var methods = handlerProvider.Metadata.Methods; + var handlerTypes = handlerProvider.Metadata.HandlerTypes; // Instantiate all the providers as one lazy object and re-use it for all methods that the provider provides handlers for. // This ensures 2 things: // 1. That the handler provider is not instantiated (and therefore its dependencies are not) until a handler it provides is needed. // 2. That the handler provider's CreateRequestHandlers is only called once and always returns the same handler instances. - var lazyProviders = new Lazy>(() => handlerProvider.Value.CreateRequestHandlers(serverKind).ToImmutableDictionary(p => p.Method, p => p, StringComparer.OrdinalIgnoreCase)); + var lazyProviders = new Lazy>(() => handlerProvider.Value.CreateRequestHandlers(serverKind) + .ToImmutableDictionary(p => GetRequestHandlerMethod(p.GetType()), p => p, StringComparer.OrdinalIgnoreCase)); - foreach (var method in methods) + foreach (var handlerType in handlerTypes) { + var (requestType, responseType) = ConvertHandlerTypeToRequestResponseTypes(handlerType); + var method = GetRequestHandlerMethod(handlerType); + // Using the lazy set of handlers, create a lazy instance that will resolve the set of handlers for the provider // and then lookup the correct handler for the specified method. - requestHandlerDictionary.Add(method, new Lazy(() => lazyProviders.Value[method])); + requestHandlerDictionary.Add(new RequestHandlerMetadata(method, requestType, responseType), new Lazy(() => lazyProviders.Value[method])); } } return requestHandlerDictionary.ToImmutable(); + + static string GetRequestHandlerMethod(Type handlerType) + { + // Get the LSP method name from the handler's method name attribute. + var methodAttribute = Attribute.GetCustomAttribute(handlerType, typeof(MethodAttribute)) as MethodAttribute; + Contract.ThrowIfNull(methodAttribute, $"{handlerType.FullName} is missing Method attribute"); + + return methodAttribute.Method; + } } - public Task ExecuteRequestAsync( - RequestExecutionQueue queue, + /// + /// Retrieves the generic argument information from the request handler type without instantiating it. + /// + private static (Type requestType, Type responseType) ConvertHandlerTypeToRequestResponseTypes(Type handlerType) + { + var requestHandlerGenericType = handlerType.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)).SingleOrDefault(); + Contract.ThrowIfNull(requestHandlerGenericType, $"Provided handler type {handlerType.FullName} does not implement IRequestHandler<,>"); + + var genericArguments = requestHandlerGenericType.GetGenericArguments(); + Contract.ThrowIfFalse(genericArguments.Length == 2, $"Provided handler type {handlerType.FullName} does not have exactly two generic arguments"); + var requestType = genericArguments[0]; + var responseType = genericArguments[1]; + + return (requestType, responseType); + } + + public async Task ExecuteRequestAsync( string methodName, - RequestType request, + TRequestType request, LSP.ClientCapabilities clientCapabilities, string? clientName, - CancellationToken cancellationToken) where RequestType : class + RequestExecutionQueue queue, + CancellationToken cancellationToken) where TRequestType : class { - Contract.ThrowIfNull(request); - Contract.ThrowIfTrue(string.IsNullOrEmpty(methodName), "Invalid method name"); - - if (request is ExecuteCommandParams executeCommandRequest) - { - // If we have a workspace/executeCommand request, get the request name - // from the command name. - methodName = AbstractExecuteWorkspaceCommandHandler.GetRequestNameForCommandName(executeCommandRequest.Command); - } + // Get the handler matching the requested method. + var requestHandlerMetadata = new RequestHandlerMetadata(methodName, typeof(TRequestType), typeof(TResponseType)); - var handlerEntry = _requestHandlers[methodName]; - Contract.ThrowIfNull(handlerEntry, string.Format("Request handler entry not found for method {0}", methodName)); + var handler = _requestHandlers[requestHandlerMetadata].Value; - var mutatesSolutionState = handlerEntry.Value.MutatesSolutionState; - var requiresLSPSolution = handlerEntry.Value.RequiresLSPSolution; + var mutatesSolutionState = handler.MutatesSolutionState; + var requiresLspSolution = handler.RequiresLSPSolution; - var handler = (IRequestHandler?)handlerEntry.Value; - Contract.ThrowIfNull(handler, string.Format("Request handler not found for method {0}", methodName)); + var strongHandler = (IRequestHandler?)handler; + Contract.ThrowIfNull(strongHandler, string.Format("Request handler not found for method {0}", methodName)); - return ExecuteRequestAsync(queue, request, clientCapabilities, clientName, methodName, mutatesSolutionState, requiresLSPSolution, handler, cancellationToken); + var result = await ExecuteRequestAsync(queue, mutatesSolutionState, requiresLspSolution, strongHandler, request, clientCapabilities, clientName, methodName, cancellationToken).ConfigureAwait(false); + return result; } - protected virtual Task ExecuteRequestAsync(RequestExecutionQueue queue, RequestType request, ClientCapabilities clientCapabilities, string? clientName, string methodName, bool mutatesSolutionState, bool requiresLSPSolution, IRequestHandler handler, CancellationToken cancellationToken) where RequestType : class + protected virtual Task ExecuteRequestAsync( + RequestExecutionQueue queue, + bool mutatesSolutionState, + bool requiresLSPSolution, + IRequestHandler handler, + TRequestType request, + LSP.ClientCapabilities clientCapabilities, + string? clientName, + string methodName, + CancellationToken cancellationToken) where TRequestType : class { return queue.ExecuteAsync(mutatesSolutionState, requiresLSPSolution, handler, request, clientCapabilities, clientName, methodName, cancellationToken); } + public ImmutableArray GetRegisteredMethods() + { + return _requestHandlers.Keys.ToImmutableArray(); + } + internal TestAccessor GetTestAccessor() => new TestAccessor(this); @@ -105,7 +140,7 @@ public TestAccessor(RequestDispatcher requestDispatcher) => _requestDispatcher = requestDispatcher; public IRequestHandler GetHandler(string methodName) - => (IRequestHandler)_requestDispatcher._requestHandlers[methodName].Value; + => (IRequestHandler)_requestDispatcher._requestHandlers.Single(handler => handler.Key.MethodName == methodName).Value.Value; } } } diff --git a/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs b/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs index e4829d282094e..4896ff6a330fc 100644 --- a/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs +++ b/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs @@ -16,7 +16,7 @@ internal sealed class RequestDispatcherFactory : AbstractRequestDispatcherFactor { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RequestDispatcherFactory([ImportMany] IEnumerable> requestHandlerProviders) + public RequestDispatcherFactory([ImportMany(ProtocolConstants.RoslynLspLanguagesContract)] IEnumerable> requestHandlerProviders) : base(requestHandlerProviders) { } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs index 536bbdca169b8..773c05c3077c3 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionResolveTests.cs @@ -132,11 +132,10 @@ void M() private static async Task RunGetCodeActionResolveAsync( TestLspServer testLspServer, - VSInternalCodeAction unresolvedCodeAction, - LSP.ClientCapabilities clientCapabilities = null) + VSInternalCodeAction unresolvedCodeAction) { var result = (VSInternalCodeAction)await testLspServer.ExecuteRequestAsync( - LSP.Methods.CodeActionResolveName, unresolvedCodeAction, clientCapabilities, null, CancellationToken.None); + LSP.Methods.CodeActionResolveName, unresolvedCodeAction, CancellationToken.None); return result; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs index abe6e07cc3952..61b186b32e74a 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/CodeActionsTests.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Xunit; @@ -85,7 +86,7 @@ void M() var results = await RunGetCodeActionsAsync(testLspServer, caretLocation); var introduceConstant = results[0].Children.FirstOrDefault( - r => ((CodeActionResolveData)r.Data).UniqueIdentifier == FeaturesResources.Introduce_constant + r => ((JObject)r.Data).ToObject().UniqueIdentifier == FeaturesResources.Introduce_constant + '|' + string.Format(FeaturesResources.Introduce_constant_for_0, "1")); AssertJsonEquals(expected, introduceConstant); @@ -204,11 +205,10 @@ private static void AssertRangeAndDocEqual( private static async Task RunGetCodeActionsAsync( TestLspServer testLspServer, - LSP.Location caret, - LSP.ClientCapabilities clientCapabilities = null) + LSP.Location caret) { var result = await testLspServer.ExecuteRequestAsync( - LSP.Methods.TextDocumentCodeActionName, CreateCodeActionParams(caret), clientCapabilities, null, CancellationToken.None); + LSP.Methods.TextDocumentCodeActionName, CreateCodeActionParams(caret), CancellationToken.None); return result.Cast().ToArray(); } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs index d922c457d22df..dd6d69153b211 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs @@ -67,7 +67,7 @@ private static async Task ExecuteRunCodeActionCommandAsync( }; var result = await testLspServer.ExecuteRequestAsync( - LSP.Methods.WorkspaceExecuteCommandName, command, new LSP.ClientCapabilities(), null, CancellationToken.None); + LSP.Methods.WorkspaceExecuteCommandName, command, CancellationToken.None); Contract.ThrowIfNull(result); return (bool)result; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs index ca05efafd8712..915764c77bbf9 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionResolveTests.cs @@ -37,7 +37,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + var clientCapabilities = new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true, @@ -52,16 +52,17 @@ void M() } } }; + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); + var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, - label: "A", - clientCapabilities).ConfigureAwait(false); + label: "A").ConfigureAwait(false); var description = new ClassifiedTextElement(CreateClassifiedTextRunForClass("A")); var expected = CreateResolvedCompletionItem(clientCompletionItem, description, "class A", null); var results = (LSP.VSInternalCompletionItem)await RunResolveCompletionItemAsync( - testLspServer, clientCompletionItem, clientCapabilities).ConfigureAwait(false); + testLspServer, clientCompletionItem).ConfigureAwait(false); AssertJsonEquals(expected, results); } @@ -76,7 +77,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); var clientCompletionItem = await GetCompletionItemToResolveAsync(testLspServer, label: "A").ConfigureAwait(false); var description = new ClassifiedTextElement(CreateClassifiedTextRunForClass("A")); @@ -101,7 +102,7 @@ class B : A { override {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); var clientCompletionItem = await GetCompletionItemToResolveAsync(testLspServer, label: "M()").ConfigureAwait(false); var results = (LSP.VSInternalCompletionItem)await RunResolveCompletionItemAsync( testLspServer, clientCompletionItem).ConfigureAwait(false); @@ -128,7 +129,7 @@ class B : A { override {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + // Explicitly enable snippets. This allows us to set the cursor with $0. Currently only applies to C# in Razor docs. var clientCapabilities = new LSP.VSInternalClientCapabilities { @@ -144,13 +145,13 @@ class B : A } } }; + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, - label: "M()", - clientCapabilities).ConfigureAwait(false); + label: "M()").ConfigureAwait(false); var results = (LSP.VSInternalCompletionItem)await RunResolveCompletionItemAsync( - testLspServer, clientCompletionItem, clientCapabilities).ConfigureAwait(false); + testLspServer, clientCompletionItem).ConfigureAwait(false); Assert.NotNull(results.TextEdit); Assert.Null(results.InsertText); @@ -224,11 +225,23 @@ void M() AMet{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + var clientCapabilities = new ClientCapabilities + { + TextDocument = new TextDocumentClientCapabilities + { + Completion = new CompletionSetting + { + CompletionItem = new CompletionItemSetting + { + DocumentationFormat = new MarkupKind[] { MarkupKind.Markdown } + } + } + } + }; + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, - label: "AMethod", - new ClientCapabilities()).ConfigureAwait(false); + label: "AMethod").ConfigureAwait(false); Assert.True(clientCompletionItem is not VSInternalCompletionItem); var expected = @"```csharp @@ -247,20 +260,7 @@ void A.AMethod(int i) var results = await RunResolveCompletionItemAsync( testLspServer, - clientCompletionItem, - new ClientCapabilities - { - TextDocument = new TextDocumentClientCapabilities - { - Completion = new CompletionSetting - { - CompletionItem = new CompletionItemSetting - { - DocumentationFormat = new MarkupKind[] { MarkupKind.Markdown } - } - } - } - }).ConfigureAwait(false); + clientCompletionItem).ConfigureAwait(false); Assert.Equal(expected, results.Documentation.Value.Second.Value); } @@ -303,8 +303,7 @@ void M() using var testLspServer = await CreateTestLspServerAsync(markup); var clientCompletionItem = await GetCompletionItemToResolveAsync( testLspServer, - label: "AMethod", - new ClientCapabilities()).ConfigureAwait(false); + label: "AMethod").ConfigureAwait(false); Assert.True(clientCompletionItem is not VSInternalCompletionItem); var expected = @"void A.AMethod(int i) @@ -320,8 +319,7 @@ underline text var results = await RunResolveCompletionItemAsync( testLspServer, - clientCompletionItem, - new ClientCapabilities()).ConfigureAwait(false); + clientCompletionItem).ConfigureAwait(false); Assert.Equal(expected, results.Documentation.Value.Second.Value); } @@ -337,7 +335,7 @@ void M() a.{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }); var clientCompletionItem = await GetCompletionItemToResolveAsync(testLspServer, label: "(byte)").ConfigureAwait(false); var results = (LSP.VSInternalCompletionItem)await RunResolveCompletionItemAsync( @@ -346,11 +344,10 @@ void M() Assert.NotNull(results.Description); } - private static async Task RunResolveCompletionItemAsync(TestLspServer testLspServer, LSP.CompletionItem completionItem, LSP.ClientCapabilities clientCapabilities = null) + private static async Task RunResolveCompletionItemAsync(TestLspServer testLspServer, LSP.CompletionItem completionItem) { - clientCapabilities ??= new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }; return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionResolveName, - completionItem, clientCapabilities, null, CancellationToken.None); + completionItem, CancellationToken.None); } private static LSP.VSInternalCompletionItem CreateResolvedCompletionItem( @@ -376,23 +373,23 @@ private static LSP.VSInternalCompletionItem CreateResolvedCompletionItem( private static ClassifiedTextRun[] CreateClassifiedTextRunForClass(string className) => new ClassifiedTextRun[] { + new ClassifiedTextRun("whitespace", string.Empty), new ClassifiedTextRun("keyword", "class"), new ClassifiedTextRun("whitespace", " "), - new ClassifiedTextRun("class name", className) + new ClassifiedTextRun("class name", className), + new ClassifiedTextRun("whitespace", string.Empty), }; private static async Task GetCompletionItemToResolveAsync( TestLspServer testLspServer, - string label, - LSP.ClientCapabilities clientCapabilities = null) where T : LSP.CompletionItem + string label) where T : LSP.CompletionItem { var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), LSP.VSInternalCompletionInvokeKind.Explicit, "\0", LSP.CompletionTriggerKind.Invoked); - clientCapabilities ??= new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }; - var completionList = await RunGetCompletionsAsync(testLspServer, completionParams, clientCapabilities); + var completionList = await RunGetCompletionsAsync(testLspServer, completionParams); - if (clientCapabilities.HasCompletionListDataCapability()) + if (testLspServer.ClientCapabilities.HasCompletionListDataCapability()) { var vsCompletionList = Assert.IsAssignableFrom(completionList); Assert.NotNull(vsCompletionList.Data); @@ -405,14 +402,13 @@ private static async Task GetCompletionItemToResolveAsync( private static async Task RunGetCompletionsAsync( TestLspServer testLspServer, - LSP.CompletionParams completionParams, - LSP.ClientCapabilities clientCapabilities) + LSP.CompletionParams completionParams) { var completionList = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, - completionParams, clientCapabilities, null, CancellationToken.None); + completionParams, CancellationToken.None); // Emulate client behavior of promoting "Data" completion list properties onto completion items. - if (clientCapabilities.HasCompletionListDataCapability() && + if (testLspServer.ClientCapabilities.HasCompletionListDataCapability() && completionList is VSInternalCompletionList vsCompletionList && vsCompletionList.Data != null) { diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs index 67bcb053fd123..64acc0383b2d2 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Completion/CompletionTests.cs @@ -47,7 +47,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -57,13 +57,13 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: new string[] { "Class", "Internal" }, - request: completionParams, document: document, commitCharacters: CompletionRules.Default.DefaultCommitCharacters, insertText: "A").ConfigureAwait(false); + request: completionParams, document: document, commitCharacters: CompletionRules.Default.DefaultCommitCharacters).ConfigureAwait(false); var expectedCommitCharacters = expected.CommitCharacters; // Null out the commit characters since we're expecting the commit characters will be lifted onto the completion list. expected.CommitCharacters = null; - var results = await RunGetCompletionsAsync(testLspServer, completionParams, clientCapabilities).ConfigureAwait(false); + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); var vsCompletionList = Assert.IsAssignableFrom(results); Assert.Equal(expectedCommitCharacters, vsCompletionList.CommitCharacters.Value.First); @@ -90,7 +90,7 @@ public async Task TestGetCompletions_PromotesNothingWhenNoCommitCharactersAsync( @"namespace M {{|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -106,7 +106,7 @@ public async Task TestGetCompletions_PromotesNothingWhenNoCommitCharactersAsync( // Null out the commit characters since we're expecting the commit characters will be lifted onto the completion list. expected.CommitCharacters = null; - var results = await RunGetCompletionsAsync(testLspServer, completionParams, clientCapabilities).ConfigureAwait(false); + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); Assert.All(results.Items, item => Assert.Null(item.CommitCharacters)); var vsCompletionList = Assert.IsAssignableFrom(results); Assert.Equal(expectedCommitCharacters, vsCompletionList.CommitCharacters.Value.First); @@ -123,7 +123,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -133,7 +133,7 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: new string[] { "Class", "Internal" }, - request: completionParams, document: document, commitCharacters: null, insertText: "A").ConfigureAwait(false); + request: completionParams, document: document, commitCharacters: null).ConfigureAwait(false); var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); @@ -150,7 +150,7 @@ void M() A{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -160,7 +160,7 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var expected = await CreateCompletionItemAsync(label: "A", kind: LSP.CompletionItemKind.Class, tags: new string[] { "Class", "Internal" }, - request: completionParams, document: document, commitCharacters: null, insertText: "A").ConfigureAwait(false); + request: completionParams, document: document, commitCharacters: null).ConfigureAwait(false); var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); @@ -177,7 +177,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var solution = testLspServer.TestWorkspace.CurrentSolution; // Make sure the unimported types option is on by default. @@ -201,7 +201,8 @@ public async Task TestGetCompletionsUsesSnippetOptionAsync() { {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); testLspServer.TestWorkspace.GlobalOptions.SetGlobalOption( new OptionKey(CompletionOptionsStorage.SnippetsBehavior, LanguageNames.CSharp), SnippetsRule.NeverInclude); @@ -227,7 +228,7 @@ void M() A classA = new {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -237,8 +238,7 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var expected = await CreateCompletionItemAsync("A", LSP.CompletionItemKind.Class, new string[] { "Class", "Internal" }, - completionParams, document, preselect: true, commitCharacters: ImmutableArray.Create(' ', '(', '[', '{', ';', '.'), - insertText: "A").ConfigureAwait(false); + completionParams, document, preselect: true, commitCharacters: ImmutableArray.Create(' ', '(', '[', '{', ';', '.')).ConfigureAwait(false); var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); @@ -262,7 +262,7 @@ void M() } } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -286,7 +286,7 @@ void M() DateTime.Now.ToString(""{|caret:|}); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -296,7 +296,7 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var expected = await CreateCompletionItemAsync( - label: "d", kind: LSP.CompletionItemKind.Text, tags: new string[] { "Text" }, request: completionParams, document: document, insertText: "d", sortText: "0000").ConfigureAwait(false); + label: "d", kind: LSP.CompletionItemKind.Text, tags: new string[] { "Text" }, request: completionParams, document: document, sortText: "0000").ConfigureAwait(false); var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); AssertJsonEquals(expected, results.Items.First()); @@ -315,7 +315,7 @@ void M() new Regex(""{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -352,7 +352,7 @@ void M() new Regex(@""\{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -389,7 +389,7 @@ void M() Regex r = new(""\\{|caret:|}""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -425,7 +425,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var cache = GetCompletionListCache(testLspServer); Assert.NotNull(cache); @@ -488,7 +488,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Deletion, @@ -519,7 +519,7 @@ class B : A { override {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -546,7 +546,7 @@ partial class C { partial {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -581,7 +581,7 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - var results = await RunGetCompletionsAsync(testLspServer, completionParams, new LSP.VSInternalClientCapabilities()).ConfigureAwait(false); + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); Assert.NotNull(results); Assert.NotEmpty(results.Items); Assert.All(results.Items, (item) => Assert.NotNull(item.CommitCharacters)); @@ -608,10 +608,10 @@ void M() var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - var results = await RunGetCompletionsAsync(testLspServer, completionParams, new LSP.VSInternalClientCapabilities()).ConfigureAwait(false); + var results = await RunGetCompletionsAsync(testLspServer, completionParams).ConfigureAwait(false); Assert.NotNull(results); Assert.NotEmpty(results.Items); - Assert.All(results.Items, (item) => Assert.True(item.CommitCharacters.Length == 0)); + Assert.All(results.Items, (item) => Assert.Null(item.CommitCharacters)); } [Fact] @@ -651,7 +651,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Typing, @@ -703,7 +703,7 @@ void M() W someW = new {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); var completionParams = CreateCompletionParams( @@ -756,7 +756,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -822,7 +822,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -888,7 +888,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -983,7 +983,7 @@ void M2() Console.W{|secondCaret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var firstCaret = testLspServer.GetLocations("firstCaret").Single(); await testLspServer.OpenDocumentAsync(firstCaret.Uri); @@ -1047,7 +1047,7 @@ void M() Ta{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); var completionParams = CreateCompletionParams( @@ -1106,7 +1106,7 @@ void M2() {|secondCaret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var firstCaretLocation = testLspServer.GetLocations("firstCaret").Single(); await testLspServer.OpenDocumentAsync(firstCaretLocation.Uri); @@ -1178,7 +1178,7 @@ void M() {|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var completionParams = CreateCompletionParams( testLspServer.GetLocations("caret").Single(), invokeKind: LSP.VSInternalCompletionInvokeKind.Explicit, @@ -1232,7 +1232,7 @@ void M() T{|caret:|} } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var caretLocation = testLspServer.GetLocations("caret").Single(); await testLspServer.OpenDocumentAsync(caretLocation.Uri); @@ -1262,17 +1262,8 @@ void M() internal static Task RunGetCompletionsAsync(TestLspServer testLspServer, LSP.CompletionParams completionParams) { - var clientCapabilities = new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }; - return RunGetCompletionsAsync(testLspServer, completionParams, clientCapabilities); - } - - private static async Task RunGetCompletionsAsync( - TestLspServer testLspServer, - LSP.CompletionParams completionParams, - LSP.VSInternalClientCapabilities clientCapabilities) - { - return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, - completionParams, clientCapabilities, null, CancellationToken.None); + return testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentCompletionName, + completionParams, CancellationToken.None); } private static CompletionListCache GetCompletionListCache(TestLspServer testLspServer) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs index ee27ce34b44ef..5273013540e74 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs @@ -150,7 +150,7 @@ End Class private static async Task RunGotoDefinitionAsync(TestLspServer testLspServer, LSP.Location caret) { return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentDefinitionName, - CreateTextDocumentPositionParams(caret), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateTextDocumentPositionParams(caret), CancellationToken.None); } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs index b68d5c7646a87..9176786d78648 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Definitions/GoToTypeDefinitionTests.cs @@ -79,7 +79,7 @@ class B private static async Task RunGotoTypeDefinitionAsync(TestLspServer testLspServer, LSP.Location caret) { return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentTypeDefinitionName, - CreateTextDocumentPositionParams(caret), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateTextDocumentPositionParams(caret), CancellationToken.None); } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 3b61a7839ecb7..88714713717a8 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -37,7 +37,7 @@ public async Task TestNoDocumentDiagnosticsForClosedFilesWithFSAOff(bool useVSDi { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); @@ -51,7 +51,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesWithFSAOff(bool useVSDiagno { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -72,7 +72,7 @@ public async Task TestNoDocumentDiagnosticsForOpenFilesWithFSAOffIfInPushMode(bo { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, pullDiagnostics: false); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics, pullDiagnostics: false); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -91,7 +91,7 @@ public async Task TestNoDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOf { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Default); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Default, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -110,7 +110,7 @@ public async Task TestDocumentDiagnosticsForOpenFilesIfDefaultAndFeatureFlagOn(b { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Default); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Default, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -128,7 +128,7 @@ public async Task TestDocumentDiagnosticsForRemovedDocument(bool useVSDiagnostic { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var workspace = testLspServer.TestWorkspace; // Calling GetTextBuffer will effectively open the file. @@ -160,7 +160,7 @@ public async Task TestNoChangeIfDocumentDiagnosticsCalledTwice(bool useVSDiagnos { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -186,7 +186,7 @@ public async Task TestDocumentDiagnosticsRemovedAfterErrorIsFixed(bool useVSDiag { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -209,7 +209,7 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed(bool useVSDi { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. var buffer = testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -238,7 +238,7 @@ public async Task TestDocumentDiagnosticsAreNotMapped(bool useVSDiagnostics) var markup = @"#line 1 ""test.txt"" class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -275,7 +275,7 @@ public async Task TestStreamingDocumentDiagnostics(bool useVSDiagnostics) { var markup = @"class A {"; - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -307,7 +307,7 @@ class B {"; "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.OpenFiles); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -322,15 +322,23 @@ class B {"; testLspServer.TestWorkspace.SetDocumentContext(csproj2Document.Id); var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj2Document.GetURI(), useVSDiagnostics); Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); - var vsDiagnostic = (LSP.VSDiagnostic)results.Single().Diagnostics.Single(); - Assert.Equal("CSProj2", vsDiagnostic.Projects.Single().ProjectName); + if (useVSDiagnostics) + { + // Only VSDiagnostics will have the project. + var vsDiagnostic = (LSP.VSDiagnostic)results.Single().Diagnostics.Single(); + Assert.Equal("CSProj2", vsDiagnostic.Projects.Single().ProjectName); + } // Set CSProj1 as the active context and get diagnostics. testLspServer.TestWorkspace.SetDocumentContext(csproj1Document.Id); results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, csproj1Document.GetURI(), useVSDiagnostics); Assert.Equal(2, results.Single().Diagnostics!.Length); Assert.All(results.Single().Diagnostics, d => Assert.Equal("CS1513", d.Code)); - Assert.All(results.Single().Diagnostics, d => Assert.Equal("CSProj1", ((VSDiagnostic)d).Projects.Single().ProjectName)); + + if (useVSDiagnostics) + { + Assert.All(results.Single().Diagnostics, d => Assert.Equal("CSProj1", ((VSDiagnostic)d).Projects.Single().ProjectName)); + } } [Theory, CombinatorialData] @@ -358,7 +366,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -404,7 +412,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj1Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj1").Single().Documents.First(); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); @@ -434,7 +442,7 @@ public async Task TestDocumentDiagnosticsFromRazorServer(bool useVSDiagnostics) @"class A {"; // Turn off pull diagnostics by default, but send a request to the razor LSP server which is always pull. - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Push, serverKind: WellKnownLspServerKinds.RazorLspServer); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Push, useVSDiagnostics, serverKind: WellKnownLspServerKinds.RazorLspServer); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -458,7 +466,7 @@ public async Task TestDocumentDiagnosticsFromLiveShareServer(bool useVSDiagnosti @"class A {"; // Turn off pull diagnostics by default, but send a request to the razor LSP server which is always pull. - using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Push, serverKind: WellKnownLspServerKinds.LiveShareLspServer); + using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync(markup, BackgroundAnalysisScope.OpenFiles, DiagnosticMode.Push, useVSDiagnostics, serverKind: WellKnownLspServerKinds.LiveShareLspServer); // Calling GetTextBuffer will effectively open the file. testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); @@ -486,7 +494,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOff(bool useVSD @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.OpenFiles); + new[] { markup1, markup2 }, BackgroundAnalysisScope.OpenFiles, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -500,7 +508,7 @@ public async Task TestWorkspaceDiagnosticsForClosedFilesWithFSAOn(bool useVSDiag @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -516,7 +524,7 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesWithFSAOnAndInPushMode @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, pullDiagnostics: false); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics, pullDiagnostics: false); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -542,11 +550,11 @@ public async Task TestNoWorkspaceDiagnosticsForClosedFilesInProjectsWithIncorrec "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); - Assert.True(results.All(r => r.TextDocument!.Uri.OriginalString == "C:\\C.cs")); + Assert.True(results.All(r => r.TextDocument!.Uri.LocalPath == "C:\\C.cs")); } [Theory, CombinatorialData] @@ -556,7 +564,7 @@ public async Task TestWorkspaceDiagnosticsForRemovedDocument(bool useVSDiagnosti @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -590,7 +598,7 @@ public async Task TestNoChangeIfWorkspaceDiagnosticsCalledTwice(bool useVSDiagno @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -615,7 +623,7 @@ public async Task TestWorkspaceDiagnosticsRemovedAfterErrorIsFixed(bool useVSDia @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -645,7 +653,7 @@ public async Task TestWorkspaceDiagnosticsRemainAfterErrorIsNotFixed(bool useVSD @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -682,7 +690,7 @@ public async Task TestStreamingWorkspaceDiagnostics(bool useVSDiagnostics) @"class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); @@ -703,7 +711,7 @@ public async Task TestWorkspaceDiagnosticsAreNotMapped(bool useVSDiagnostics) class A {"; var markup2 = ""; using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( - new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics); Assert.Equal(2, results.Length); @@ -738,7 +746,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -810,7 +818,7 @@ public class {|caret:|} "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj3Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj3").Single().Documents.First(); // Verify we have a diagnostic in C.cs initially. @@ -869,7 +877,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -926,7 +934,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -981,7 +989,7 @@ public class {|caret:|} { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -1037,7 +1045,7 @@ class A : B { } "; - using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution).ConfigureAwait(false); + using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, BackgroundAnalysisScope.FullSolution, useVSDiagnostics).ConfigureAwait(false); var csproj2Document = testLspServer.GetCurrentSolution().Projects.Where(p => p.Name == "CSProj2").Single().Documents.First(); // Verify we a diagnostic in A.cs since B does not exist @@ -1094,8 +1102,6 @@ private static async Task> RunGetDocumentPu var diagnostics = await testLspServer.ExecuteRequestAsync( VSInternalMethods.DocumentPullDiagnosticName, CreateDocumentDiagnosticParams(uri, previousResultId, progress), - new LSP.VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true }, - clientName: null, CancellationToken.None).ConfigureAwait(false); if (useProgress) @@ -1113,8 +1119,6 @@ private static async Task> RunGetDocumentPu var diagnostics = await testLspServer.ExecuteRequestAsync?>( ExperimentalMethods.TextDocumentDiagnostic, CreateProposedDocumentDiagnosticParams(uri, previousResultId, progress), - new LSP.VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true }, - clientName: null, CancellationToken.None).ConfigureAwait(false); if (useProgress) { @@ -1161,8 +1165,6 @@ private static async Task> RunGetWorkspaceP var diagnostics = await testLspServer.ExecuteRequestAsync( VSInternalMethods.WorkspacePullDiagnosticName, CreateWorkspaceDiagnosticParams(previousResults, progress), - new LSP.ClientCapabilities(), - clientName: null, CancellationToken.None).ConfigureAwait(false); if (useProgress) @@ -1180,8 +1182,6 @@ private static async Task> RunGetWorkspaceP var returnedResult = await testLspServer.ExecuteRequestAsync( ExperimentalMethods.WorkspaceDiagnostic, CreateProposedWorkspaceDiagnosticParams(previousResults, progress), - new LSP.VSInternalClientCapabilities() { SupportsVisualStudioExtensions = true }, - clientName: null, CancellationToken.None).ConfigureAwait(false); if (useProgress) @@ -1252,26 +1252,26 @@ private static VSInternalWorkspaceDiagnosticsParams CreateWorkspaceDiagnosticPar }; } - private Task CreateTestWorkspaceWithDiagnosticsAsync(string markup, BackgroundAnalysisScope scope, bool pullDiagnostics = true) - => CreateTestWorkspaceWithDiagnosticsAsync(markup, scope, pullDiagnostics ? DiagnosticMode.Pull : DiagnosticMode.Push); + private Task CreateTestWorkspaceWithDiagnosticsAsync(string markup, BackgroundAnalysisScope scope, bool useVSDiagnostics, bool pullDiagnostics = true) + => CreateTestWorkspaceWithDiagnosticsAsync(markup, scope, pullDiagnostics ? DiagnosticMode.Pull : DiagnosticMode.Push, useVSDiagnostics); - private async Task CreateTestWorkspaceWithDiagnosticsAsync(string markup, BackgroundAnalysisScope scope, DiagnosticMode mode, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) + private async Task CreateTestWorkspaceWithDiagnosticsAsync(string markup, BackgroundAnalysisScope scope, DiagnosticMode mode, bool useVSDiagnostics, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer) { - var testLspServer = await CreateTestLspServerAsync(markup, serverKind); + var testLspServer = await CreateTestLspServerAsync(markup, useVSDiagnostics ? CapabilitiesWithVSExtensions : new LSP.ClientCapabilities(), serverKind); InitializeDiagnostics(scope, testLspServer.TestWorkspace, mode); return testLspServer; } - private async Task CreateTestWorkspaceFromXmlAsync(string xmlMarkup, BackgroundAnalysisScope scope, bool pullDiagnostics = true) + private async Task CreateTestWorkspaceFromXmlAsync(string xmlMarkup, BackgroundAnalysisScope scope, bool useVSDiagnostics, bool pullDiagnostics = true) { - var testLspServer = await CreateXmlTestLspServerAsync(xmlMarkup); + var testLspServer = await CreateXmlTestLspServerAsync(xmlMarkup, clientCapabilities: useVSDiagnostics ? CapabilitiesWithVSExtensions : new LSP.ClientCapabilities()); InitializeDiagnostics(scope, testLspServer.TestWorkspace, pullDiagnostics ? DiagnosticMode.Pull : DiagnosticMode.Push); return testLspServer; } - private async Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, BackgroundAnalysisScope scope, bool pullDiagnostics = true) + private async Task CreateTestWorkspaceWithDiagnosticsAsync(string[] markups, BackgroundAnalysisScope scope, bool useVSDiagnostics, bool pullDiagnostics = true) { - var testLspServer = await CreateTestLspServerAsync(markups); + var testLspServer = await CreateTestLspServerAsync(markups, useVSDiagnostics ? CapabilitiesWithVSExtensions : new LSP.ClientCapabilities()); InitializeDiagnostics(scope, testLspServer.TestWorkspace, pullDiagnostics ? DiagnosticMode.Pull : DiagnosticMode.Push); return testLspServer; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs index f6ba6080e5dca..ca9b74b622a83 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.LinkedDocuments.cs @@ -3,14 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; -using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Utilities; using Xunit; @@ -19,9 +14,6 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.DocumentChanges { public partial class DocumentChangesTests { - protected override TestComposition Composition => base.Composition - .AddParts(typeof(GetLspSolutionHandlerProvider)); - [Fact] public async Task LinkedDocuments_AllTracked() { @@ -44,7 +36,7 @@ public async Task LinkedDocuments_AllTracked() var trackedDocuments = testLspServer.GetQueueAccessor().GetTrackedTexts(); Assert.Equal(1, trackedDocuments.Length); - var solution = await GetLSPSolution(testLspServer, caretLocation.Uri); + var solution = GetLSPSolution(testLspServer, caretLocation.Uri); foreach (var document in solution.Projects.First().Documents) { @@ -95,7 +87,7 @@ void M() await DidChange(testLspServer, caretLocation.Uri, (4, 8, "// hi there")); - var solution = await GetLSPSolution(testLspServer, caretLocation.Uri); + var solution = GetLSPSolution(testLspServer, caretLocation.Uri); foreach (var document in solution.Projects.First().Documents) { @@ -107,40 +99,11 @@ void M() Assert.Empty(testLspServer.GetQueueAccessor().GetTrackedTexts()); } - private static async Task GetLSPSolution(TestLspServer testLspServer, Uri uri) - { - var result = await testLspServer.ExecuteRequestAsync(nameof(GetLSPSolutionHandler), uri, new ClientCapabilities(), null, CancellationToken.None); - Contract.ThrowIfNull(result); - return result; - } - - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(GetLSPSolutionHandler.MethodName)] - private class GetLspSolutionHandlerProvider : AbstractRequestHandlerProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public GetLspSolutionHandlerProvider() - { - } - - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) => ImmutableArray.Create(new GetLSPSolutionHandler()); - } - - private class GetLSPSolutionHandler : IRequestHandler + private static Solution GetLSPSolution(TestLspServer testLspServer, Uri uri) { - public const string MethodName = nameof(GetLSPSolutionHandler); - - public string Method => MethodName; - - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => true; - - public TextDocumentIdentifier? GetTextDocumentIdentifier(Uri request) - => new TextDocumentIdentifier { Uri = request }; - - public Task HandleRequestAsync(Uri request, RequestContext context, CancellationToken cancellationToken) - => Task.FromResult(context.Solution!); + var lspDocument = testLspServer.GetManager().GetLspDocument(new TextDocumentIdentifier { Uri = uri }, null); + Contract.ThrowIfNull(lspDocument); + return lspDocument.Project.Solution; } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs index df2c3eb44f08d..6f7945652507d 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.cs @@ -102,7 +102,7 @@ void M() { await DidOpen(testLspServer, locationTyped.Uri); - await Assert.ThrowsAsync(() => DidOpen(testLspServer, locationTyped.Uri)); + await Assert.ThrowsAsync(() => DidOpen(testLspServer, locationTyped.Uri)); } } @@ -121,7 +121,7 @@ void M() using (testLspServer) { - await Assert.ThrowsAsync(() => DidClose(testLspServer, locationTyped.Uri)); + await Assert.ThrowsAsync(() => DidClose(testLspServer, locationTyped.Uri)); } } @@ -140,7 +140,7 @@ void M() using (testLspServer) { - await Assert.ThrowsAsync(() => DidChange(testLspServer, locationTyped.Uri, (0, 0, "goo"))); + await Assert.ThrowsAsync(() => DidChange(testLspServer, locationTyped.Uri, (0, 0, "goo"))); } } @@ -317,7 +317,7 @@ void M() private async Task<(TestLspServer, LSP.Location, string)> GetTestLspServerAndLocationAsync(string source) { - var testLspServer = await CreateTestLspServerAsync(source); + var testLspServer = await CreateTestLspServerAsync(source, CapabilitiesWithVSExtensions); var locationTyped = testLspServer.GetLocations("type").Single(); var documentText = await testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single().GetTextAsync(); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs index 321ed11e85d18..8f133cd53ea5f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs @@ -72,7 +72,7 @@ public async Task TestGetFoldingRangeAsync_Regions() }; return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentFoldingRangeName, - request, new LSP.ClientCapabilities(), null, CancellationToken.None); + request, CancellationToken.None); } private static LSP.FoldingRange CreateFoldingRange(LSP.FoldingRangeKind kind, LSP.Range range) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs index 1702121bb6618..20f9e9461f55f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentOnTypeTests.cs @@ -86,7 +86,7 @@ void M() { return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentOnTypeFormattingName, CreateDocumentOnTypeFormattingParams( - characterTyped, locationTyped, insertSpaces, tabSize), new LSP.ClientCapabilities(), null, CancellationToken.None); + characterTyped, locationTyped, insertSpaces, tabSize), CancellationToken.None); } private static LSP.DocumentOnTypeFormattingParams CreateDocumentOnTypeFormattingParams( diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs index 8480e9d27cdc1..514f4504ee7ff 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentRangeTests.cs @@ -80,8 +80,6 @@ void M() return await testLspServer.ExecuteRequestAsync( LSP.Methods.TextDocumentRangeFormattingName, CreateDocumentRangeFormattingParams(location, insertSpaces, tabSize), - new LSP.ClientCapabilities(), - clientName: null, CancellationToken.None); } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs index 275cbf026edbb..1f41b900316c2 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Formatting/FormatDocumentTests.cs @@ -107,7 +107,7 @@ void M() int tabSize = 4) { return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentFormattingName, - CreateDocumentFormattingParams(uri, insertSpaces, tabSize), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateDocumentFormattingParams(uri, insertSpaces, tabSize), CancellationToken.None); } private static LSP.DocumentFormattingParams CreateDocumentFormattingParams(Uri uri, bool insertSpaces, int tabSize) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs index e0fe7ff9c44a7..fbb668f7aa5c7 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Highlights/DocumentHighlightTests.cs @@ -91,7 +91,7 @@ void M() private static async Task RunGetDocumentHighlightAsync(TestLspServer testLspServer, LSP.Location caret) { var results = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentDocumentHighlightName, - CreateTextDocumentPositionParams(caret), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateTextDocumentPositionParams(caret), CancellationToken.None); Array.Sort(results, (h1, h2) => { var compareKind = h1.Kind.CompareTo(h2.Kind); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs index b37d23ebc8661..c2fa4a2157de5 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Hover/HoverTests.cs @@ -32,7 +32,7 @@ public async Task TestGetHoverAsync() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -56,7 +56,7 @@ public async Task TestGetHoverAsync_WithExceptions() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -79,7 +79,7 @@ public async Task TestGetHoverAsync_WithRemarks() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -107,7 +107,7 @@ public async Task TestGetHoverAsync_WithList() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var expectedLocation = testLspServer.GetLocations("caret").Single(); var results = await RunGetHoverAsync(testLspServer, expectedLocation).ConfigureAwait(false); @@ -179,7 +179,7 @@ static void Main(string[] args) "; - using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml); + using var testLspServer = await CreateXmlTestLspServerAsync(workspaceXml, clientCapabilities: CapabilitiesWithVSExtensions); var location = testLspServer.GetLocations("caret").Single(); foreach (var project in testLspServer.GetCurrentSolution().Projects) @@ -229,7 +229,11 @@ public async Task TestGetHoverAsync_UsingMarkupContent() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + var clientCapabilities = new LSP.ClientCapabilities + { + TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = new LSP.MarkupKind[] { LSP.MarkupKind.Markdown } } } + }; + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var expectedLocation = testLspServer.GetLocations("caret").Single(); var expectedMarkdown = @$"```csharp @@ -257,11 +261,7 @@ [link text](https://google.com) var results = await RunGetHoverAsync( testLspServer, - expectedLocation, - clientCapabilities: new LSP.ClientCapabilities - { - TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = new LSP.MarkupKind[] { LSP.MarkupKind.Markdown } } } - }).ConfigureAwait(false); + expectedLocation).ConfigureAwait(false); Assert.Equal(expectedMarkdown, results.Contents.Third.Value); } @@ -328,8 +328,7 @@ a string var results = await RunGetHoverAsync( testLspServer, - expectedLocation, - clientCapabilities: new LSP.ClientCapabilities()).ConfigureAwait(false); + expectedLocation).ConfigureAwait(false); Assert.Equal(expectedText, results.Contents.Third.Value); } @@ -360,7 +359,11 @@ public async Task TestGetHoverAsync_UsingMarkupContentProperlyEscapes() { } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + var clientCapabilities = new LSP.ClientCapabilities + { + TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = new LSP.MarkupKind[] { LSP.MarkupKind.Markdown } } } + }; + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var expectedLocation = testLspServer.GetLocations("caret").Single(); var expectedMarkdown = @"```csharp @@ -379,23 +382,17 @@ void A.AMethod(int i) var results = await RunGetHoverAsync( testLspServer, - expectedLocation, - clientCapabilities: new LSP.ClientCapabilities - { - TextDocument = new LSP.TextDocumentClientCapabilities { Hover = new LSP.HoverSetting { ContentFormat = new LSP.MarkupKind[] { LSP.MarkupKind.Markdown } } } - }).ConfigureAwait(false); + expectedLocation).ConfigureAwait(false); Assert.Equal(expectedMarkdown, results.Contents.Third.Value); } private static async Task RunGetHoverAsync( TestLspServer testLspServer, LSP.Location caret, - ProjectId projectContext = null, - LSP.ClientCapabilities clientCapabilities = null) + ProjectId projectContext = null) { - clientCapabilities ??= new LSP.VSInternalClientCapabilities { SupportsVisualStudioExtensions = true }; return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentHoverName, - CreateTextDocumentPositionParams(caret, projectContext), clientCapabilities, null, CancellationToken.None); + CreateTextDocumentPositionParams(caret, projectContext), CancellationToken.None); } private void VerifyVSContent(LSP.Hover hover, string expectedContent) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs index 3e4fb32e12d21..d643c86e39c7c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs @@ -261,8 +261,8 @@ private async Task VerifyMarkupAndExpected(string markup, string expected, LSP.F Options = options }; - var response = await testLspServer.ExecuteRequestAsync(LSP.VSInternalMethods.TextDocumentInlineCompletionName, - request, new LSP.ClientCapabilities(), null, CancellationToken.None); + var response = await testLspServer.ExecuteRequestAsync( + LSP.VSInternalMethods.TextDocumentInlineCompletionName, request, CancellationToken.None); Contract.ThrowIfNull(response); return response; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs index 9556f4a82ad74..6e1cdf2e47be1 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/LanguageServerTargetTests.cs @@ -2,21 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.LanguageServer.Protocol; -using Nerdbank.Streams; using Roslyn.Test.Utilities; -using StreamJsonRpc; using Xunit; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests @@ -27,81 +15,48 @@ public class LanguageServerTargetTests : AbstractLanguageServerProtocolTests [Fact] public async Task LanguageServerQueueEmptyOnShutdownMessage() { - await using var languageServerTarget = CreateLanguageServer(out var jsonRpc); - AssertServerAlive(languageServerTarget); + var server = await CreateTestLspServerAsync(""); + AssertServerAlive(server); - await languageServerTarget.ShutdownAsync(CancellationToken.None).ConfigureAwait(false); - await AssertServerQueueClosed(languageServerTarget).ConfigureAwait(false); - Assert.False(jsonRpc.IsDisposed); + server.GetServerAccessor().ShutdownServer(); + await AssertServerQueueClosed(server).ConfigureAwait(false); + Assert.False(server.GetServerAccessor().GetServerRpc().IsDisposed); } [Fact] public async Task LanguageServerCleansUpOnExitMessage() { - await using var languageServerTarget = CreateLanguageServer(out var jsonRpc); - AssertServerAlive(languageServerTarget); + var server = await CreateTestLspServerAsync(""); + AssertServerAlive(server); - await languageServerTarget.ShutdownAsync(CancellationToken.None).ConfigureAwait(false); - await languageServerTarget.ExitAsync(CancellationToken.None).ConfigureAwait(false); - await AssertServerQueueClosed(languageServerTarget).ConfigureAwait(false); - Assert.True(jsonRpc.IsDisposed); + server.GetServerAccessor().ShutdownServer(); + server.GetServerAccessor().ExitServer(); + await AssertServerQueueClosed(server).ConfigureAwait(false); + Assert.True(server.GetServerAccessor().GetServerRpc().IsDisposed); } [Fact] public async Task LanguageServerCleansUpOnUnexpectedJsonRpcDisconnectAsync() { - await using var languageServerTarget = CreateLanguageServer(out var jsonRpc); - AssertServerAlive(languageServerTarget); + using var server = await CreateTestLspServerAsync(""); + AssertServerAlive(server); - jsonRpc.Dispose(); - await AssertServerQueueClosed(languageServerTarget).ConfigureAwait(false); - Assert.True(jsonRpc.IsDisposed); + server.GetServerAccessor().GetServerRpc().Dispose(); + await AssertServerQueueClosed(server).ConfigureAwait(false); + Assert.True(server.GetServerAccessor().GetServerRpc().IsDisposed); } - private static void AssertServerAlive(LanguageServerTarget server) + private static void AssertServerAlive(TestLspServer server) { - Assert.False(server.HasShutdownStarted); - Assert.False(server.GetTestAccessor().GetQueueAccessor().IsComplete()); + Assert.False(server.GetServerAccessor().HasShutdownStarted()); + Assert.False(server.GetQueueAccessor().IsComplete()); } - private static async Task AssertServerQueueClosed(LanguageServerTarget server) + private static async Task AssertServerQueueClosed(TestLspServer server) { - await server.GetTestAccessor().GetQueueAccessor().WaitForProcessingToStopAsync().ConfigureAwait(false); - Assert.True(server.HasShutdownStarted); - Assert.True(server.GetTestAccessor().GetQueueAccessor().IsComplete()); - } - - private LanguageServerTarget CreateLanguageServer(out JsonRpc serverJsonRpc) - { - using var workspace = TestWorkspace.CreateCSharp("", composition: Composition); - - var (_, serverStream) = FullDuplexStream.CreatePair(); - var dispatcherFactory = workspace.GetService(); - var lspWorkspaceRegistrationService = workspace.GetService(); - var capabilitiesProvider = workspace.GetService(); - var globalOptions = workspace.GetService(); - var listenerProvider = workspace.GetService(); - - serverJsonRpc = new JsonRpc(new HeaderDelimitedMessageHandler(serverStream, serverStream)) - { - ExceptionStrategy = ExceptionProcessing.ISerializable, - }; - - var languageServer = new LanguageServerTarget( - dispatcherFactory, - serverJsonRpc, - capabilitiesProvider, - lspWorkspaceRegistrationService, - new LspMiscellaneousFilesWorkspace(NoOpLspLogger.Instance), - globalOptions, - listenerProvider, - NoOpLspLogger.Instance, - ProtocolConstants.RoslynLspLanguages, - clientName: null, - WellKnownLspServerKinds.AlwaysActiveVSLspServer); - - serverJsonRpc.StartListening(); - return languageServer; + await server.GetQueueAccessor().WaitForProcessingToStopAsync().ConfigureAwait(false); + Assert.True(server.GetServerAccessor().HasShutdownStarted()); + Assert.True(server.GetQueueAccessor().IsComplete()); } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs index f1fbc5e02a56c..9eb067f713948 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs @@ -189,7 +189,7 @@ void M() private static async Task RunGetHoverAsync(TestLspServer testLspServer, LSP.Location caret) { var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentHoverName, - CreateTextDocumentPositionParams(caret), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateTextDocumentPositionParams(caret), CancellationToken.None); Contract.ThrowIfNull(result); return result; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs index 84d70dedba389..b1e29203a2d43 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs @@ -339,7 +339,7 @@ private async Task VerifyNoResult(string characterTyped, string markup, bool ins int tabSize) { return await testLspServer.ExecuteRequestAsync(VSInternalMethods.OnAutoInsertName, - CreateDocumentOnAutoInsertParams(characterTyped, locationTyped, insertSpaces, tabSize), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateDocumentOnAutoInsertParams(characterTyped, locationTyped, insertSpaces, tabSize), CancellationToken.None); } private static LSP.VSInternalDocumentOnAutoInsertParams CreateDocumentOnAutoInsertParams( diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs index 95ffc9ac764e9..031c3fb6c644e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingMutatingRequestHandler.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. #nullable disable + using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; @@ -13,35 +13,25 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(FailingMutatingRequestHandler.MethodName)] - internal class FailingMutatingRequestHandlerProvider : AbstractRequestHandlerProvider + [Shared, ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FailingMutatingRequestHandler)), PartNotDiscoverable] + [Method(MethodName)] + internal class FailingMutatingRequestHandler : AbstractStatelessRequestHandler { + public const string MethodName = nameof(FailingMutatingRequestHandler); + private const int Delay = 100; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FailingMutatingRequestHandlerProvider() - { - } - - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) + public FailingMutatingRequestHandler() { - return ImmutableArray.Create(new FailingMutatingRequestHandler()); } - } - - internal class FailingMutatingRequestHandler : IRequestHandler - { - public const string MethodName = nameof(FailingMutatingRequestHandler); - private const int Delay = 100; - - public string Method => MethodName; - public bool MutatesSolutionState => true; - public bool RequiresLSPSolution => true; + public override bool MutatesSolutionState => true; + public override bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; + public override TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) + public override async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { await Task.Delay(Delay, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs index b655d3aa53504..ca787d4129f3e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/FailingRequestHandler.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; @@ -15,35 +14,25 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(FailingRequestHandler.MethodName)] - internal class FailingRequestHandlerProvider : AbstractRequestHandlerProvider + [Shared, ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FailingRequestHandler)), PartNotDiscoverable] + [Method(MethodName)] + internal class FailingRequestHandler : AbstractStatelessRequestHandler { + public const string MethodName = nameof(FailingRequestHandler); + private const int Delay = 100; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FailingRequestHandlerProvider() - { - } - - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) + public FailingRequestHandler() { - return ImmutableArray.Create(new FailingRequestHandler()); } - } - - internal class FailingRequestHandler : IRequestHandler - { - public const string MethodName = nameof(FailingRequestHandler); - private const int Delay = 100; - - public string Method => MethodName; - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => true; + public override bool MutatesSolutionState => false; + public override bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; + public override TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) + public override async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { await Task.Delay(Delay, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs index 112aecd50dee0..5853e12dab00d 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/LongRunningNonMutatingRequestHandler.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; @@ -16,32 +15,25 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(LongRunningNonMutatingRequestHandler.MethodName)] - internal class LongRunningNonMutatingRequestHandlerProvider : AbstractRequestHandlerProvider + [Shared, ExportRoslynLanguagesLspRequestHandlerProvider(typeof(LongRunningNonMutatingRequestHandler)), PartNotDiscoverable] + [Method(MethodName)] + internal class LongRunningNonMutatingRequestHandler : AbstractStatelessRequestHandler { + public const string MethodName = nameof(LongRunningNonMutatingRequestHandler); + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public LongRunningNonMutatingRequestHandlerProvider() + public LongRunningNonMutatingRequestHandler() { } - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) => ImmutableArray.Create(new LongRunningNonMutatingRequestHandler()); - } - - internal class LongRunningNonMutatingRequestHandler : IRequestHandler - { - public const string MethodName = nameof(LongRunningNonMutatingRequestHandler); - - public string Method => MethodName; - - public bool MutatesSolutionState => false; + public override bool MutatesSolutionState => false; - public bool RequiresLSPSolution => true; + public override bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; + public override TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) + public override Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { do { diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs index f2dfeed623a38..f1a9f84b1ae17 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/MutatingRequestHandler.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; @@ -14,39 +13,28 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(MutatingRequestHandler.MethodName)] - internal class MutatingRequestHandlerProvider : AbstractRequestHandlerProvider + [Shared, ExportRoslynLanguagesLspRequestHandlerProvider(typeof(MutatingRequestHandler)), PartNotDiscoverable] + [Method(MethodName)] + internal class MutatingRequestHandler : AbstractStatelessRequestHandler { + public const string MethodName = nameof(MutatingRequestHandler); + private const int Delay = 100; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public MutatingRequestHandlerProvider() - { - } - - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) + public MutatingRequestHandler() { - return ImmutableArray.Create(new MutatingRequestHandler()); } - } - - internal class MutatingRequestHandler : IRequestHandler - { - public const string MethodName = nameof(MutatingRequestHandler); - private const int Delay = 100; - - public string Method => MethodName; - public bool MutatesSolutionState => true; - public bool RequiresLSPSolution => true; + public override bool MutatesSolutionState => true; + public override bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; + public override TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) + public override async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { var response = new TestResponse { - Solution = context.Solution, StartTime = DateTime.UtcNow }; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs index 74b4e67e6b70e..b9cdc5b5cfc3f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonLSPSolutionRequestHandlerProvider.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; @@ -16,34 +15,24 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(NonLSPSolutionRequestHandler.MethodName)] - internal class NonLSPSolutionRequestHandlerProvider : AbstractRequestHandlerProvider + [Shared, ExportRoslynLanguagesLspRequestHandlerProvider(typeof(NonLSPSolutionRequestHandler)), PartNotDiscoverable] + [Method(MethodName)] + internal class NonLSPSolutionRequestHandler : AbstractStatelessRequestHandler { + public const string MethodName = nameof(NonLSPSolutionRequestHandler); + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public NonLSPSolutionRequestHandlerProvider() - { - } - - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) + public NonLSPSolutionRequestHandler() { - return ImmutableArray.Create(new NonLSPSolutionRequestHandler()); } - } - - internal class NonLSPSolutionRequestHandler : IRequestHandler - { - public const string MethodName = nameof(NonLSPSolutionRequestHandler); - - public string Method => MethodName; - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => false; + public override bool MutatesSolutionState => false; + public override bool RequiresLSPSolution => false; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; + public override TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) + public override Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { Assert.Null(context.Solution); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs index 03bedb57afc54..4eadbfd198671 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/NonMutatingRequestHandler.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; @@ -14,39 +13,29 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering { - [Shared, ExportRoslynLanguagesLspRequestHandlerProvider, PartNotDiscoverable] - [ProvidesMethod(NonMutatingRequestHandler.MethodName)] - internal class NonMutatingRequestHandlerProvider : AbstractRequestHandlerProvider + [Shared, ExportRoslynLanguagesLspRequestHandlerProvider(typeof(NonMutatingRequestHandler)), PartNotDiscoverable] + [Method(MethodName)] + internal class NonMutatingRequestHandler : AbstractStatelessRequestHandler { + public const string MethodName = nameof(NonMutatingRequestHandler); + private const int Delay = 100; + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public NonMutatingRequestHandlerProvider() - { - } - - public override ImmutableArray CreateRequestHandlers(WellKnownLspServerKinds serverKind) + public NonMutatingRequestHandler() { - return ImmutableArray.Create(new NonMutatingRequestHandler()); } - } - - internal class NonMutatingRequestHandler : IRequestHandler - { - public const string MethodName = nameof(NonMutatingRequestHandler); - private const int Delay = 100; - - public string Method => nameof(NonMutatingRequestHandler); - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => true; + public override bool MutatesSolutionState => false; + public override bool RequiresLSPSolution => true; - public TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; + public override TextDocumentIdentifier GetTextDocumentIdentifier(TestRequest request) => null; - public async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) + public override async Task HandleRequestAsync(TestRequest request, RequestContext context, CancellationToken cancellationToken) { var response = new TestResponse(); - response.Solution = context.Solution; + response.ContextHasSolution = context.Solution != null; response.StartTime = DateTime.UtcNow; await Task.Delay(Delay, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs index 30e66d9b0eddf..d62499f04d08a 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/RequestOrderingTests.cs @@ -20,12 +20,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.RequestOrdering public partial class RequestOrderingTests : AbstractLanguageServerProtocolTests { protected override TestComposition Composition => base.Composition - .AddParts(typeof(MutatingRequestHandlerProvider)) - .AddParts(typeof(NonMutatingRequestHandlerProvider)) - .AddParts(typeof(FailingRequestHandlerProvider)) - .AddParts(typeof(FailingMutatingRequestHandlerProvider)) - .AddParts(typeof(NonLSPSolutionRequestHandlerProvider)) - .AddParts(typeof(LongRunningNonMutatingRequestHandlerProvider)); + .AddParts(typeof(MutatingRequestHandler)) + .AddParts(typeof(NonMutatingRequestHandler)) + .AddParts(typeof(FailingRequestHandler)) + .AddParts(typeof(FailingMutatingRequestHandler)) + .AddParts(typeof(NonLSPSolutionRequestHandler)) + .AddParts(typeof(LongRunningNonMutatingRequestHandler)); [Fact] public async Task MutatingRequestsDontOverlap() @@ -114,7 +114,7 @@ public async Task ThrowingTaskDoesntBringDownQueue() var waitables = StartTestRun(testLspServer, requests); // first task should fail - await Assert.ThrowsAsync(() => waitables[0]); + await Assert.ThrowsAsync(() => waitables[0]); // remaining tasks should have executed normally var responses = await Task.WhenAll(waitables.Skip(1)); @@ -169,7 +169,7 @@ public async Task FailingMutableTaskShutsDownQueue() var waitables = StartTestRun(testLspServer, requests); // first task should fail - await Assert.ThrowsAsync(() => waitables[0]); + await Assert.ThrowsAsync(() => waitables[0]); // The failed request returns to the client before the shutdown completes. // Wait for the queue to finish handling the failed request and shutdown. @@ -246,15 +246,22 @@ private static async Task ExecuteDidOpen(TestLspServer testLspServer, Uri docume Text = "// hi there" } }; - await testLspServer.ExecuteRequestAsync(Methods.TextDocumentDidOpenName, didOpenParams, new LSP.ClientCapabilities(), null, CancellationToken.None); + await testLspServer.ExecuteRequestAsync(Methods.TextDocumentDidOpenName, didOpenParams, CancellationToken.None); } - private static async Task GetLSPSolution(TestLspServer testLspServer, string methodName) + private static async Task GetLSPSolution(TestLspServer testLspServer, string methodName) { var request = new TestRequest(methodName); - var response = await testLspServer.ExecuteRequestAsync(request.MethodName, request, new LSP.ClientCapabilities(), null, CancellationToken.None); + var response = await testLspServer.ExecuteRequestAsync(request.MethodName, request, CancellationToken.None); Contract.ThrowIfNull(response); - return response.Solution; + if (response.ContextHasSolution) + { + var solution = testLspServer.GetManager().TryGetHostLspSolution(); + Contract.ThrowIfNull(solution); + return solution; + } + + return null; } private static async Task TestAsync(TestLspServer testLspServer, TestRequest[] requests) @@ -273,11 +280,9 @@ private static async Task TestAsync(TestLspServer testLspServer, private static List> StartTestRun(TestLspServer testLspServer, TestRequest[] requests, CancellationToken cancellationToken = default) { - var clientCapabilities = new LSP.ClientCapabilities(); - var waitables = new List>(); foreach (var request in requests) - waitables.Add(testLspServer.ExecuteRequestAsync(request.MethodName, request, clientCapabilities, null, cancellationToken)); + waitables.Add(testLspServer.ExecuteRequestAsync(request.MethodName, request, cancellationToken)); return waitables; } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/TestResponse.cs b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/TestResponse.cs index 632a509342882..52537931e758e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Ordering/TestResponse.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Ordering/TestResponse.cs @@ -11,6 +11,6 @@ internal class TestResponse public DateTime StartTime { get; set; } public DateTime EndTime { get; set; } - public Solution Solution { get; set; } + public bool ContextHasSolution { get; set; } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs index 8b2ec34d7cbb7..60aa9c11ba000 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/ProjectContext/GetTextDocumentWithContextHandlerTests.cs @@ -97,7 +97,7 @@ public async Task SwitchingContextsChangesDefaultContext() internal static async Task RunGetProjectContext(TestLspServer testLspServer, Uri uri) { return await testLspServer.ExecuteRequestAsync(LSP.VSMethods.GetProjectContextsName, - CreateGetProjectContextParams(uri), new LSP.ClientCapabilities(), clientName: null, cancellationToken: CancellationToken.None); + CreateGetProjectContextParams(uri), cancellationToken: CancellationToken.None); } private static LSP.VSGetProjectContextsParams CreateGetProjectContextParams(Uri uri) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs index 3b6c51b208d5c..88d9d347be81c 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindAllReferencesHandlerTests.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.VisualStudio.Text.Adornments; +using Newtonsoft.Json.Linq; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -41,7 +42,7 @@ void M2() var j = someInt + A.{|caret:|}{|reference:someInt|}; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertLocationsEqual(testLspServer.GetLocations("reference"), results.Select(result => result.Location)); @@ -77,7 +78,7 @@ void M2() var j = someInt + A.{|caret:|}{|reference:someInt|}; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); using var progress = BufferedProgress.Create(null); @@ -87,7 +88,9 @@ void M2() // BufferedProgress wraps individual elements in an array, so when they are nested them like this, // with the test creating one, and the handler another, we have to unwrap. - results = progress.GetValues().Cast().ToArray(); + // 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 => ((JObject)reference).ToObject()).ToArray(); Assert.NotNull(results); Assert.NotEmpty(results); @@ -125,7 +128,7 @@ void M2() var j = someInt + {|caret:|}{|reference:A|}.someInt; } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertLocationsEqual(testLspServer.GetLocations("reference"), results.Select(result => result.Location)); @@ -168,7 +171,7 @@ void M2() }" }; - using var testLspServer = await CreateTestLspServerAsync(markups); + using var testLspServer = await CreateTestLspServerAsync(markups, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertLocationsEqual(testLspServer.GetLocations("reference"), results.Select(result => result.Location)); @@ -192,7 +195,7 @@ public async Task TestFindAllReferencesAsync_InvalidLocation() { {|caret:|} }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); Assert.Empty(results); @@ -211,7 +214,7 @@ void M() Console.{|caret:|}{|reference:WriteLine|}(""text""); } }"; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); Assert.NotNull(results[0].Location.Uri); @@ -233,7 +236,7 @@ void M() } } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); @@ -263,7 +266,7 @@ void M() } } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); AssertHighlightCount(results, expectedDefinitionCount: 1, expectedWrittenReferenceCount: 1, expectedReferenceCount: 1); @@ -275,7 +278,7 @@ public async Task TestFindAllReferencesAsync_StaticClassification() var markup = @"static class {|caret:|}{|reference:C|} { } "; - using var testLspServer = await CreateTestLspServerAsync(markup); + using var testLspServer = await CreateTestLspServerAsync(markup, CapabilitiesWithVSExtensions); var results = await RunFindAllReferencesAsync(testLspServer, testLspServer.GetLocations("caret").First()); @@ -295,13 +298,8 @@ private static LSP.ReferenceParams CreateReferenceParams(LSP.Location caret, IPr internal static async Task RunFindAllReferencesAsync(TestLspServer testLspServer, LSP.Location caret, IProgress progress = null) { - var vsClientCapabilities = new LSP.VSInternalClientCapabilities - { - SupportsVisualStudioExtensions = true - }; - var results = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentReferencesName, - CreateReferenceParams(caret, progress), vsClientCapabilities, null, CancellationToken.None); + CreateReferenceParams(caret, progress), CancellationToken.None); return results?.Cast()?.ToArray(); } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs index b6fba47f373e2..0f145ac8beb1f 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs @@ -150,7 +150,7 @@ class C : IDisposable private static async Task RunFindImplementationAsync(TestLspServer testLspServer, LSP.Location caret) { return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentImplementationName, - CreateTextDocumentPositionParams(caret), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateTextDocumentPositionParams(caret), CancellationToken.None); } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs index f0855f7f0d1b4..e00c7c239d33e 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Rename/RenameTests.cs @@ -170,8 +170,7 @@ private static LSP.RenameParams CreateRenameParams(LSP.Location location, string private static async Task RunRenameAsync(TestLspServer testLspServer, LSP.RenameParams renameParams) { - return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentRenameName, - renameParams, new LSP.ClientCapabilities(), null, CancellationToken.None); + return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentRenameName, renameParams, CancellationToken.None); } } } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs index 3b8315a864103..e76778c1fa6af 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs @@ -7,6 +7,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; @@ -18,8 +21,8 @@ public abstract class AbstractSemanticTokensTests : AbstractLanguageServerProtoc { private protected static async Task RunGetSemanticTokensRangeAsync(TestLspServer testLspServer, LSP.Location caret, LSP.Range range) { - var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentSemanticTokensRangeName, - CreateSemanticTokensRangeParams(caret, range), new LSP.VSInternalClientCapabilities(), null, CancellationToken.None); + var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentSemanticTokensRangeName, + CreateSemanticTokensRangeParams(caret, range), CancellationToken.None); Contract.ThrowIfNull(result); return result; } @@ -31,6 +34,12 @@ private static LSP.SemanticTokensRangeParams CreateSemanticTokensRangeParams(LSP Range = range }; + protected static async Task UpdateDocumentTextAsync(string updatedText, Workspace workspace) + { + var docId = ((TestWorkspace)workspace).Documents.First().Id; + await ((TestWorkspace)workspace).ChangeDocumentAsync(docId, SourceText.From(updatedText)); + } + // VS doesn't currently support multi-line tokens, so we want to verify that we aren't // returning any in the tokens array. private protected static async Task VerifyNoMultiLineTokens(TestLspServer testLspServer, int[] tokens) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs index 64507fd85008f..b3262e025cc07 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/SignatureHelp/SignatureHelpTests.cs @@ -50,7 +50,7 @@ int M2(string a) { return await testLspServer.ExecuteRequestAsync( LSP.Methods.TextDocumentSignatureHelpName, - CreateTextDocumentPositionParams(caret), new LSP.ClientCapabilities(), null, CancellationToken.None); + CreateTextDocumentPositionParams(caret), CancellationToken.None); } private static LSP.SignatureInformation CreateSignatureInformation(string methodLabal, string methodDocumentation, string parameterLabel, string parameterDocumentation) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs index af86521433832..87a3d8e18b118 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/DocumentSymbolsTests.cs @@ -26,14 +26,24 @@ public async Task TestGetDocumentSymbolsAsync() { }|} }|}"; - using var testLspServer = await CreateTestLspServerAsync(markup); + var clientCapabilities = new LSP.ClientCapabilities() + { + TextDocument = new LSP.TextDocumentClientCapabilities() + { + DocumentSymbol = new LSP.DocumentSymbolSetting() + { + HierarchicalDocumentSymbolSupport = true + } + } + }; + using var testLspServer = await CreateTestLspServerAsync(markup, clientCapabilities); var expected = new LSP.DocumentSymbol[] { CreateDocumentSymbol(LSP.SymbolKind.Class, "A", "A", testLspServer.GetLocations("class").Single(), testLspServer.GetLocations("classSelection").Single()) }; CreateDocumentSymbol(LSP.SymbolKind.Method, "M", "M()", testLspServer.GetLocations("method").Single(), testLspServer.GetLocations("methodSelection").Single(), expected.First()); - var results = await RunGetDocumentSymbolsAsync(testLspServer, true); + var results = await RunGetDocumentSymbolsAsync(testLspServer); AssertJsonEquals(expected, results); } @@ -54,7 +64,7 @@ public async Task TestGetDocumentSymbolsAsync__WithoutHierarchicalSupport() CreateSymbolInformation(LSP.SymbolKind.Method, "M()", testLspServer.GetLocations("method").Single(), Glyph.MethodPrivate, "A") }; - var results = await RunGetDocumentSymbolsAsync(testLspServer, false); + var results = await RunGetDocumentSymbolsAsync(testLspServer); AssertJsonEquals(expected, results); } @@ -72,7 +82,7 @@ void Method() } }"; using var testLspServer = await CreateTestLspServerAsync(markup); - var results = await RunGetDocumentSymbolsAsync(testLspServer, false).ConfigureAwait(false); + var results = await RunGetDocumentSymbolsAsync(testLspServer).ConfigureAwait(false); Assert.Equal(3, results.Length); } @@ -81,11 +91,11 @@ public async Task TestGetDocumentSymbolsAsync__NoSymbols() { using var testLspServer = await CreateTestLspServerAsync(string.Empty); - var results = await RunGetDocumentSymbolsAsync(testLspServer, true); + var results = await RunGetDocumentSymbolsAsync(testLspServer); Assert.Empty(results); } - private static async Task RunGetDocumentSymbolsAsync(TestLspServer testLspServer, bool hierarchicalSupport) + private static async Task RunGetDocumentSymbolsAsync(TestLspServer testLspServer) { var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); var request = new LSP.DocumentSymbolParams @@ -93,19 +103,8 @@ private static async Task RunGetDocumentSymbolsAsync(TestLspServer tes TextDocument = CreateTextDocumentIdentifier(new Uri(document.FilePath)) }; - var clientCapabilities = new LSP.ClientCapabilities() - { - TextDocument = new LSP.TextDocumentClientCapabilities() - { - DocumentSymbol = new LSP.DocumentSymbolSetting() - { - HierarchicalDocumentSymbolSupport = hierarchicalSupport - } - } - }; - - return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentDocumentSymbolName, - request, clientCapabilities, null, CancellationToken.None); + return await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentDocumentSymbolName, + request, CancellationToken.None); } private static void AssertDocumentSymbolEquals(LSP.DocumentSymbol expected, LSP.DocumentSymbol actual) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs index d9275801b67d6..62ab70aadee6a 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Symbols/WorkspaceSymbolsTests.cs @@ -210,7 +210,7 @@ End Sub }; return testLspServer.ExecuteRequestAsync(LSP.Methods.WorkspaceSymbolName, - request, new LSP.ClientCapabilities(), null, CancellationToken.None); + request, CancellationToken.None); } private static string GetContainerName(Solution solution, string? containingSymbolName = null) diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs index 85f8c12cd37fc..9ac86e8d92161 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs @@ -469,8 +469,8 @@ public async Task TestSeparateWorkspaceManagerPerServerAsync() var documentUri = testWorkspace.CurrentSolution.Projects.First().Documents.First().GetURI(); - using var testLspServerOne = new TestLspServer(testWorkspace); - using var testLspServerTwo = new TestLspServer(testWorkspace); + using var testLspServerOne = await TestLspServer.CreateAsync(testWorkspace, clientCapabilities: new(), WellKnownLspServerKinds.AlwaysActiveVSLspServer); + using var testLspServerTwo = await TestLspServer.CreateAsync(testWorkspace, clientCapabilities: new(), WellKnownLspServerKinds.AlwaysActiveVSLspServer); Assert.NotEqual(testLspServerOne.GetManager(), testLspServerTwo.GetManager()); diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/CodeActions/CodeActionsHandlerProvider.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/CodeActions/CodeActionsHandlerProvider.cs index 881ce5a012c98..0537fa14c91fc 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/CodeActions/CodeActionsHandlerProvider.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/CodeActions/CodeActionsHandlerProvider.cs @@ -14,8 +14,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; -using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; -using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { @@ -26,10 +24,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler /// /// Same as C# and VB but for XAML. See also . /// - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(LSP.Methods.TextDocumentCodeActionName)] - [ProvidesMethod(LSP.Methods.CodeActionResolveName)] - [ProvidesCommand(CodeActionsHandler.RunCodeActionCommandName)] + [ExportXamlLspRequestHandlerProvider(typeof(CodeActionsHandler), typeof(CodeActionResolveHandler), typeof(RunCodeActionHandler)), Shared] internal class CodeActionsHandlerProvider : AbstractRequestHandlerProvider { private readonly ICodeFixService _codeFixService; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs index 42f6fe81d6a0d..b6cb52bf8cec3 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandler.cs @@ -19,6 +19,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageSe /// /// Handle the command that adds an event handler method in code /// + [Command(StringConstants.CreateEventHandlerCommand)] internal class CreateEventCommandHandler : AbstractExecuteWorkspaceCommandHandler { public override string Command => StringConstants.CreateEventHandlerCommand; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandlerProvider.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandlerProvider.cs index 6e93c43f612e2..586464ac8afa2 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandlerProvider.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Commands/CreateEventCommandHandlerProvider.cs @@ -10,11 +10,11 @@ using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.Commands; +using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Handler.Commands { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesCommand(StringConstants.CreateEventHandlerCommand)] + [ExportXamlLspRequestHandlerProvider(typeof(CreateEventCommandHandler)), Shared] internal class CreateEventCommandHandlerProvider : AbstractRequestHandlerProvider { [ImportingConstructor] diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs index a9311f8ffd7e4..452cd53d616b3 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionHandler.cs @@ -24,11 +24,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler /// /// Handle a completion request. /// - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentCompletionName)] + [ExportXamlLspRequestHandlerProvider(typeof(CompletionHandler)), Shared] + [Method(Methods.TextDocumentCompletionName)] internal class CompletionHandler : AbstractStatelessRequestHandler { - public override string Method => Methods.TextDocumentCompletionName; private const string CreateEventHandlerCommandTitle = "Create Event Handler"; private static readonly Command s_retriggerCompletionCommand = new Command() diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs index 4bbf661b4a744..d92ee3b29540b 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs @@ -27,14 +27,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler /// /// Handle a completion resolve request to add description. /// - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(LSP.Methods.TextDocumentCompletionResolveName)] + [ExportXamlLspRequestHandlerProvider(typeof(CompletionResolveHandler)), Shared] + [Method(LSP.Methods.TextDocumentCompletionResolveName)] internal class CompletionResolveHandler : AbstractStatelessRequestHandler { private readonly IGlobalOptionService _globalOptions; - public override string Method => LSP.Methods.TextDocumentCompletionResolveName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs index 5eff03c391abd..73c91a2d8cd49 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Definitions/GoToDefinitionHandler.cs @@ -20,13 +20,14 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Definitions; +using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; using Roslyn.Utilities; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Handler.Definitions { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentDefinitionName)] + [ExportXamlLspRequestHandlerProvider(typeof(GoToDefinitionHandler)), Shared] + [Method(Methods.TextDocumentDefinitionName)] internal class GoToDefinitionHandler : AbstractStatelessRequestHandler { private readonly IMetadataAsSourceFileService _metadataAsSourceFileService; @@ -38,8 +39,6 @@ public GoToDefinitionHandler(IMetadataAsSourceFileService metadataAsSourceFileSe _metadataAsSourceFileService = metadataAsSourceFileService; } - public override string Method => Methods.TextDocumentDefinitionName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs index 81a655ca34e64..08ab143035859 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs @@ -11,11 +11,12 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Diagnostics; +using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Handler.Diagnostics { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(VSInternalMethods.DocumentPullDiagnosticName)] + [ExportXamlLspRequestHandlerProvider(typeof(DocumentPullDiagnosticHandler)), Shared] + [Method(VSInternalMethods.DocumentPullDiagnosticName)] internal class DocumentPullDiagnosticHandler : AbstractPullDiagnosticHandler { [ImportingConstructor] @@ -25,8 +26,6 @@ public DocumentPullDiagnosticHandler( : base(xamlPullDiagnosticService) { } - public override string Method => VSInternalMethods.DocumentPullDiagnosticName; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalDocumentDiagnosticsParams request) => request.TextDocument; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index f966b18ea3fe8..6502af8cbed76 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -14,12 +14,13 @@ using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Extensions; +using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Handler.Diagnostics { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(VSInternalMethods.WorkspacePullDiagnosticName)] + [ExportXamlLspRequestHandlerProvider(typeof(WorkspacePullDiagnosticHandler)), Shared] + [Method(VSInternalMethods.WorkspacePullDiagnosticName)] internal class WorkspacePullDiagnosticHandler : AbstractPullDiagnosticHandler { [ImportingConstructor] @@ -29,8 +30,6 @@ public WorkspacePullDiagnosticHandler( : base(xamlPullDiagnosticService) { } - public override string Method => VSInternalMethods.WorkspacePullDiagnosticName; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalWorkspaceDiagnosticsParams request) => null; protected override VSInternalWorkspaceDiagnosticReport CreateReport(TextDocumentIdentifier? identifier, VSDiagnostic[]? diagnostics, string? resultId) diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs index d936116e96db4..e7a5beab88d3f 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/FoldingRanges/FoldingRangesHandler.cs @@ -15,8 +15,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentFoldingRangeName)] + [ExportXamlLspRequestHandlerProvider(typeof(FoldingRangesHandler)), Shared] + [Method(Methods.TextDocumentFoldingRangeName)] internal class FoldingRangesHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -25,8 +25,6 @@ public FoldingRangesHandler() { } - public override string Method => Methods.TextDocumentFoldingRangeName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs index 41008fd3fbba0..412636961380d 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentHandler.cs @@ -13,8 +13,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(LSP.Methods.TextDocumentFormattingName)] + [ExportXamlLspRequestHandlerProvider(typeof(FormatDocumentHandler)), Shared] + [Method(LSP.Methods.TextDocumentFormattingName)] internal class FormatDocumentHandler : AbstractFormatDocumentHandlerBase { [ImportingConstructor] @@ -23,8 +23,6 @@ public FormatDocumentHandler() { } - public override string Method => LSP.Methods.TextDocumentFormattingName; - public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.DocumentFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync(LSP.DocumentFormattingParams request, RequestContext context, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 8bb993af21e32..30d1a5f660369 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -19,8 +19,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentOnTypeFormattingName)] + [ExportXamlLspRequestHandlerProvider(typeof(FormatDocumentOnTypeHandler)), Shared] + [Method(Methods.TextDocumentOnTypeFormattingName)] internal class FormatDocumentOnTypeHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -29,8 +29,6 @@ public FormatDocumentOnTypeHandler() { } - public override string Method => Methods.TextDocumentOnTypeFormattingName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs index 504c4b5665a36..f67afcb105a91 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -13,8 +13,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentRangeFormattingName)] + [ExportXamlLspRequestHandlerProvider(typeof(FormatDocumentRangeHandler)), Shared] + [Method(Methods.TextDocumentRangeFormattingName)] internal class FormatDocumentRangeHandler : AbstractFormatDocumentHandlerBase { [ImportingConstructor] @@ -23,8 +23,6 @@ public FormatDocumentRangeHandler() { } - public override string Method => Methods.TextDocumentRangeFormattingName; - public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentRangeFormattingParams request) => request.TextDocument; public override Task HandleRequestAsync(DocumentRangeFormattingParams request, RequestContext context, CancellationToken cancellationToken) diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs index 23c0be4fd7e7d..f43439072400c 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Hover/HoverHandler.cs @@ -23,8 +23,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentHoverName)] + [ExportXamlLspRequestHandlerProvider(typeof(HoverHandler)), Shared] + [Method(Methods.TextDocumentHoverName)] internal sealed class HoverHandler : AbstractStatelessRequestHandler { private readonly IGlobalOptionService _globalOptions; @@ -36,8 +36,6 @@ public HoverHandler(IGlobalOptionService globalOptions) _globalOptions = globalOptions; } - public override string Method => Methods.TextDocumentHoverName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 7ceddc909b1a7..f964579828523 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -16,8 +16,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(VSInternalMethods.OnAutoInsertName)] + [ExportXamlLspRequestHandlerProvider(typeof(OnAutoInsertHandler)), Shared] + [Method(VSInternalMethods.OnAutoInsertName)] internal class OnAutoInsertHandler : AbstractStatelessRequestHandler { [ImportingConstructor] @@ -26,8 +26,6 @@ public OnAutoInsertHandler() { } - public override string Method => VSInternalMethods.OnAutoInsertName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs index e336cb6827928..3dd53b1372e5a 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs @@ -17,8 +17,8 @@ namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler { - [ExportLspRequestHandlerProvider(StringConstants.XamlLanguageName), Shared] - [ProvidesMethod(Methods.TextDocumentLinkedEditingRangeName)] + [ExportXamlLspRequestHandlerProvider(typeof(OnTypeRenameHandler)), Shared] + [Method(Methods.TextDocumentLinkedEditingRangeName)] internal class OnTypeRenameHandler : AbstractStatelessRequestHandler { // From https://www.w3.org/TR/xml/#NT-NameStartChar @@ -58,8 +58,6 @@ public OnTypeRenameHandler() { } - public override string Method => Methods.TextDocumentLinkedEditingRangeName; - public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs index 8b8ce0b20458e..2c64957cae0f1 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestDispatcherFactory.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -30,7 +31,7 @@ internal sealed class XamlRequestDispatcherFactory : AbstractRequestDispatcherFa [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public XamlRequestDispatcherFactory( - [ImportMany] IEnumerable> requestHandlerProviders, + [ImportMany(StringConstants.XamlLspLanguagesContract)] IEnumerable> requestHandlerProviders, XamlProjectService projectService, [Import(AllowDefault = true)] IXamlLanguageServerFeedbackService? feedbackService) : base(requestHandlerProviders) @@ -39,9 +40,9 @@ public XamlRequestDispatcherFactory( _feedbackService = feedbackService; } - public override RequestDispatcher CreateRequestDispatcher(ImmutableArray supportedLanguages, WellKnownLspServerKinds serverKind) + public override RequestDispatcher CreateRequestDispatcher(WellKnownLspServerKinds serverKind) { - return new XamlRequestDispatcher(_projectService, _requestHandlerProviders, _feedbackService, supportedLanguages, serverKind); + return new XamlRequestDispatcher(_projectService, _requestHandlerProviders, _feedbackService, serverKind); } private class XamlRequestDispatcher : RequestDispatcher @@ -53,17 +54,16 @@ public XamlRequestDispatcher( XamlProjectService projectService, ImmutableArray> requestHandlerProviders, IXamlLanguageServerFeedbackService? feedbackService, - ImmutableArray languageNames, - WellKnownLspServerKinds serverKind) : base(requestHandlerProviders, languageNames, serverKind) + WellKnownLspServerKinds serverKind) : base(requestHandlerProviders, serverKind) { _projectService = projectService; _feedbackService = feedbackService; } - protected override async Task ExecuteRequestAsync( - RequestExecutionQueue queue, RequestType request, ClientCapabilities clientCapabilities, string? clientName, string methodName, bool mutatesSolutionState, bool requiresLSPSolution, IRequestHandler handler, CancellationToken cancellationToken) - where RequestType : class - where ResponseType : default + protected override async Task ExecuteRequestAsync( + RequestExecutionQueue queue, bool mutatesSolutionState, bool requiresLSPSolution, IRequestHandler handler, TRequestType request, ClientCapabilities clientCapabilities, string? clientName, string methodName, CancellationToken cancellationToken) + where TRequestType : class + where TResponseType : default { var textDocument = handler.GetTextDocumentIdentifier(request); @@ -77,7 +77,7 @@ public XamlRequestDispatcher( { try { - return await base.ExecuteRequestAsync(queue, request, clientCapabilities, clientName, methodName, mutatesSolutionState, requiresLSPSolution, handler, cancellationToken).ConfigureAwait(false); + return await base.ExecuteRequestAsync(queue, mutatesSolutionState, requiresLSPSolution, handler, request, clientCapabilities, clientName, methodName, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (e is not OperationCanceledException) { @@ -90,4 +90,12 @@ public XamlRequestDispatcher( } } } + + [AttributeUsage(AttributeTargets.Class), MetadataAttribute] + internal class ExportXamlLspRequestHandlerProviderAttribute : ExportLspRequestHandlerProviderAttribute + { + public ExportXamlLspRequestHandlerProviderAttribute(Type first, params Type[] handlerTypes) : base(StringConstants.XamlLspLanguagesContract, first, handlerTypes) + { + } + } } diff --git a/src/VisualStudio/Xaml/Impl/StringConstants.cs b/src/VisualStudio/Xaml/Impl/StringConstants.cs index 5f9fc444b0431..23b7f97e91ed5 100644 --- a/src/VisualStudio/Xaml/Impl/StringConstants.cs +++ b/src/VisualStudio/Xaml/Impl/StringConstants.cs @@ -13,5 +13,7 @@ internal static class StringConstants public const string CreateEventHandlerCommand = "Xaml.CreateEventHandler"; public const string RetriggerCompletionCommand = "editor.action.triggerSuggest"; + + public const string XamlLspLanguagesContract = "XamlLspLanguages"; } } From 32069d8811550ed8d23939b5aa24a2ac83235e79 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Wed, 16 Feb 2022 16:26:53 -0800 Subject: [PATCH 088/187] Fix --- .../AsyncCompletion/CommitManager.cs | 27 ++++++++-------- .../AsyncCompletion/CompletionItemData.cs | 2 +- .../AsyncCompletion/CompletionSessionData.cs | 9 +++--- .../AsyncCompletion/CompletionSource.cs | 31 ++++++++++--------- .../AsyncCompletion/ItemManager.cs | 9 +++--- .../CSharpCompletionCommandHandlerTests.vb | 6 ++-- 6 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs index e51c5c5a5cb05..1059605bec8ed 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs @@ -79,9 +79,8 @@ public bool ShouldCommitCompletion( return false; } - var sessionData = CompletionSessionData.GetOrCreateSessionData(session); - return !sessionData.ExcludedCommitCharacters.HasValue - || !sessionData.ExcludedCommitCharacters.Value.Contains(typedChar); + return !(session.Properties.TryGetProperty(CompletionSource.ExcludedCommitCharacters, out ImmutableArray excludedCommitCharacter) + && excludedCommitCharacter.Contains(typedChar)); } public AsyncCompletionData.CommitResult TryCommit( @@ -106,16 +105,12 @@ public AsyncCompletionData.CommitResult TryCommit( return CommitResultUnhandled; } - if (!CompletionItemData.TryGetData(item, out var itemData) || !itemData.IsProvidedByRoslynCompletionSource) + if (!CompletionItemData.TryGetData(item, out var itemData)) { // Roslyn should not be called if the item committing was not provided by Roslyn. return CommitResultUnhandled; } - // Need the trigger snapshot to calculate the span when the commit changes to be applied. - // They should always be available from items provided by Roslyn CompletionSource. - Contract.ThrowIfFalse(itemData.TriggerLocation.HasValue); - var filterText = session.ApplicableToSpan.GetText(session.ApplicableToSpan.TextBuffer.CurrentSnapshot) + typeChar; if (Helpers.IsFilterCharacter(itemData.RoslynItem, typeChar, filterText)) { @@ -125,7 +120,6 @@ public AsyncCompletionData.CommitResult TryCommit( var options = _globalOptions.GetCompletionOptions(document.Project.Language); var serviceRules = completionService.GetRules(options); - var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // We can be called before for ShouldCommitCompletion. However, that call does not provide rules applied for the completion item. // Now we check for the commit character in the context of Rules that could change the list of commit characters. @@ -137,19 +131,26 @@ public AsyncCompletionData.CommitResult TryCommit( return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None); } - if (!sessionData.CompletionListSpan.HasValue) + if (!itemData.TriggerLocation.HasValue) { + // Need the trigger snapshot to calculate the span when the commit changes to be applied. + // They should always be available from items provided by Roslyn CompletionSource. + // Just to be defensive, if it's not found here, Roslyn should not make a commit. return CommitResultUnhandled; } - var completionListSpan = sessionData.CompletionListSpan.Value; - var triggerDocument = itemData.TriggerLocation.Value.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (triggerDocument == null) { return CommitResultUnhandled; } + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + if (!sessionData.CompletionListSpan.HasValue) + { + return CommitResultUnhandled; + } + // Telemetry if (sessionData.TargetTypeFilterExperimentEnabled) { @@ -166,7 +167,7 @@ public AsyncCompletionData.CommitResult TryCommit( var commitChar = typeChar == '\0' ? null : (char?)typeChar; return Commit( session, triggerDocument, completionService, subjectBuffer, - itemData.RoslynItem, completionListSpan, commitChar, itemData.TriggerLocation.Value.Snapshot, serviceRules, + itemData.RoslynItem, sessionData.CompletionListSpan.Value, commitChar, itemData.TriggerLocation.Value.Snapshot, serviceRules, filterText, cancellationToken); } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs index d2edab010c587..7d01e672b6bb4 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs @@ -19,7 +19,7 @@ public static bool TryGetData(CompletionItem vsCompletionitem, out CompletionIte public static RoslynCompletionItem GetOrAddDummyRoslynItem(CompletionItem vsItem) { - if (vsItem.Properties.TryGetProperty(RoslynCompletionItemData, out CompletionItemData data)) + if (TryGetData(vsItem, out var data)) return data.RoslynItem; var roslynItem = CreateDummyRoslynItem(vsItem); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs index 3491716bdf5e0..98729d3730fe5 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSessionData.cs @@ -23,11 +23,10 @@ internal sealed class CompletionSessionData public bool TargetTypeFilterSelected { get; set; } public bool HasSuggestionItemOptions { get; set; } - public Optional ExpandedItemTriggerLocation { get; set; } - public Optional CompletionListSpan { get; set; } - public Optional> CombinedSortedList { get; set; } - public Optional> ExpandedItemsTask { get; set; } - public Optional> ExcludedCommitCharacters { get; set; } + public SnapshotPoint? ExpandedItemTriggerLocation { get; set; } + public TextSpan? CompletionListSpan { get; set; } + public ImmutableArray? CombinedSortedList { get; set; } + public Task<(CompletionContext, RoslynCompletionList)>? ExpandedItemsTask { get; set; } private CompletionSessionData() { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 440ef2eb4ac92..ec2d0fca7ee4e 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -43,6 +43,9 @@ internal sealed class CompletionSource : ForegroundThreadAffinitizedObject, IAsy internal const string PotentialCommitCharacters = nameof(PotentialCommitCharacters); internal const string NonBlockingCompletion = nameof(NonBlockingCompletion); + // Don't change this property! Editor code currently has a dependency on it. + internal const string ExcludedCommitCharacters = nameof(ExcludedCommitCharacters); + private static readonly ImmutableArray s_warningImageAttributeImagesArray = ImmutableArray.Create(new ImageElement(Glyph.CompletionWarning.GetImageId(), EditorFeaturesResources.Warning_image_element)); @@ -263,7 +266,7 @@ public async Task GetCompletionContextAsync( var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - UpdateSessionData(sessionData, list, triggerLocation); + UpdateSessionData(session, sessionData, list, triggerLocation); return context; } else if (!_responsiveCompletionEnabled) @@ -273,7 +276,7 @@ public async Task GetCompletionContextAsync( var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.AllItems }, cancellationToken).ConfigureAwait(false); - UpdateSessionData(sessionData, list, triggerLocation); + UpdateSessionData(session, sessionData, list, triggerLocation); AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: true, delayed: false); return context; } @@ -301,13 +304,13 @@ public async Task GetCompletionContextAsync( // Now trigger and wait for core providers to return; var (nonExpandedContext, nonExpandedCompletionList) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - UpdateSessionData(sessionData, nonExpandedCompletionList, triggerLocation); + UpdateSessionData(session, sessionData, nonExpandedCompletionList, triggerLocation); if (expandedItemsTask.IsCompleted) { // the task of expanded item is completed, get the result and combine it with result of non-expanded items. var (expandedContext, expandedCompletionList) = await expandedItemsTask.ConfigureAwait(false); - UpdateSessionData(sessionData, expandedCompletionList, triggerLocation); + UpdateSessionData(session, sessionData, expandedCompletionList, triggerLocation); AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: false); return CombineCompletionContext(nonExpandedContext, expandedContext); @@ -319,7 +322,7 @@ public async Task GetCompletionContextAsync( // after core providers completed (instead of how long it takes end-to-end). stopwatch.Start(); - sessionData.ExpandedItemsTask = new(expandedItemsTask); + sessionData.ExpandedItemsTask = expandedItemsTask; AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: true); return nonExpandedContext; @@ -364,15 +367,15 @@ public async Task GetExpandedCompletionContextAsync( // It's possible we didn't provide expanded items at the beginning of completion session because it was slow even if the feature is enabled. // ExpandedItemsTask would be available in this case, so we just need to return its result. - if (sessionData.ExpandedItemsTask.HasValue) + if (sessionData.ExpandedItemsTask != null) { // Make sure the task is removed when returning expanded items, // so duplicated items won't be added in subsequent list updates. - var task = sessionData.ExpandedItemsTask.Value; - sessionData.ExpandedItemsTask = new(); + var task = sessionData.ExpandedItemsTask; + sessionData.ExpandedItemsTask = null; var (expandedContext, expandedCompletionList) = await task.ConfigureAwait(false); - UpdateSessionData(sessionData, expandedCompletionList, initialTriggerLocation); + UpdateSessionData(session, sessionData, expandedCompletionList, initialTriggerLocation); return expandedContext; } @@ -390,7 +393,7 @@ public async Task GetExpandedCompletionContextAsync( }; var (context, completionList) = await GetCompletionContextWorkerAsync(document, intialTrigger, initialTriggerLocation, options, cancellationToken).ConfigureAwait(false); - UpdateSessionData(sessionData, completionList, initialTriggerLocation); + UpdateSessionData(session, sessionData, completionList, initialTriggerLocation); return context; } @@ -446,7 +449,7 @@ public async Task GetExpandedCompletionContextAsync( return (new(items, suggestionItemOptions, selectionHint: AsyncCompletionData.InitialSelectionHint.SoftSelection, filters), completionList); } - private static void UpdateSessionData(CompletionSessionData sessionData, CompletionList completionList, SnapshotPoint triggerLocation) + private static void UpdateSessionData(IAsyncCompletionSession session, CompletionSessionData sessionData, CompletionList completionList, SnapshotPoint triggerLocation) { // Store around the span this completion list applies to. We'll use this later // to pass this value in when we're committing a completion list item. @@ -461,12 +464,12 @@ private static void UpdateSessionData(CompletionSessionData sessionData, Complet var excludedCommitCharacters = GetExcludedCommitCharacters(completionList.Items); if (excludedCommitCharacters.Length > 0) { - if (sessionData.ExcludedCommitCharacters.HasValue) + if (session.Properties.TryGetProperty(ExcludedCommitCharacters, out ImmutableArray excludedCommitCharactersBefore)) { - excludedCommitCharacters = excludedCommitCharacters.Union(sessionData.ExcludedCommitCharacters.Value).ToImmutableArray(); + excludedCommitCharacters = excludedCommitCharacters.Union(excludedCommitCharactersBefore).ToImmutableArray(); } - sessionData.ExcludedCommitCharacters = excludedCommitCharacters; + session.Properties[ExcludedCommitCharacters] = excludedCommitCharacters; } // We need to remember the trigger location for when a completion service claims expanded items are available diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs index b6bb9677d76e1..308c4b21fa1f8 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs @@ -10,7 +10,6 @@ using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using Roslyn.Utilities; -using RoslynCompletionItem = Microsoft.CodeAnalysis.Completion.CompletionItem; using VSCompletionItem = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion @@ -70,14 +69,14 @@ public Task> SortCompletionListAsync( data = new AsyncCompletionSessionDataSnapshot(sessionData.CombinedSortedList.Value, data.Snapshot, data.Trigger, data.InitialTrigger, data.SelectedFilters, data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); } - else if (sessionData.ExpandedItemsTask.HasValue) + else if (sessionData.ExpandedItemsTask != null) { - var task = sessionData.ExpandedItemsTask.Value; + var task = sessionData.ExpandedItemsTask; if (task.Status == TaskStatus.RanToCompletion) { // Make sure the task is removed when Adding expanded items, // so duplicated items won't be added in subsequent list updates. - sessionData.ExpandedItemsTask = new(); + sessionData.ExpandedItemsTask = null; var (expandedContext, _) = await task.ConfigureAwait(false); if (expandedContext.Items.Length > 0) @@ -89,7 +88,7 @@ public Task> SortCompletionListAsync( var combinedList = itemsBuilder.MoveToImmutable(); // Add expanded items into a combined list, and save it to be used for future updates during the same session. - sessionData.CombinedSortedList = new(combinedList); + sessionData.CombinedSortedList = combinedList; var combinedFilterStates = FilterSet.CombineFilterStates(expandedContext.Filters, data.SelectedFilters); data = new AsyncCompletionSessionDataSnapshot(combinedList, data.Snapshot, data.Trigger, data.InitialTrigger, combinedFilterStates, diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index a7fcb3a55a4d0..022364fd20aae 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -9702,7 +9702,7 @@ class C Dim session = Await state.GetCompletionSession() Dim sessionData = CompletionSessionData.GetOrCreateSessionData(session) - Dim expandTask = sessionData.ExpandedItemsTask.GetValueOrNull() + Dim expandTask = sessionData.ExpandedItemsTask Assert.NotNull(expandTask) Assert.False(expandTask.IsCompleted) @@ -9761,7 +9761,7 @@ class C Dim session = Await state.GetCompletionSession() Dim sessionData = CompletionSessionData.GetOrCreateSessionData(session) - Dim expandTask = sessionData.ExpandedItemsTask.GetValueOrNull() + Dim expandTask = sessionData.ExpandedItemsTask Assert.NotNull(expandTask) Assert.False(expandTask.IsCompleted) @@ -9821,7 +9821,7 @@ class C Dim session = Await state.GetCompletionSession() Dim sessionData = CompletionSessionData.GetOrCreateSessionData(session) - Dim expandTask = sessionData.ExpandedItemsTask.GetValueOrNull() + Dim expandTask = sessionData.ExpandedItemsTask Assert.NotNull(expandTask) Assert.False(expandTask.IsCompleted) From 77654251eccacde4d9d79318346d512f0de898d8 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 16 Feb 2022 16:35:09 -0800 Subject: [PATCH 089/187] Update src/Features/Core/Portable/CodeFixes/CodeFixService.cs Co-authored-by: Jason Malinowski --- src/Features/Core/Portable/CodeFixes/CodeFixService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 23439340bacbf..861b9c611b5fb 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -246,7 +246,7 @@ Func GetFixableDiagnosticFilter(Document document) using var resultDisposer = ArrayBuilder.GetInstance(out var result); var spanToDiagnostics = new SortedDictionary> { - {range, diagnostics }, + { range, diagnostics }, }; await AppendFixesAsync( From ef0d63f9a3f717fc0fbbd9360ca5bfdd552dea10 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Wed, 16 Feb 2022 16:36:21 -0800 Subject: [PATCH 090/187] rename --- .../IntelliSense/AsyncCompletion/CompletionSource.cs | 4 ++-- .../Handlers/Completion/CompletionHandler.cs | 4 ++-- .../CompletionProviders/CrefCompletionProvider.cs | 2 +- .../Core/Portable/Completion/CommonCompletionItem.cs | 10 +++++----- .../Completion/Providers/SymbolCompletionItem.cs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index ec2d0fca7ee4e..78cefd11a30cb 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -444,7 +444,7 @@ public async Task GetExpandedCompletionContextAsync( var suggestionItemOptions = new AsyncCompletionData.SuggestionItemOptions( completionList.SuggestionModeItem.DisplayText, - completionList.SuggestionModeItem.Properties.TryGetValue(CommonCompletionItem.Description, out var description) ? description : string.Empty); + completionList.SuggestionModeItem.Properties.TryGetValue(CommonCompletionItem.DescriptionProperty, out var description) ? description : string.Empty); return (new(items, suggestionItemOptions, selectionHint: AsyncCompletionData.InitialSelectionHint.SoftSelection, filters), completionList); } @@ -559,7 +559,7 @@ private VSCompletionItem Convert( // roslynItem generated by providers can contain an insertionText in a property bag. // We will not use it but other providers may need it. // We actually will calculate the insertion text once again when called TryCommit. - if (!roslynItem.Properties.TryGetValue(CommonCompletionItem.InsertionText, out var insertionText)) + if (!roslynItem.Properties.TryGetValue(CommonCompletionItem.InsertionTextProperty, out var insertionText)) { insertionText = roslynItem.DisplayText; } diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs index 909c9bd1cdc0f..c4f674845709c 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs @@ -261,8 +261,8 @@ static async Task CreateCompletionItemAsync( // If the feature flag is off, return an InsertText. else { - completionItem.InsertText = item.Properties.ContainsKey(CommonCompletionItem.InsertionText) - ? item.Properties[CommonCompletionItem.InsertionText] + completionItem.InsertText = item.Properties.ContainsKey(CommonCompletionItem.InsertionTextProperty) + ? item.Properties[CommonCompletionItem.InsertionTextProperty] : completeDisplayText; } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs index bf8f4dc50a7ae..7d3964708e97e 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs @@ -419,7 +419,7 @@ private static CompletionItemRules GetRules(string displayText) protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) { - if (!selectedItem.Properties.TryGetValue(CommonCompletionItem.InsertionText, out var insertionText)) + if (!selectedItem.Properties.TryGetValue(CommonCompletionItem.InsertionTextProperty, out var insertionText)) { insertionText = selectedItem.DisplayText; } diff --git a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs index 490c8bc544300..4a5a720b8ccb0 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs @@ -11,8 +11,8 @@ namespace Microsoft.CodeAnalysis.Completion { internal static class CommonCompletionItem { - public const string Description = nameof(Description); - public const string InsertionText = nameof(InsertionText); + public const string DescriptionProperty = nameof(DescriptionProperty); + public const string InsertionTextProperty = nameof(InsertionTextProperty); public static CompletionItem Create( string displayText, @@ -45,7 +45,7 @@ public static CompletionItem Create( properties ??= ImmutableDictionary.Empty; if (!description.IsDefault && description.Length > 0) { - properties = properties.Add(Description, EncodeDescription(description.ToTaggedText())); + properties = properties.Add(DescriptionProperty, EncodeDescription(description.ToTaggedText())); } return CompletionItem.Create( @@ -62,11 +62,11 @@ public static CompletionItem Create( } public static bool HasDescription(CompletionItem item) - => item.Properties.ContainsKey(Description); + => item.Properties.ContainsKey(DescriptionProperty); public static CompletionDescription GetDescription(CompletionItem item) { - if (item.Properties.TryGetValue(Description, out var encodedDescription)) + if (item.Properties.TryGetValue(DescriptionProperty, out var encodedDescription)) { return DecodeDescription(encodedDescription); } diff --git a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs index 6df78671c6185..c8f90f98d8176 100644 --- a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs @@ -43,7 +43,7 @@ private static CompletionItem CreateWorker( if (insertionText != null) { - props = props.Add(CommonCompletionItem.InsertionText, insertionText); + props = props.Add(CommonCompletionItem.InsertionTextProperty, insertionText); } props = props.Add("ContextPosition", contextPosition.ToString()); @@ -254,7 +254,7 @@ public static int GetDescriptionPosition(CompletionItem item) => GetContextPosition(item); public static string GetInsertionText(CompletionItem item) - => item.Properties[CommonCompletionItem.InsertionText]; + => item.Properties[CommonCompletionItem.InsertionTextProperty]; // COMPAT OVERLOAD: This is used by IntelliCode. public static CompletionItem CreateWithSymbolId( From 626677b6d44aa8c6b9ff66715d5b172230f13963 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 16 Feb 2022 16:39:05 -0800 Subject: [PATCH 091/187] Be better about checking cancellation --- .../Core/Portable/CodeFixes/CodeFixService.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 861b9c611b5fb..b23441adba1e6 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -81,6 +81,8 @@ public CodeFixService( public async Task GetMostSevereFixableDiagnosticAsync( Document document, TextSpan range, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + if (document == null || !document.IsOpen()) { return default; @@ -126,6 +128,8 @@ public async Task GetMostSevereFixableDiagnosticAsync( TextSpan range, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + foreach (var diagnostic in severityGroup) { if (!range.IntersectsWith(diagnostic.GetTextSpan())) @@ -150,6 +154,8 @@ public async Task> GetFixesAsync( Func addOperationScope, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + // REVIEW: this is the first and simplest design. basically, when ctrl+. is pressed, it asks diagnostic service to give back // current diagnostics for the given span, and it will use that to get fixes. internally diagnostic service will either return cached information // (if it is up-to-date) or synchronously do the work at the spot. @@ -239,6 +245,8 @@ Func GetFixableDiagnosticFilter(Document document) public async Task GetDocumentFixAllForIdInSpanAsync( Document document, TextSpan range, string diagnosticId, CodeActionOptions options, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + var diagnostics = (await _diagnosticService.GetDiagnosticsForSpanAsync(document, range, diagnosticId, includeSuppressedDiagnostics: false, cancellationToken: cancellationToken).ConfigureAwait(false)).ToList(); if (diagnostics.Count == 0) return null; @@ -260,6 +268,8 @@ await AppendFixesAsync( public async Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document document, string diagnosticId, IProgressTracker progressTracker, CodeActionOptions options, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var textSpan = new TextSpan(0, tree.Length); @@ -352,6 +362,8 @@ private async Task AppendFixesAsync( Func addOperationScope, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap); var projectFixersMap = GetProjectFixers(document.Project); @@ -488,6 +500,8 @@ private static async Task> GetCodeFixesAsync( Dictionary<(Diagnostic diagnostic, string? equivalenceKey), CodeFixProvider> diagnosticAndEquivalenceKeyToFixersMap, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + using var fixesDisposer = ArrayBuilder.GetInstance(out var fixes); var context = new CodeFixContext(document, span, diagnostics, // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? @@ -568,6 +582,8 @@ private async Task AppendConfigurationsAsync( PooledHashSet registeredConfigurationFixTitles, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + if (!_configurationProvidersMap.TryGetValue(document.Project.Language, out var lazyConfigurationProviders) || lazyConfigurationProviders.Value == null) { return; @@ -603,6 +619,8 @@ private async Task AppendFixesOrConfigurationsAsync( CancellationToken cancellationToken) where TCodeFixProvider : notnull { + cancellationToken.ThrowIfCancellationRequested(); + var allDiagnostics = await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) .ToDiagnosticsAsync(document.Project, cancellationToken).ConfigureAwait(false); @@ -697,6 +715,8 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) private async Task> GetDocumentDiagnosticsAsync(Document document, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + Contract.ThrowIfNull(document); var solution = document.Project.Solution; var diagnostics = await _diagnosticService.GetDiagnosticsForIdsAsync(solution, null, document.Id, diagnosticIds, includeSuppressedDiagnostics, cancellationToken).ConfigureAwait(false); @@ -706,6 +726,8 @@ private async Task> GetDocumentDiagnosticsAsync(Document private async Task> GetProjectDiagnosticsAsync(Project project, bool includeAllDocumentDiagnostics, ImmutableHashSet? diagnosticIds, bool includeSuppressedDiagnostics, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + Contract.ThrowIfNull(project); if (includeAllDocumentDiagnostics) @@ -726,6 +748,8 @@ private async Task> GetProjectDiagnosticsAsync(Project p private async Task ContainsAnyFixAsync( Document document, DiagnosticData diagnostic, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + var workspaceFixers = ImmutableArray.Empty; var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap) && fixerMap.Value.TryGetValue(diagnostic.Id, out workspaceFixers); var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnostic.Id, out var projectFixers); From 8eadc41e4f5c19be6b9a7971cfc33a3b94a01c8e Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 16 Feb 2022 18:03:32 -0800 Subject: [PATCH 092/187] Update tagger event sources to handle delayed source generated file updates --- .../AbstractDiagnosticsTaggerProvider.cs | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs index f678f96324403..09d84a584d7b6 100644 --- a/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Diagnostics/AbstractDiagnosticsTaggerProvider.cs @@ -75,38 +75,32 @@ private void OnDiagnosticsUpdated(object? sender, DiagnosticsUpdatedArgs e) return; } - var asyncToken = AsyncListener.BeginAsyncOperation(nameof(OnDiagnosticsUpdatedAsync)); - _ = OnDiagnosticsUpdatedAsync(e.Id, e.Workspace, e.Solution, e.DocumentId).CompletesAsyncOperation(asyncToken); + var document = e.Solution.GetDocument(e.DocumentId); - static async Task OnDiagnosticsUpdatedAsync(object updateGroupId, Workspace workspace, Solution solution, DocumentId documentId) + // If we couldn't find a normal document, and all features are enabled for source generated documents, + // attempt to locate a matching source generated document in the project. + if (document is null + && e.Workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true } + && e.Solution.GetProject(e.DocumentId.ProjectId) is { } project) { - var document = solution.GetDocument(documentId); - - // If we couldn't find a normal document, and all features are enabled for source generated documents, - // attempt to locate a matching source generated document in the project. - if (document is null - && workspace.Services.GetService() is { EnableOpeningSourceGeneratedFilesInWorkspace: true } - && solution.GetProject(documentId.ProjectId) is { } project) - { - document = await project.GetSourceGeneratedDocumentAsync(documentId, CancellationToken.None).ConfigureAwait(false); - } + document = ThreadingContext.JoinableTaskFactory.Run(() => project.GetSourceGeneratedDocumentAsync(e.DocumentId, CancellationToken.None).AsTask()); + } - // Open documents *should* always have their SourceText available, but we cannot guarantee - // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's - // a small risk that between calling .IsOpen the file may then close, which then would - // cause TryGetText to fail. However, that's ok. In that case, if we do need to tag this - // document, we'll just use the current editor snapshot. If that's the same, then the tags - // will be hte same. If it is different, we'll eventually hear about the new diagnostics - // for it and we'll reach our fixed point. - if (document != null && document.IsOpen()) + // Open documents *should* always have their SourceText available, but we cannot guarantee + // (i.e. assert) that they do. That's because we're not on the UI thread here, so there's + // a small risk that between calling .IsOpen the file may then close, which then would + // cause TryGetText to fail. However, that's ok. In that case, if we do need to tag this + // document, we'll just use the current editor snapshot. If that's the same, then the tags + // will be hte same. If it is different, we'll eventually hear about the new diagnostics + // for it and we'll reach our fixed point. + if (document != null && document.IsOpen()) + { + // This should always be fast since the document is open. + var sourceText = document.State.GetTextSynchronously(cancellationToken: default); + snapshot = sourceText.FindCorrespondingEditorTextSnapshot(); + if (snapshot != null) { - // This should always be fast since the document is open. - var sourceText = document.State.GetTextSynchronously(cancellationToken: default); - var snapshot = sourceText.FindCorrespondingEditorTextSnapshot(); - if (snapshot != null) - { - _diagnosticIdToTextSnapshot.GetValue(updateGroupId, _ => snapshot); - } + _diagnosticIdToTextSnapshot.GetValue(e.Id, _ => snapshot); } } } @@ -116,10 +110,15 @@ static async Task OnDiagnosticsUpdatedAsync(object updateGroupId, Workspace work protected override ITaggerEventSource CreateEventSource(ITextView textViewOpt, ITextBuffer subjectBuffer) { + // OnTextChanged is added for diagnostics in source generated files: it's possible that the analyzer driver + // executed on content which was produced by a source generator but is not yet reflected in an open text + // buffer for that generated file. In this case, we need to update the tags after the buffer updates (which + // triggers a text changed event) to ensure diagnostics are positioned correctly. return TaggerEventSources.Compose( TaggerEventSources.OnDocumentActiveContextChanged(subjectBuffer), TaggerEventSources.OnWorkspaceRegistrationChanged(subjectBuffer), - TaggerEventSources.OnDiagnosticsChanged(subjectBuffer, _diagnosticService)); + TaggerEventSources.OnDiagnosticsChanged(subjectBuffer, _diagnosticService), + TaggerEventSources.OnTextChanged(subjectBuffer)); } protected internal abstract bool IsEnabled { get; } From fba2d342c385cb4946eebbe709f8fbbc4c3274dc Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 08:43:11 -0800 Subject: [PATCH 093/187] Port CSharpF1Help to the new integration tests --- .../VisualBasic/BasicF1Help.cs | 60 ------------------- .../CSharp/CSharpF1Help.cs | 33 +++++----- .../InProcess/EditorInProcess.cs | 36 +++++++++++ .../VisualBasic/BasicF1Help.cs | 59 ++++++++++++++++++ .../TestUtilities/InProcess/Editor_InProc.cs | 33 ---------- .../OutOfProcess/Editor_OutOfProc.cs | 3 - 6 files changed, 110 insertions(+), 114 deletions(-) delete mode 100644 src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs rename src/VisualStudio/IntegrationTest/{IntegrationTests => New.IntegrationTests}/CSharp/CSharpF1Help.cs (59%) create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs deleted file mode 100644 index 95819182d9168..0000000000000 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicF1Help.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; -using Xunit; -using Xunit.Abstractions; - -namespace Roslyn.VisualStudio.IntegrationTests.VisualBasic -{ - [Collection(nameof(SharedIntegrationHostFixture))] - public class BasicF1Help : AbstractEditorTest - { - protected override string LanguageName => LanguageNames.VisualBasic; - - public BasicF1Help(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(BasicF1Help)) - { - } - - [WpfFact, Trait(Traits.Feature, Traits.Features.F1Help)] - private void F1Help() - { - var text = @" -Imports System -Imports System.Collections.Generic -Imports System.Linq - -Module Program$$ - Sub Main(args As String()) - Dim query = From arg In args - Select args.Any(Function(a) a.Length > 5) - Dim x = 0 - x += 1 - End Sub - Public Function F() As Object - Return Nothing - End Function -End Module"; - - SetUpEditor(text); - Verify("Linq", "System.Linq"); - Verify("String", "vb.String"); - Verify("Any", "System.Linq.Enumerable.Any"); - Verify("From", "vb.QueryFrom"); - Verify("+=", "vb.+="); - Verify("Nothing", "vb.Nothing"); - - } - - private void Verify(string word, string expectedKeyword) - { - VisualStudio.Editor.PlaceCaret(word, charsOffset: -1); - Assert.Contains(expectedKeyword, VisualStudio.Editor.GetF1Keyword()); - } - } -} diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpF1Help.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpF1Help.cs similarity index 59% rename from src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpF1Help.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpF1Help.cs index 45a8bcd352d05..bb85cde539413 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpF1Help.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpF1Help.cs @@ -2,29 +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. -#nullable disable - +using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Roslyn.VisualStudio.IntegrationTests.CSharp { - [Collection(nameof(SharedIntegrationHostFixture))] + [Trait(Traits.Feature, Traits.Features.F1Help)] public class CSharpF1Help : AbstractEditorTest { protected override string LanguageName => LanguageNames.CSharp; - public CSharpF1Help(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(CSharpF1Help)) + public CSharpF1Help() + : base(nameof(CSharpF1Help)) { } - [WpfFact, Trait(Traits.Feature, Traits.Features.F1Help)] - private void F1Help() + [IdeFact] + private async Task F1Help() { var text = @" using System; @@ -69,17 +66,17 @@ group n by n % 5 into g #endregion TaoRegion }"; - SetUpEditor(text); - Verify("abstract", "abstract_CSharpKeyword"); - Verify("ascending", "ascending_CSharpKeyword"); - Verify("from", "from_CSharpKeyword"); - Verify("First();", "System.Linq.Enumerable.First``1"); + await SetUpEditorAsync(text, HangMitigatingCancellationToken); + await VerifyAsync("abstract", "abstract_CSharpKeyword", HangMitigatingCancellationToken); + await VerifyAsync("ascending", "ascending_CSharpKeyword", HangMitigatingCancellationToken); + await VerifyAsync("from", "from_CSharpKeyword", HangMitigatingCancellationToken); + await VerifyAsync("First();", "System.Linq.Enumerable.First``1", HangMitigatingCancellationToken); } - private void Verify(string word, string expectedKeyword) + private async Task VerifyAsync(string word, string expectedKeyword, CancellationToken cancellationToken) { - VisualStudio.Editor.PlaceCaret(word, charsOffset: -1); - Assert.Contains(expectedKeyword, VisualStudio.Editor.GetF1Keyword()); + await TestServices.Editor.PlaceCaretAsync(word, charsOffset: -1, cancellationToken); + Assert.Contains(expectedKeyword, await TestServices.Editor.GetF1KeywordsAsync(cancellationToken)); } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 632870b89dab2..792663a008b42 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -291,6 +291,42 @@ public async Task SetUseSuggestionModeAsync(bool forDebuggerTextView, bool value private static bool IsDebuggerTextView(ITextView textView) => textView.Roles.Contains("DEBUGVIEW"); + public async Task> GetF1KeywordsAsync(CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var vsView = await GetActiveVsTextViewAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(vsView.GetBuffer(out var textLines)); + ErrorHandler.ThrowOnFailure(textLines.GetLanguageServiceID(out var languageServiceGuid)); + + var languageService = await ((AsyncServiceProvider)AsyncServiceProvider.GlobalProvider).QueryServiceAsync(languageServiceGuid).WithCancellation(cancellationToken); + Assumes.Present(languageService); + + var languageContextProvider = (IVsLanguageContextProvider)languageService; + var monitorUserContext = await GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(monitorUserContext.CreateEmptyContext(out var emptyUserContext)); + ErrorHandler.ThrowOnFailure(vsView.GetCaretPos(out var line, out var column)); + + var span = new TextManager.Interop.TextSpan() + { + iStartLine = line, + iStartIndex = column, + iEndLine = line, + iEndIndex = column, + }; + + ErrorHandler.ThrowOnFailure(languageContextProvider.UpdateLanguageContext(dwHint: 0, textLines, new[] { span }, emptyUserContext)); + ErrorHandler.ThrowOnFailure(emptyUserContext.CountAttributes("keyword", fIncludeChildren: Convert.ToInt32(true), out var count)); + var results = ImmutableArray.CreateBuilder(count); + for (var i = 0; i < count; i++) + { + emptyUserContext.GetAttribute(i, "keyword", fIncludeChildren: Convert.ToInt32(true), pbstrName: out _, out var value); + results.Add(value); + } + + return results.MoveToImmutable(); + } + #region Navigation bars public async Task ExpandNavigationBarAsync(NavigationBarDropdownKind index, CancellationToken cancellationToken) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs new file mode 100644 index 0000000000000..b90e54250fbe2 --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicF1Help.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Roslyn.VisualStudio.IntegrationTests.VisualBasic +{ + [Trait(Traits.Feature, Traits.Features.F1Help)] + public class BasicF1Help : AbstractEditorTest + { + protected override string LanguageName => LanguageNames.VisualBasic; + + public BasicF1Help() + : base(nameof(BasicF1Help)) + { + } + + [IdeFact] + private async Task F1Help() + { + var text = @" +Imports System +Imports System.Collections.Generic +Imports System.Linq + +Module Program$$ + Sub Main(args As String()) + Dim query = From arg In args + Select args.Any(Function(a) a.Length > 5) + Dim x = 0 + x += 1 + End Sub + Public Function F() As Object + Return Nothing + End Function +End Module"; + + await SetUpEditorAsync(text, HangMitigatingCancellationToken); + await VerifyAsync("Linq", "System.Linq", HangMitigatingCancellationToken); + await VerifyAsync("String", "vb.String", HangMitigatingCancellationToken); + await VerifyAsync("Any", "System.Linq.Enumerable.Any", HangMitigatingCancellationToken); + await VerifyAsync("From", "vb.QueryFrom", HangMitigatingCancellationToken); + await VerifyAsync("+=", "vb.+=", HangMitigatingCancellationToken); + await VerifyAsync("Nothing", "vb.Nothing", HangMitigatingCancellationToken); + + } + + private async Task VerifyAsync(string word, string expectedKeyword, CancellationToken cancellationToken) + { + await TestServices.Editor.PlaceCaretAsync(word, charsOffset: -1, cancellationToken); + Assert.Contains(expectedKeyword, await TestServices.Editor.GetF1KeywordsAsync(cancellationToken)); + } + } +} diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs index 22c4a7376e484..9be898d6dc390 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/InProcess/Editor_InProc.cs @@ -736,39 +736,6 @@ public string[] GetOutliningSpans() }); } - public List GetF1Keywords() - { - return InvokeOnUIThread(cancellationToken => - { - var results = new List(); - GetActiveVsTextView().GetBuffer(out var textLines); - Marshal.ThrowExceptionForHR(textLines.GetLanguageServiceID(out var languageServiceGuid)); - Marshal.ThrowExceptionForHR(Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.QueryService(languageServiceGuid, out var languageService)); - var languageContextProvider = (IVsLanguageContextProvider)languageService; - - var monitorUserContext = GetGlobalService(); - Marshal.ThrowExceptionForHR(monitorUserContext.CreateEmptyContext(out var emptyUserContext)); - Marshal.ThrowExceptionForHR(GetActiveVsTextView().GetCaretPos(out var line, out var column)); - var span = new TextManager.Interop.TextSpan() - { - iStartLine = line, - iStartIndex = column, - iEndLine = line, - iEndIndex = column - }; - - Marshal.ThrowExceptionForHR(languageContextProvider.UpdateLanguageContext(0, textLines, new[] { span }, emptyUserContext)); - Marshal.ThrowExceptionForHR(emptyUserContext.CountAttributes("keyword", VSConstants.S_FALSE, out var count)); - for (var i = 0; i < count; i++) - { - emptyUserContext.GetAttribute(i, "keyword", VSConstants.S_FALSE, out var key, out var value); - results.Add(value); - } - - return results; - }); - } - public void GoToDefinition() => ExecuteCommand(WellKnownCommandNames.Edit_GoToDefinition); diff --git a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs index e491862fda2b8..3929d76969eb9 100644 --- a/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs +++ b/src/VisualStudio/IntegrationTest/TestUtilities/OutOfProcess/Editor_OutOfProc.cs @@ -239,9 +239,6 @@ public void WaitForActiveView(string viewName) public string[] GetErrorTags() => _editorInProc.GetErrorTags(); - public List GetF1Keyword() - => _editorInProc.GetF1Keywords(); - public void ExpandTypeNavBar() { _instance.Workspace.WaitForAsyncOperations(Helper.HangMitigatingTimeout, FeatureAttribute.NavigationBar); From ed66950dde1b26b3ef50a4a099369593b36bda6c Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 11:00:16 -0800 Subject: [PATCH 094/187] Simplify command execution --- .../InProcess/EditorInProcess.cs | 8 +-- .../InProcess/ShellInProcess.cs | 51 +++++++++++++++++++ .../InProcess/SolutionExplorerInProcess.cs | 5 +- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs index 792663a008b42..bdbf90e0f1669 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/EditorInProcess.cs @@ -37,7 +37,6 @@ using Roslyn.VisualStudio.IntegrationTests.InProcess; using Xunit; using IObjectWithSite = Microsoft.VisualStudio.OLE.Interop.IObjectWithSite; -using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; using OLECMDEXECOPT = Microsoft.VisualStudio.OLE.Interop.OLECMDEXECOPT; using TextSpan = Microsoft.CodeAnalysis.Text.TextSpan; @@ -278,9 +277,7 @@ public async Task SetUseSuggestionModeAsync(bool forDebuggerTextView, bool value { if (await IsUseSuggestionModeOnAsync(forDebuggerTextView, cancellationToken) != value) { - var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd2KCmdID).GUID, (uint)VSConstants.VSStd2KCmdID.ToggleConsumeFirstCompletionMode, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); - + await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd2KCmdID.ToggleConsumeFirstCompletionMode, cancellationToken); if (await IsUseSuggestionModeOnAsync(forDebuggerTextView, cancellationToken) != value) { throw new InvalidOperationException($"{WellKnownCommandNames.Edit_ToggleCompletionMode} did not leave the editor in the expected state."); @@ -886,8 +883,7 @@ public async Task GoToBaseAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(EditorConstants.EditorCommandSet, (uint)EditorConstants.EditorCommandID.GoToBase, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + await TestServices.Shell.ExecuteCommandAsync(EditorConstants.EditorCommandID.GoToBase, cancellationToken); await TestServices.Workspace.WaitForAsyncOperationsAsync(FeatureAttribute.Workspace, cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs index 4d53d48932271..2ff1364699281 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs @@ -2,20 +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. +using System; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.UnitTests; +using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using IAsyncDisposable = System.IAsyncDisposable; +using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; +using OLECMDEXECOPT = Microsoft.VisualStudio.OLE.Interop.OLECMDEXECOPT; namespace Microsoft.VisualStudio.Extensibility.Testing { internal partial class ShellInProcess { + public async Task<(Guid commandGroup, uint commandId)> PrepareCommandAsync(string command, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var commandWindow = await GetRequiredGlobalServiceAsync(cancellationToken); + var result = new PREPARECOMMANDRESULT[1]; + ErrorHandler.ThrowOnFailure(commandWindow.PrepareCommand(command, out var commandGroup, out var commandId, out var cmdArg, result)); + + if (cmdArg != IntPtr.Zero) + { + throw new NotSupportedException("Unable to create a disposable wrapper for command arguments (VARIANT)."); + } + + return (commandGroup, commandId); + } + + public async Task ExecuteCommandAsync(string command, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var (commandGuid, commandId) = await PrepareCommandAsync(command, cancellationToken); + await ExecuteCommandAsync(commandGuid, commandId, cancellationToken); + } + + public Task ExecuteCommandAsync(TEnum command, CancellationToken cancellationToken) + where TEnum : struct, Enum + { + var commandGuid = command switch + { + EditorConstants.EditorCommandID => EditorConstants.EditorCommandSet, + _ => typeof(TEnum).GUID, + }; + + return ExecuteCommandAsync(commandGuid, Convert.ToUInt32(command), cancellationToken); + } + + public Task ExecuteCommandAsync((Guid commandGuid, uint commandId) command, CancellationToken cancellationToken) + => ExecuteCommandAsync(command.commandGuid, command.commandId, cancellationToken); + + public async Task ExecuteCommandAsync(Guid commandGuid, uint commandId, CancellationToken cancellationToken) + { + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + var dispatcher = await TestServices.Shell.GetRequiredGlobalServiceAsync(cancellationToken); + ErrorHandler.ThrowOnFailure(dispatcher.Exec(commandGuid, commandId, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + } + public async Task PauseFileChangesAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs index dee04f7fdc0d3..b490f15a66def 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/SolutionExplorerInProcess.cs @@ -206,10 +206,7 @@ await Helper.RetryAsync( public async Task SaveAllAsync(CancellationToken cancellationToken) { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var dispatcher = await GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(typeof(VSConstants.VSStd97CmdID).GUID, (uint)VSConstants.VSStd97CmdID.SaveSolution, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); + await TestServices.Shell.ExecuteCommandAsync(VSConstants.VSStd97CmdID.SaveSolution, cancellationToken); } public async Task OpenFileAsync(string projectName, string relativeFilePath, CancellationToken cancellationToken) From 8e72ebe3e4e2b89a929eae2307620fae3446062a Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 11 Feb 2022 11:05:53 -0800 Subject: [PATCH 095/187] Port CSharpOrganizing to the new integration test project --- .../CSharp/CSharpOrganizing.cs | 27 ++++++++----------- .../New.IntegrationTests/WellKnownCommands.cs | 17 ++++++++++++ 2 files changed, 28 insertions(+), 16 deletions(-) rename src/VisualStudio/IntegrationTest/{IntegrationTests => New.IntegrationTests}/CSharp/CSharpOrganizing.cs (55%) create mode 100644 src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpOrganizing.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpOrganizing.cs similarity index 55% rename from src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpOrganizing.cs rename to src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpOrganizing.cs index d6c96fabf184b..5901459adab23 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpOrganizing.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpOrganizing.cs @@ -2,31 +2,27 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Roslyn.VisualStudio.IntegrationTests.CSharp { - [Collection(nameof(SharedIntegrationHostFixture))] + [Trait(Traits.Feature, Traits.Features.Organizing)] public class CSharpOrganizing : AbstractEditorTest { protected override string LanguageName => LanguageNames.CSharp; - public CSharpOrganizing(VisualStudioInstanceFactory instanceFactory) - : base(instanceFactory, nameof(CSharpOrganizing)) + public CSharpOrganizing() + : base(nameof(CSharpOrganizing)) { } - [WpfFact, Trait(Traits.Feature, Traits.Features.Organizing)] - public void RemoveAndSort() + [IdeFact] + public async Task RemoveAndSort() { - SetUpEditor(@"$$ + await SetUpEditorAsync(@"$$ using C; using B; using A; @@ -38,9 +34,9 @@ class Test } namespace A { public class CA { } } namespace B { public class CB { } } -namespace C { public class CC { } }"); - VisualStudio.ExecuteCommand("Edit.RemoveAndSort"); - VisualStudio.Editor.Verify.TextContains(@" +namespace C { public class CC { } }", HangMitigatingCancellationToken); + await TestServices.Shell.ExecuteCommandAsync(WellKnownCommands.Edit.RemoveAndSort, HangMitigatingCancellationToken); + await TestServices.EditorVerifier.TextContainsAsync(@" using A; using C; @@ -51,8 +47,7 @@ class Test } namespace A { public class CA { } } namespace B { public class CB { } } -namespace C { public class CC { } }"); - +namespace C { public class CC { } }", cancellationToken: HangMitigatingCancellationToken); } } } diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs new file mode 100644 index 0000000000000..45e7742dc685f --- /dev/null +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.VisualStudio; + +namespace Roslyn.VisualStudio.IntegrationTests +{ + internal static class WellKnownCommands + { + public static class Edit + { + public static readonly (Guid commandGroup, uint commandId) RemoveAndSort = (VSConstants.CMDSETID.CSharpGroup_guid, 6419); + } + } +} From 4da854c246ae5f4979837131e4b3d1cc8f209726 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 16 Feb 2022 13:12:48 -0800 Subject: [PATCH 096/187] Update to vs-extension-testing 0.1.127-beta --- eng/Versions.props | 2 +- .../InProcess/ShellInProcess.cs | 51 ------------------- .../New.IntegrationTests/WellKnownCommands.cs | 4 +- 3 files changed, 3 insertions(+), 54 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index bb7a9a0e7202f..efa3d1be8afc3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -22,7 +22,7 @@ 3.3.3-beta1.21105.3 6.0.0-rc1.21366.2 1.1.1-beta1.22081.4 - 0.1.124-beta + 0.1.127-beta 4.0.1 16.10.230 diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs index 2ff1364699281..4d53d48932271 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ShellInProcess.cs @@ -2,71 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.UnitTests; -using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using IAsyncDisposable = System.IAsyncDisposable; -using IOleCommandTarget = Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget; -using OLECMDEXECOPT = Microsoft.VisualStudio.OLE.Interop.OLECMDEXECOPT; namespace Microsoft.VisualStudio.Extensibility.Testing { internal partial class ShellInProcess { - public async Task<(Guid commandGroup, uint commandId)> PrepareCommandAsync(string command, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var commandWindow = await GetRequiredGlobalServiceAsync(cancellationToken); - var result = new PREPARECOMMANDRESULT[1]; - ErrorHandler.ThrowOnFailure(commandWindow.PrepareCommand(command, out var commandGroup, out var commandId, out var cmdArg, result)); - - if (cmdArg != IntPtr.Zero) - { - throw new NotSupportedException("Unable to create a disposable wrapper for command arguments (VARIANT)."); - } - - return (commandGroup, commandId); - } - - public async Task ExecuteCommandAsync(string command, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var (commandGuid, commandId) = await PrepareCommandAsync(command, cancellationToken); - await ExecuteCommandAsync(commandGuid, commandId, cancellationToken); - } - - public Task ExecuteCommandAsync(TEnum command, CancellationToken cancellationToken) - where TEnum : struct, Enum - { - var commandGuid = command switch - { - EditorConstants.EditorCommandID => EditorConstants.EditorCommandSet, - _ => typeof(TEnum).GUID, - }; - - return ExecuteCommandAsync(commandGuid, Convert.ToUInt32(command), cancellationToken); - } - - public Task ExecuteCommandAsync((Guid commandGuid, uint commandId) command, CancellationToken cancellationToken) - => ExecuteCommandAsync(command.commandGuid, command.commandId, cancellationToken); - - public async Task ExecuteCommandAsync(Guid commandGuid, uint commandId, CancellationToken cancellationToken) - { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - - var dispatcher = await TestServices.Shell.GetRequiredGlobalServiceAsync(cancellationToken); - ErrorHandler.ThrowOnFailure(dispatcher.Exec(commandGuid, commandId, (uint)OLECMDEXECOPT.OLECMDEXECOPT_DODEFAULT, IntPtr.Zero, IntPtr.Zero)); - } - public async Task PauseFileChangesAsync(CancellationToken cancellationToken) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs index 45e7742dc685f..ba104e0ac500f 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/WellKnownCommands.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; +using System.ComponentModel.Design; using Microsoft.VisualStudio; namespace Roslyn.VisualStudio.IntegrationTests @@ -11,7 +11,7 @@ internal static class WellKnownCommands { public static class Edit { - public static readonly (Guid commandGroup, uint commandId) RemoveAndSort = (VSConstants.CMDSETID.CSharpGroup_guid, 6419); + public static readonly CommandID RemoveAndSort = new(VSConstants.CMDSETID.CSharpGroup_guid, 6419); } } } From 6f94f3dbfde7ba42e37532eb0713f4b67a3616b7 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 16 Feb 2022 13:15:48 -0800 Subject: [PATCH 097/187] Log execution time for tests --- src/Tools/Source/RunTests/ProcessTestExecutor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tools/Source/RunTests/ProcessTestExecutor.cs b/src/Tools/Source/RunTests/ProcessTestExecutor.cs index e8dc9282c45af..9f34c63733534 100644 --- a/src/Tools/Source/RunTests/ProcessTestExecutor.cs +++ b/src/Tools/Source/RunTests/ProcessTestExecutor.cs @@ -196,7 +196,7 @@ private async Task RunTestAsyncInternal(AssemblyInfo assemblyInfo, b } } - Logger.Log($"Command line {assemblyInfo.DisplayName}: {Options.DotnetFilePath} {commandLineArguments}"); + Logger.Log($"Command line {assemblyInfo.DisplayName} completed in {span.TotalSeconds} seconds: {Options.DotnetFilePath} {commandLineArguments}"); var standardOutput = string.Join(Environment.NewLine, xunitProcessResult.OutputLines) ?? ""; var errorOutput = string.Join(Environment.NewLine, xunitProcessResult.ErrorLines) ?? ""; From 09af1e37c2482a846a2e11c4cd8ab287afd85e55 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Thu, 17 Feb 2022 10:53:50 -0800 Subject: [PATCH 098/187] Fix test --- .../IntelliSense/AsyncCompletion/CompletionItemData.cs | 3 +-- .../IntelliSense/AsyncCompletion/CompletionSource.cs | 3 +-- .../AsyncCompletion/ItemManager.CompletionListUpdater.cs | 2 +- .../Test/Completion/FileSystemCompletionHelperTests.cs | 4 +--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs index 7d01e672b6bb4..cba26a8302f54 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionItemData.cs @@ -12,8 +12,6 @@ internal sealed record class CompletionItemData(RoslynCompletionItem RoslynItem, { private const string RoslynCompletionItemData = nameof(RoslynCompletionItemData); - public bool IsProvidedByRoslynCompletionSource => TriggerLocation.HasValue; - public static bool TryGetData(CompletionItem vsCompletionitem, out CompletionItemData data) => vsCompletionitem.Properties.TryGetProperty(RoslynCompletionItemData, out data); @@ -22,6 +20,7 @@ public static RoslynCompletionItem GetOrAddDummyRoslynItem(CompletionItem vsItem if (TryGetData(vsItem, out var data)) return data.RoslynItem; + // TriggerLocation is null for items provided by non-roslyn completion source var roslynItem = CreateDummyRoslynItem(vsItem); AddData(vsItem, roslynItem, triggerLocation: null); diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 78cefd11a30cb..5603b0d71f2be 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -490,10 +490,9 @@ private static void UpdateSessionData(IAsyncCompletionSession session, Completio if (item is null) throw new ArgumentNullException(nameof(item)); - if (!CompletionItemData.TryGetData(item, out var itemData) || !itemData.IsProvidedByRoslynCompletionSource) + if (!CompletionItemData.TryGetData(item, out var itemData) || !itemData.TriggerLocation.HasValue) return null; - Contract.ThrowIfNull(itemData.TriggerLocation); var document = itemData.TriggerLocation.Value.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) return null; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs index 27427bb85f36b..5bcbf0c447000 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.CompletionListUpdater.cs @@ -559,7 +559,7 @@ private static bool TryGetInitialTriggerLocation(AsyncCompletionSessionDataSnaps { foreach (var item in data.InitialSortedList) { - if (CompletionItemData.TryGetData(item, out var itemData) && itemData.IsProvidedByRoslynCompletionSource) + if (CompletionItemData.TryGetData(item, out var itemData) && itemData.TriggerLocation.HasValue) { intialTriggerLocation = itemData.TriggerLocation.Value; return true; diff --git a/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs b/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs index 977908cdef448..dd635658652c4 100644 --- a/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs +++ b/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.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.Immutable; using System.Linq; @@ -20,7 +18,7 @@ private static void AssertItemsEqual(ImmutableArray actual, para { AssertEx.Equal( expected, - actual.Select(c => $"'{c.DisplayText}', {string.Join(", ", c.Tags)}, '{c.Properties["Description"]}'"), + actual.Select(c => $"'{c.DisplayText}', {string.Join(", ", c.Tags)}, '{c.Properties[CommonCompletionItem.DescriptionProperty]}'"), itemInspector: c => $"@\"{c}\""); Assert.True(actual.All(i => i.Rules == TestFileSystemCompletionHelper.CompletionRules)); From 316e912ce9ab2342ada9b7945975c35071dafb8a Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Thu, 17 Feb 2022 15:18:03 -0800 Subject: [PATCH 099/187] Fix crash in initializer wrapping with trailing comma --- .../InitializerExpressionWrappingTests.cs | 42 +++++++++++++++++++ .../SeparatedSyntaxListCodeActionComputer.cs | 30 ++++++------- 2 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs b/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs index c7fa1ae9105cb..a50ebf963609e 100644 --- a/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs +++ b/src/EditorFeatures/CSharpTest/Wrapping/InitializerExpressionWrappingTests.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Wrapping; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Wrapping @@ -26,6 +27,18 @@ void Bar() { }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsWrapping)] + [WorkItem(59624, "https://github.com/dotnet/roslyn/issues/59624")] + public async Task TestNoWrappingSuggestions_TrailingComma() + { + await TestMissingAsync( +@"class C { + void Bar() { + var test = new[] [||]{ 1, }; + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsWrapping)] public async Task TestWrappingShortInitializerExpression() { @@ -54,6 +67,35 @@ void Bar() { }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsWrapping)] + [WorkItem(59624, "https://github.com/dotnet/roslyn/issues/59624")] + public async Task TestWrappingShortInitializerExpression_TrailingComma() + { + await TestAllWrappingCasesAsync( +@"class C { + void Bar() { + var test = new[] [||]{ 1, 2, }; + } +}", +@"class C { + void Bar() { + var test = new[] + { + 1, + 2, + }; + } +}", +@"class C { + void Bar() { + var test = new[] + { + 1, 2, + }; + } +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsWrapping)] public async Task TestWrappingLongIntializerExpression() { diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs index cd570ed0e0433..5ac92ee692845 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -200,7 +198,9 @@ private ImmutableArray GetUnwrapAllEdits(WrappingStyle wrappingStyle) result.Add(Edit.DeleteBetween(comma, comma.GetNextToken())); } - result.Add(Edit.DeleteBetween(_listItems.Last(), _listSyntax.GetLastToken())); + var last = _listItems.GetWithSeparators().Last(); + if (last.IsNode) + result.Add(Edit.DeleteBetween(last, _listSyntax.GetLastToken())); return result.ToImmutable(); } @@ -282,7 +282,7 @@ private ImmutableArray GetWrapLongLinesEdits( for (var i = 0; i < itemsAndSeparators.Count; i += 2) { - var item = itemsAndSeparators[i].AsNode(); + var item = itemsAndSeparators[i].AsNode()!; // Figure out where we'd be after this item. currentOffset += item.Span.Length; @@ -318,11 +318,11 @@ private ImmutableArray GetWrapLongLinesEdits( if (this.Wrapper.ShouldMoveCloseBraceToNewLine) { - result.Add(Edit.UpdateBetween(_listItems.Last(), NewLineTrivia, _braceIndentationTrivia, _listSyntax.GetLastToken())); + result.Add(Edit.UpdateBetween(itemsAndSeparators.Last(), NewLineTrivia, _braceIndentationTrivia, _listSyntax.GetLastToken())); } else { - result.Add(Edit.DeleteBetween(_listItems.Last(), _listSyntax.GetLastToken())); + result.Add(Edit.DeleteBetween(itemsAndSeparators.Last(), _listSyntax.GetLastToken())); } return result.ToImmutable(); @@ -403,29 +403,29 @@ private ImmutableArray GetWrapEachEdits( var itemsAndSeparators = _listItems.GetWithSeparators(); - for (var i = 0; i < itemsAndSeparators.Count; i += 2) + for (var i = 1; i < itemsAndSeparators.Count; i += 2) { - var item = itemsAndSeparators[i].AsNode(); + var comma = itemsAndSeparators[i].AsToken(); + + var item = itemsAndSeparators[i - 1]; + result.Add(Edit.DeleteBetween(item, comma)); + if (i < itemsAndSeparators.Count - 1) { - // intermediary item - var comma = itemsAndSeparators[i + 1].AsToken(); - result.Add(Edit.DeleteBetween(item, comma)); - // Always wrap between this comma and the next item. result.Add(Edit.UpdateBetween( - comma, NewLineTrivia, indentationTrivia, itemsAndSeparators[i + 2])); + comma, NewLineTrivia, indentationTrivia, itemsAndSeparators[i + 1])); } } if (_shouldMoveCloseBraceToNewLine) { - result.Add(Edit.UpdateBetween(_listItems.Last(), NewLineTrivia, _braceIndentationTrivia, _listSyntax.GetLastToken())); + result.Add(Edit.UpdateBetween(itemsAndSeparators.Last(), NewLineTrivia, _braceIndentationTrivia, _listSyntax.GetLastToken())); } else { // last item. Delete whatever is between it and the close token of the list. - result.Add(Edit.DeleteBetween(_listItems.Last(), _listSyntax.GetLastToken())); + result.Add(Edit.DeleteBetween(itemsAndSeparators.Last(), _listSyntax.GetLastToken())); } return result.ToImmutable(); From 46b782f068c321351d5956f411567e7e0b76aba2 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Thu, 17 Feb 2022 16:15:20 -0800 Subject: [PATCH 100/187] Fix flaky test This test was trying to assert that we didn't do a reparse by comparing the green nodes of the two trees. We didn't hold onto the root of the old tree, so it was possible for the root to fall away if we were testing recoverable trees. If the root had fallen away, it meant that asking for the old root would recover it as well, which would result in deserializing the roots twice and the green nodes being different. The fix is to ensure the root doesn't fall away. --- src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index 7f08cb81dddcf..ddffd2b8293ca 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs @@ -787,6 +787,11 @@ public async Task ChangingPreprocessorDirectivesMayReparse(string source, bool e var oldTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); + // Hold onto the old root, so we don't actually release the root; if the root were to fall away + // we're unable to use IsIncrementallyIdenticalTo to see if we didn't reparse, since asking for + // the old root will recover the tree and produce a new green node. + var oldRoot = oldTree.GetRoot(); + Assert.Equal(document.Project.ParseOptions, oldTree.Options); ParseOptions newOptions = @@ -799,7 +804,7 @@ public async Task ChangingPreprocessorDirectivesMayReparse(string source, bool e Assert.Equal(document.Project.ParseOptions, newTree.Options); - Assert.Equal(expectReuse, oldTree.GetRoot().IsIncrementallyIdenticalTo(newTree.GetRoot())); + Assert.Equal(expectReuse, oldRoot.IsIncrementallyIdenticalTo(newTree.GetRoot())); } [Fact] From 36dc033b65f48a3538c572d7bf45eec580114be6 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Thu, 17 Feb 2022 17:00:29 -0800 Subject: [PATCH 101/187] Print method name and hexadecimal offsets in ILVerify output (#59583) --- .../Test/Core/CompilationVerifier.cs | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index 1ed7b012db3a8..c76f113040c23 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -10,6 +10,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Xml.Linq; @@ -323,15 +324,27 @@ private void ILVerify(Verification verification) if (errorCount > 0) { - return (false, printVerificationResult(result)); + var metadataReader = mainModule.GetMetadataReader(); + return (false, printVerificationResult(result, metadataReader)); } return (true, string.Empty); } - static string printVerificationResult(IEnumerable result) + static string printVerificationResult(IEnumerable result, MetadataReader metadataReader) { - return string.Join("\r\n", result.Select(r => r.Message + printErrorArguments(r.ErrorArguments))); + return string.Join("\r\n", result.Select(r => printMethod(r.Method, metadataReader) + r.Message + printErrorArguments(r.ErrorArguments))); + } + + static string printMethod(MethodDefinitionHandle method, MetadataReader metadataReader) + { + if (method.IsNil) + { + return ""; + } + + var methodName = metadataReader.GetString(metadataReader.GetMethodDefinition(method).Name); + return $"[{methodName}]: "; } static string printErrorArguments(ILVerify.ErrorArgument[] errorArguments) @@ -345,7 +358,7 @@ static string printErrorArguments(ILVerify.ErrorArgument[] errorArguments) var pooledBuilder = PooledStringBuilder.GetInstance(); var builder = pooledBuilder.Builder; builder.Append(" { "); - var x = errorArguments.Select(a => a.Name + " = " + a.Value.ToString()).ToArray(); + var x = errorArguments.Select(a => printErrorArgument(a)).ToArray(); for (int i = 0; i < x.Length; i++) { if (i > 0) @@ -358,6 +371,23 @@ static string printErrorArguments(ILVerify.ErrorArgument[] errorArguments) return pooledBuilder.ToStringAndFree(); } + + static string printErrorArgument(ILVerify.ErrorArgument errorArgument) + { + var name = errorArgument.Name; + + string value; + if (name == "Offset" && errorArgument.Value is int i) + { + value = "0x" + Convert.ToString(i, 16); + } + else + { + value = errorArgument.Value.ToString(); + } + + return name + " = " + value; + } } // TODO(tomat): Fold into CompileAndVerify. From 6352f238e3a304c07615e5e7c2f0869b421ef218 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 18 Feb 2022 19:47:27 +1100 Subject: [PATCH 102/187] Format list patterns (#57568) --- .../AutomaticBracketCompletionTests.cs | 23 +- .../SmartIndenterEnterOnTokenTests.cs | 4 +- ...actCurlyBraceOrBracketCompletionService.cs | 268 ++++++++++++++++++ .../BracketBraceCompletionService.cs | 50 +++- .../CurlyBraceCompletionService.cs | 253 +---------------- .../Formatting/TypingFormattingRule.cs | 2 +- .../CSharp/CSharpAutomaticBraceCompletion.cs | 4 +- .../CSharp/Extensions/SyntaxNodeExtensions.cs | 3 +- .../CSharp/Formatting/FormattingHelpers.cs | 24 +- .../Formatting/Rules/BaseFormattingRule.cs | 2 +- .../Rules/IndentBlockFormattingRule.cs | 22 +- .../Rules/IndentUserSettingsFormattingRule.cs | 2 +- .../Rules/WrappingFormattingRule.cs | 2 +- 13 files changed, 391 insertions(+), 268 deletions(-) create mode 100644 src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs index 8f3f2fc8b7b83..8aaf2260a4842 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBracketCompletionTests.cs @@ -258,7 +258,16 @@ class C { void M(object o) { - _ = o is $$ + _ = o is$$ + } +} +"; + var expectedBeforeReturn = @" +class C +{ + void M(object o) + { + _ = o is [] } } "; @@ -267,17 +276,17 @@ class C { void M(object o) { - _ = o is [ -] + _ = o is + [ + + ] } } "; using var session = CreateSession(code); CheckStart(session.Session); - // Open bracket probably should be moved to new line - // Close bracket probably should be aligned with open bracket - // Tracked by https://github.com/dotnet/roslyn/issues/57244 - CheckReturn(session.Session, 0, expected); + CheckText(session.Session, expectedBeforeReturn); + CheckReturn(session.Session, 12, expected); } internal static Holder CreateSession(string code) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index c2479a5ff807f..f8d087ef82fef 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -1369,12 +1369,10 @@ void Main(object o) ] } }"; - // Expected indentation probably should be 12 instead - // Tracked by https://github.com/dotnet/roslyn/issues/57244 await AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync( code, indentationLine: 7, - expectedIndentation: 8); + expectedIndentation: 12); } [Trait(Traits.Feature, Traits.Features.SmartIndent)] diff --git a/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs new file mode 100644 index 0000000000000..59f1d7906edb5 --- /dev/null +++ b/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs @@ -0,0 +1,268 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.BraceCompletion; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion +{ + internal abstract class AbstractCurlyBraceOrBracketCompletionService : AbstractBraceCompletionService + { + /// + /// Annotation used to find the closing brace location after formatting changes are applied. + /// The closing brace location is then used as the caret location. + /// + private static readonly SyntaxAnnotation s_closingBraceSyntaxAnnotation = new(nameof(s_closingBraceSyntaxAnnotation)); + + protected abstract ImmutableArray GetBraceFormattingIndentationRulesAfterReturn(IndentationOptions options); + + protected abstract int AdjustFormattingEndPoint(SourceText text, SyntaxNode root, int startPoint, int endPoint); + + public sealed override async Task GetTextChangesAfterCompletionAsync(BraceCompletionContext context, IndentationOptions options, CancellationToken cancellationToken) + { + // After the closing brace is completed we need to format the span from the opening point to the closing point. + // E.g. when the user triggers completion for an if statement ($$ is the caret location) we insert braces to get + // if (true){$$} + // We then need to format this to + // if (true) { $$} + + if (!options.AutoFormattingOptions.FormatOnCloseBrace) + { + return null; + } + + var (formattingChanges, finalCurlyBraceEnd) = await FormatTrackingSpanAsync( + context.Document, + context.OpeningPoint, + context.ClosingPoint, + // We're not trying to format the indented block here, so no need to pass in additional rules. + braceFormattingIndentationRules: ImmutableArray.Empty, + options, + cancellationToken).ConfigureAwait(false); + + if (formattingChanges.IsEmpty) + { + return null; + } + + // The caret location should be at the start of the closing brace character. + var originalText = await context.Document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var formattedText = originalText.WithChanges(formattingChanges); + var caretLocation = formattedText.Lines.GetLinePosition(finalCurlyBraceEnd - 1); + + return new BraceCompletionResult(formattingChanges, caretLocation); + } + + private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, int closingBraceEndPoint) + { + // Set the start point to the character after the opening brace. + var start = openingPosition + 1; + // Set the end point to the closing brace start character position. + var end = closingBraceEndPoint - 1; + + for (var i = start; i < end; i++) + { + if (!char.IsWhiteSpace(text[i])) + { + return false; + } + } + + return true; + } + + public sealed override async Task GetTextChangeAfterReturnAsync( + BraceCompletionContext context, + IndentationOptions options, + CancellationToken cancellationToken) + { + var document = context.Document; + var closingPoint = context.ClosingPoint; + var openingPoint = context.OpeningPoint; + var originalDocumentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + // check whether shape of the braces are what we support + // shape must be either "{|}" or "{ }". | is where caret is. otherwise, we don't do any special behavior + if (!ContainsOnlyWhitespace(originalDocumentText, openingPoint, closingPoint)) + { + return null; + } + + var openingPointLine = originalDocumentText.Lines.GetLineFromPosition(openingPoint).LineNumber; + var closingPointLine = originalDocumentText.Lines.GetLineFromPosition(closingPoint).LineNumber; + + // If there are already multiple empty lines between the braces, don't do anything. + // We need to allow a single empty line between the braces to account for razor scenarios where they insert a line. + if (closingPointLine - openingPointLine > 2) + { + return null; + } + + // If there is not already an empty line inserted between the braces, insert one. + TextChange? newLineEdit = null; + var textToFormat = originalDocumentText; + if (closingPointLine - openingPointLine == 1) + { + var newLineString = options.FormattingOptions.NewLine; + newLineEdit = new TextChange(new TextSpan(closingPoint - 1, 0), newLineString); + textToFormat = originalDocumentText.WithChanges(newLineEdit.Value); + + // Modify the closing point location to adjust for the newly inserted line. + closingPoint += newLineString.Length; + } + + // Format the text that contains the newly inserted line. + var (formattingChanges, newClosingPoint) = await FormatTrackingSpanAsync( + document.WithText(textToFormat), + openingPoint, + closingPoint, + braceFormattingIndentationRules: GetBraceFormattingIndentationRulesAfterReturn(options), + options, + cancellationToken).ConfigureAwait(false); + + closingPoint = newClosingPoint; + var formattedText = textToFormat.WithChanges(formattingChanges); + + // Get the empty line between the curly braces. + var desiredCaretLine = GetLineBetweenCurlys(closingPoint, formattedText); + Debug.Assert(desiredCaretLine.GetFirstNonWhitespacePosition() == null, "the line between the formatted braces is not empty"); + + // Set the caret position to the properly indented column in the desired line. + var newDocument = document.WithText(formattedText); + var newDocumentText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + var caretPosition = GetIndentedLinePosition(newDocument, newDocumentText, desiredCaretLine.LineNumber, cancellationToken); + + // The new line edit is calculated against the original text, d0, to get text d1. + // The formatting edits are calculated against d1 to get text d2. + // Merge the formatting and new line edits into a set of whitespace only text edits that all apply to d0. + var overallChanges = newLineEdit != null ? GetMergedChanges(newLineEdit.Value, formattingChanges, formattedText) : formattingChanges; + return new BraceCompletionResult(overallChanges, caretPosition); + + static TextLine GetLineBetweenCurlys(int closingPosition, SourceText text) + { + var closingBraceLineNumber = text.Lines.GetLineFromPosition(closingPosition - 1).LineNumber; + return text.Lines[closingBraceLineNumber - 1]; + } + + static LinePosition GetIndentedLinePosition(Document document, SourceText sourceText, int lineNumber, CancellationToken cancellationToken) + { + var indentationService = document.GetRequiredLanguageService(); + var indentation = indentationService.GetIndentation(document, lineNumber, cancellationToken); + + var baseLinePosition = sourceText.Lines.GetLinePosition(indentation.BasePosition); + var offsetOfBacePosition = baseLinePosition.Character; + var totalOffset = offsetOfBacePosition + indentation.Offset; + var indentedLinePosition = new LinePosition(lineNumber, totalOffset); + return indentedLinePosition; + } + + static ImmutableArray GetMergedChanges(TextChange newLineEdit, ImmutableArray formattingChanges, SourceText formattedText) + { + var newRanges = TextChangeRangeExtensions.Merge( + ImmutableArray.Create(newLineEdit.ToTextChangeRange()), + formattingChanges.SelectAsArray(f => f.ToTextChangeRange())); + + using var _ = ArrayBuilder.GetInstance(out var mergedChanges); + var amountToShift = 0; + foreach (var newRange in newRanges) + { + var newTextChangeSpan = newRange.Span; + // Get the text to put in the text change by looking at the span in the formatted text. + // As the new range start is relative to the original text, we need to adjust it assuming the previous changes were applied + // to get the correct start location in the formatted text. + // E.g. with changes + // 1. Insert "hello" at 2 + // 2. Insert "goodbye" at 3 + // "goodbye" is after "hello" at location 3 + 5 (length of "hello") in the new text. + var newTextChangeText = formattedText.GetSubText(new TextSpan(newRange.Span.Start + amountToShift, newRange.NewLength)).ToString(); + amountToShift += (newRange.NewLength - newRange.Span.Length); + mergedChanges.Add(new TextChange(newTextChangeSpan, newTextChangeText)); + } + + return mergedChanges.ToImmutable(); + } + } + + /// + /// Formats the span between the opening and closing points, options permitting. + /// Returns the text changes that should be applied to the input document to + /// get the formatted text and the end of the close curly brace in the formatted text. + /// + private async Task<(ImmutableArray textChanges, int finalBraceEnd)> FormatTrackingSpanAsync( + Document document, + int openingPoint, + int closingPoint, + ImmutableArray braceFormattingIndentationRules, + IndentationOptions options, + CancellationToken cancellationToken) + { + // Annotate the original closing brace so we can find it after formatting. + document = await GetDocumentWithAnnotatedClosingBraceAsync(document, closingPoint, cancellationToken).ConfigureAwait(false); + + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var startPoint = openingPoint; + var endPoint = AdjustFormattingEndPoint(text, root, startPoint, closingPoint); + + if (options.AutoFormattingOptions.IndentStyle == FormattingOptions.IndentStyle.Smart) + { + // Set the formatting start point to be the beginning of the first word to the left + // of the opening brace location. + // skip whitespace + while (startPoint >= 0 && char.IsWhiteSpace(text[startPoint])) + { + startPoint--; + } + + // skip tokens in the first word to the left. + startPoint--; + while (startPoint >= 0 && !char.IsWhiteSpace(text[startPoint])) + { + startPoint--; + } + } + + var spanToFormat = TextSpan.FromBounds(Math.Max(startPoint, 0), endPoint); + var rules = document.GetFormattingRules(spanToFormat, braceFormattingIndentationRules); + var services = document.Project.Solution.Workspace.Services; + var result = Formatter.GetFormattingResult( + root, SpecializedCollections.SingletonEnumerable(spanToFormat), services, options.FormattingOptions, rules, cancellationToken); + if (result == null) + { + return (ImmutableArray.Empty, closingPoint); + } + + var newRoot = result.GetFormattedRoot(cancellationToken); + var newClosingPoint = newRoot.GetAnnotatedTokens(s_closingBraceSyntaxAnnotation).Single().SpanStart + 1; + + var textChanges = result.GetTextChanges(cancellationToken).ToImmutableArray(); + return (textChanges, newClosingPoint); + + async Task GetDocumentWithAnnotatedClosingBraceAsync(Document document, int closingBraceEndPoint, CancellationToken cancellationToken) + { + var originalRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var closeBraceToken = originalRoot.FindToken(closingBraceEndPoint - 1); + Debug.Assert(IsValidClosingBraceToken(closeBraceToken)); + + var newCloseBraceToken = closeBraceToken.WithAdditionalAnnotations(s_closingBraceSyntaxAnnotation); + var root = originalRoot.ReplaceToken(closeBraceToken, newCloseBraceToken); + return document.WithSyntaxRoot(root); + } + } + } +} diff --git a/src/Features/CSharp/Portable/BraceCompletion/BracketBraceCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/BracketBraceCompletionService.cs index 56674ced58531..b18a0e50878fd 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/BracketBraceCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/BracketBraceCompletionService.cs @@ -3,16 +3,25 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BraceCompletion; +using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion { [Export(LanguageNames.CSharp, typeof(IBraceCompletionService)), Shared] - internal class BracketBraceCompletionService : AbstractBraceCompletionService + internal class BracketBraceCompletionService : AbstractCurlyBraceOrBracketCompletionService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -30,5 +39,44 @@ public override Task AllowOverTypeAsync(BraceCompletionContext context, Ca protected override bool IsValidOpeningBraceToken(SyntaxToken token) => token.IsKind(SyntaxKind.OpenBracketToken); protected override bool IsValidClosingBraceToken(SyntaxToken token) => token.IsKind(SyntaxKind.CloseBracketToken); + + protected override int AdjustFormattingEndPoint(SourceText text, SyntaxNode root, int startPoint, int endPoint) + => endPoint; + + protected override ImmutableArray GetBraceFormattingIndentationRulesAfterReturn(IndentationOptions options) + { + return ImmutableArray.Create(BracketCompletionFormattingRule.Instance); + } + + private sealed class BracketCompletionFormattingRule : BaseFormattingRule + { + public static readonly AbstractFormattingRule Instance = new BracketCompletionFormattingRule(); + + public override AdjustNewLinesOperation? GetAdjustNewLinesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustNewLinesOperation nextOperation) + { + if (currentToken.IsKind(SyntaxKind.OpenBracketToken) && currentToken.Parent.IsKind(SyntaxKind.ListPattern)) + { + // For list patterns we format brackets as though they are a block, so when formatting after Return + // we add a newline + return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines); + } + + return base.GetAdjustNewLinesOperation(in previousToken, in currentToken, in nextOperation); + } + + public override void AddAlignTokensOperations(List list, SyntaxNode node, in NextAlignTokensOperationAction nextOperation) + { + base.AddAlignTokensOperations(list, node, in nextOperation); + + var bracketPair = node.GetBracketPair(); + if (bracketPair.IsValidBracketOrBracePair() && node is ListPatternSyntax) + { + // For list patterns we format brackets as though they are a block, so ensure the close bracket + // is aligned with the open bracket + AddAlignIndentationOfTokensToBaseTokenOperation(list, node, bracketPair.openBracket, + SpecializedCollections.SingletonEnumerable(bracketPair.closeBracket), AlignTokensOption.AlignIndentationOfTokensToFirstTokenOfBaseTokenLine); + } + } + } } } diff --git a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs index 7939374ab92c5..41f463b88acc9 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BraceCompletion; @@ -20,7 +19,6 @@ 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; @@ -28,14 +26,8 @@ namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion { [Export(LanguageNames.CSharp, typeof(IBraceCompletionService)), Shared] - internal class CurlyBraceCompletionService : AbstractBraceCompletionService + internal class CurlyBraceCompletionService : AbstractCurlyBraceOrBracketCompletionService { - /// - /// Annotation used to find the closing brace location after formatting changes are applied. - /// The closing brace location is then used as the caret location. - /// - private static readonly SyntaxAnnotation s_closingBraceSyntaxAnnotation = new(nameof(s_closingBraceSyntaxAnnotation)); - [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CurlyBraceCompletionService() @@ -49,156 +41,6 @@ public CurlyBraceCompletionService() public override Task AllowOverTypeAsync(BraceCompletionContext context, CancellationToken cancellationToken) => AllowOverTypeInUserCodeWithValidClosingTokenAsync(context, cancellationToken); - public override async Task GetTextChangesAfterCompletionAsync(BraceCompletionContext context, IndentationOptions options, CancellationToken cancellationToken) - { - // After the closing brace is completed we need to format the span from the opening point to the closing point. - // E.g. when the user triggers completion for an if statement ($$ is the caret location) we insert braces to get - // if (true){$$} - // We then need to format this to - // if (true) { $$} - - if (!options.AutoFormattingOptions.FormatOnCloseBrace) - { - return null; - } - - var (formattingChanges, finalCurlyBraceEnd) = await FormatTrackingSpanAsync( - context.Document, - context.OpeningPoint, - context.ClosingPoint, - // We're not trying to format the indented block here, so no need to pass in additional rules. - braceFormattingIndentationRules: ImmutableArray.Empty, - options, - cancellationToken).ConfigureAwait(false); - - if (formattingChanges.IsEmpty) - { - return null; - } - - // The caret location should be at the start of the closing brace character. - var originalText = await context.Document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var formattedText = originalText.WithChanges(formattingChanges); - var caretLocation = formattedText.Lines.GetLinePosition(finalCurlyBraceEnd - 1); - - return new BraceCompletionResult(formattingChanges, caretLocation); - } - - public override async Task GetTextChangeAfterReturnAsync( - BraceCompletionContext context, - IndentationOptions options, - CancellationToken cancellationToken) - { - var document = context.Document; - var closingPoint = context.ClosingPoint; - var openingPoint = context.OpeningPoint; - var originalDocumentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - - // check whether shape of the braces are what we support - // shape must be either "{|}" or "{ }". | is where caret is. otherwise, we don't do any special behavior - if (!ContainsOnlyWhitespace(originalDocumentText, openingPoint, closingPoint)) - { - return null; - } - - var openingPointLine = originalDocumentText.Lines.GetLineFromPosition(openingPoint).LineNumber; - var closingPointLine = originalDocumentText.Lines.GetLineFromPosition(closingPoint).LineNumber; - - // If there are already multiple empty lines between the braces, don't do anything. - // We need to allow a single empty line between the braces to account for razor scenarios where they insert a line. - if (closingPointLine - openingPointLine > 2) - { - return null; - } - - // If there is not already an empty line inserted between the braces, insert one. - TextChange? newLineEdit = null; - var textToFormat = originalDocumentText; - if (closingPointLine - openingPointLine == 1) - { - var newLineString = options.FormattingOptions.NewLine; - newLineEdit = new TextChange(new TextSpan(closingPoint - 1, 0), newLineString); - textToFormat = originalDocumentText.WithChanges(newLineEdit.Value); - - // Modify the closing point location to adjust for the newly inserted line. - closingPoint += newLineString.Length; - } - - var braceFormattingIndentationRules = ImmutableArray.Create( - BraceCompletionFormattingRule.ForIndentStyle(options.AutoFormattingOptions.IndentStyle)); - - // Format the text that contains the newly inserted line. - var (formattingChanges, newClosingPoint) = await FormatTrackingSpanAsync( - document.WithText(textToFormat), - openingPoint, - closingPoint, - braceFormattingIndentationRules, - options, - cancellationToken).ConfigureAwait(false); - - closingPoint = newClosingPoint; - var formattedText = textToFormat.WithChanges(formattingChanges); - - // Get the empty line between the curly braces. - var desiredCaretLine = GetLineBetweenCurlys(closingPoint, formattedText); - Debug.Assert(desiredCaretLine.GetFirstNonWhitespacePosition() == null, "the line between the formatted braces is not empty"); - - // Set the caret position to the properly indented column in the desired line. - var newDocument = document.WithText(formattedText); - var newDocumentText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); - var caretPosition = GetIndentedLinePosition(newDocument, newDocumentText, desiredCaretLine.LineNumber, cancellationToken); - - // The new line edit is calculated against the original text, d0, to get text d1. - // The formatting edits are calculated against d1 to get text d2. - // Merge the formatting and new line edits into a set of whitespace only text edits that all apply to d0. - var overallChanges = newLineEdit != null ? GetMergedChanges(newLineEdit.Value, formattingChanges, formattedText) : formattingChanges; - return new BraceCompletionResult(overallChanges, caretPosition); - - static TextLine GetLineBetweenCurlys(int closingPosition, SourceText text) - { - var closingBraceLineNumber = text.Lines.GetLineFromPosition(closingPosition - 1).LineNumber; - return text.Lines[closingBraceLineNumber - 1]; - } - - static LinePosition GetIndentedLinePosition(Document document, SourceText sourceText, int lineNumber, CancellationToken cancellationToken) - { - var indentationService = document.GetRequiredLanguageService(); - var indentation = indentationService.GetIndentation(document, lineNumber, cancellationToken); - - var baseLinePosition = sourceText.Lines.GetLinePosition(indentation.BasePosition); - var offsetOfBacePosition = baseLinePosition.Character; - var totalOffset = offsetOfBacePosition + indentation.Offset; - var indentedLinePosition = new LinePosition(lineNumber, totalOffset); - return indentedLinePosition; - } - - static ImmutableArray GetMergedChanges(TextChange newLineEdit, ImmutableArray formattingChanges, SourceText formattedText) - { - var newRanges = TextChangeRangeExtensions.Merge( - ImmutableArray.Create(newLineEdit.ToTextChangeRange()), - formattingChanges.SelectAsArray(f => f.ToTextChangeRange())); - - using var _ = ArrayBuilder.GetInstance(out var mergedChanges); - var amountToShift = 0; - foreach (var newRange in newRanges) - { - var newTextChangeSpan = newRange.Span; - // Get the text to put in the text change by looking at the span in the formatted text. - // As the new range start is relative to the original text, we need to adjust it assuming the previous changes were applied - // to get the correct start location in the formatted text. - // E.g. with changes - // 1. Insert "hello" at 2 - // 2. Insert "goodbye" at 3 - // "goodbye" is after "hello" at location 3 + 5 (length of "hello") in the new text. - var newTextChangeText = formattedText.GetSubText(new TextSpan(newRange.Span.Start + amountToShift, newRange.NewLength)).ToString(); - amountToShift += (newRange.NewLength - newRange.Span.Length); - mergedChanges.Add(new TextChange(newTextChangeSpan, newTextChangeText)); - } - - return mergedChanges.ToImmutable(); - } - } - public override async Task CanProvideBraceCompletionAsync(char brace, int openingPosition, Document document, CancellationToken cancellationToken) { // Only potentially valid for curly brace completion if not in an interpolation brace completion context. @@ -216,46 +58,8 @@ protected override bool IsValidOpeningBraceToken(SyntaxToken token) protected override bool IsValidClosingBraceToken(SyntaxToken token) => token.IsKind(SyntaxKind.CloseBraceToken); - private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, int closingBraceEndPoint) - { - // Set the start point to the character after the opening brace. - var start = openingPosition + 1; - // Set the end point to the closing brace start character position. - var end = closingBraceEndPoint - 1; - - for (var i = start; i < end; i++) - { - if (!char.IsWhiteSpace(text[i])) - { - return false; - } - } - - return true; - } - - /// - /// Formats the span between the opening and closing points, options permitting. - /// Returns the text changes that should be applied to the input document to - /// get the formatted text and the end of the close curly brace in the formatted text. - /// - private static async Task<(ImmutableArray textChanges, int finalCurlyBraceEnd)> FormatTrackingSpanAsync( - Document document, - int openingPoint, - int closingPoint, - ImmutableArray braceFormattingIndentationRules, - IndentationOptions options, - CancellationToken cancellationToken) + protected override int AdjustFormattingEndPoint(SourceText text, SyntaxNode root, int startPoint, int endPoint) { - // Annotate the original closing brace so we can find it after formatting. - document = await GetDocumentWithAnnotatedClosingBraceAsync(document, closingPoint, cancellationToken).ConfigureAwait(false); - - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var startPoint = openingPoint; - var endPoint = closingPoint; - - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - // Only format outside of the completed braces if they're on the same line for array/collection/object initializer expressions. // Example: `var x = new int[]{}`: // Correct: `var x = new int[] {}` @@ -265,7 +69,7 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, if (text.Lines.GetLineFromPosition(startPoint) == text.Lines.GetLineFromPosition(endPoint)) { var startToken = root.FindToken(startPoint, findInsideTrivia: true); - if (startToken.IsKind(SyntaxKind.OpenBraceToken) && + if (IsValidOpeningBraceToken(startToken) && (startToken.Parent?.IsInitializerForArrayOrCollectionCreationExpression() == true || startToken.Parent is AnonymousObjectCreationExpressionSyntax)) { @@ -274,50 +78,13 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, } } - if (options.AutoFormattingOptions.IndentStyle == FormattingOptions.IndentStyle.Smart) - { - // Set the formatting start point to be the beginning of the first word to the left - // of the opening brace location. - // skip whitespace - while (startPoint >= 0 && char.IsWhiteSpace(text[startPoint])) - { - startPoint--; - } - - // skip tokens in the first word to the left. - startPoint--; - while (startPoint >= 0 && !char.IsWhiteSpace(text[startPoint])) - { - startPoint--; - } - } - - var spanToFormat = TextSpan.FromBounds(Math.Max(startPoint, 0), endPoint); - var rules = document.GetFormattingRules(spanToFormat, braceFormattingIndentationRules); - var services = document.Project.Solution.Workspace.Services; - var result = Formatter.GetFormattingResult( - root, SpecializedCollections.SingletonEnumerable(spanToFormat), services, options.FormattingOptions, rules, cancellationToken); - if (result == null) - { - return (ImmutableArray.Empty, closingPoint); - } - - var newRoot = result.GetFormattedRoot(cancellationToken); - var newClosingPoint = newRoot.GetAnnotatedTokens(s_closingBraceSyntaxAnnotation).Single().SpanStart + 1; - - var textChanges = result.GetTextChanges(cancellationToken).ToImmutableArray(); - return (textChanges, newClosingPoint); - - static async Task GetDocumentWithAnnotatedClosingBraceAsync(Document document, int closingBraceEndPoint, CancellationToken cancellationToken) - { - var originalRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var closeBraceToken = originalRoot.FindToken(closingBraceEndPoint - 1); - Debug.Assert(closeBraceToken.IsKind(SyntaxKind.CloseBraceToken)); + return endPoint; + } - var newCloseBraceToken = closeBraceToken.WithAdditionalAnnotations(s_closingBraceSyntaxAnnotation); - var root = originalRoot.ReplaceToken(closeBraceToken, newCloseBraceToken); - return document.WithSyntaxRoot(root); - } + protected override ImmutableArray GetBraceFormattingIndentationRulesAfterReturn(IndentationOptions options) + { + var indentStyle = options.AutoFormattingOptions.IndentStyle; + return ImmutableArray.Create(BraceCompletionFormattingRule.ForIndentStyle(indentStyle)); } private sealed class BraceCompletionFormattingRule : BaseFormattingRule @@ -486,7 +253,7 @@ public override void AddAlignTokensOperations(List list, S if (_indentStyle == FormattingOptions.IndentStyle.Block) { var bracePair = node.GetBracePair(); - if (bracePair.IsValidBracePair()) + if (bracePair.IsValidBracketOrBracePair()) { // If the user has set block style indentation and we're in a valid brace pair // then make sure we align the close brace to the open brace. diff --git a/src/Features/CSharp/Portable/Formatting/TypingFormattingRule.cs b/src/Features/CSharp/Portable/Formatting/TypingFormattingRule.cs index 811799d5ed05f..f9e6ff90ed455 100644 --- a/src/Features/CSharp/Portable/Formatting/TypingFormattingRule.cs +++ b/src/Features/CSharp/Portable/Formatting/TypingFormattingRule.cs @@ -26,7 +26,7 @@ public override void AddSuppressOperations(List list, SyntaxN private static bool TryAddSuppressionOnMissingCloseBraceCase(List list, SyntaxNode node) { var bracePair = node.GetBracePair(); - if (!bracePair.IsValidBracePair()) + if (!bracePair.IsValidBracketOrBracePair()) { return false; } diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs index 85a6345d1b2fd..326429ff2813b 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpAutomaticBraceCompletion.cs @@ -280,7 +280,7 @@ class C { VisualStudio.Workspace.SetTriggerCompletionInArgumentLists(showCompletionInArgumentLists); VisualStudio.Editor.SendKeys("int ["); - VisualStudio.Editor.Verify.CurrentLineText("int [$$]", assertCaretPosition: true); + VisualStudio.Editor.Verify.CurrentLineText("int[$$]", assertCaretPosition: true); } [WpfTheory, CombinatorialData, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)] @@ -294,7 +294,7 @@ class C { VisualStudio.Workspace.SetTriggerCompletionInArgumentLists(showCompletionInArgumentLists); VisualStudio.Editor.SendKeys("int [", ']'); - VisualStudio.Editor.Verify.CurrentLineText("int []$$", assertCaretPosition: true); + VisualStudio.Editor.Verify.CurrentLineText(" int[]$$ ", assertCaretPosition: true, trimWhitespace: false); } [WpfTheory, CombinatorialData, Trait(Traits.Feature, Traits.Features.AutomaticCompletion)] diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs index f107931667ad8..301c54d8bf83a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs @@ -942,7 +942,7 @@ public static (SyntaxToken openParen, SyntaxToken closeParen) GetParentheses(thi } } - public static (SyntaxToken openBracket, SyntaxToken closeBracket) GetBrackets(this SyntaxNode node) + public static (SyntaxToken openBracket, SyntaxToken closeBracket) GetBrackets(this SyntaxNode? node) { switch (node) { @@ -951,6 +951,7 @@ public static (SyntaxToken openBracket, SyntaxToken closeBracket) GetBrackets(th case ImplicitArrayCreationExpressionSyntax n: return (n.OpenBracketToken, n.CloseBracketToken); case AttributeListSyntax n: return (n.OpenBracketToken, n.CloseBracketToken); case BracketedParameterListSyntax n: return (n.OpenBracketToken, n.CloseBracketToken); + case ListPatternSyntax n: return (n.OpenBracketToken, n.CloseBracketToken); default: return default; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs index 31d9def066e6a..3986902727010 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/FormattingHelpers.cs @@ -48,17 +48,29 @@ public static string ContentBeforeLastNewLine(this IEnumerable tri public static (SyntaxToken openBrace, SyntaxToken closeBrace) GetBracePair(this SyntaxNode? node) => node.GetBraces(); - public static bool IsValidBracePair(this (SyntaxToken openBrace, SyntaxToken closeBrace) bracePair) + public static (SyntaxToken openBracket, SyntaxToken closeBracket) GetBracketPair(this SyntaxNode? node) + => node.GetBrackets(); + + public static bool IsValidBracketOrBracePair(this (SyntaxToken openBracketOrBrace, SyntaxToken closeBracketOrBrace) bracketOrBracePair) { - if (bracePair.openBrace.IsKind(SyntaxKind.None) || - bracePair.openBrace.IsMissing || - bracePair.closeBrace.IsKind(SyntaxKind.None)) + if (bracketOrBracePair.openBracketOrBrace.IsKind(SyntaxKind.None) || + bracketOrBracePair.openBracketOrBrace.IsMissing || + bracketOrBracePair.closeBracketOrBrace.IsKind(SyntaxKind.None)) { return false; } - // don't check whether token is actually braces as long as it is not none. - return true; + if (bracketOrBracePair.openBracketOrBrace.IsKind(SyntaxKind.OpenBraceToken)) + { + return bracketOrBracePair.closeBracketOrBrace.IsKind(SyntaxKind.CloseBraceToken); + } + + if (bracketOrBracePair.openBracketOrBrace.IsKind(SyntaxKind.OpenBracketToken)) + { + return bracketOrBracePair.closeBracketOrBrace.IsKind(SyntaxKind.CloseBracketToken); + } + + return false; } public static bool IsOpenParenInParameterListOfAConversionOperatorDeclaration(this SyntaxToken token) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs index 3d8136fbabfd5..049fd7788603a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/BaseFormattingRule.cs @@ -161,7 +161,7 @@ protected static AdjustSpacesOperation CreateAdjustSpacesOperation(int space, Ad protected static void AddBraceSuppressOperations(List list, SyntaxNode node) { var bracePair = node.GetBracePair(); - if (!bracePair.IsValidBracePair()) + if (!bracePair.IsValidBracketOrBracePair()) { return; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs index 54a56ece929ff..050521624913f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs @@ -50,6 +50,8 @@ public override void AddIndentBlockOperations(List list, S AddBlockIndentationOperation(list, node); + AddBracketIndentationOperation(list, node); + AddLabelIndentationOperation(list, node); AddSwitchIndentationOperation(list, node); @@ -212,7 +214,7 @@ private void AddBlockIndentationOperation(List list, Synta var bracePair = node.GetBracePair(); // don't put block indentation operation if the block only contains label statement - if (!bracePair.IsValidBracePair()) + if (!bracePair.IsValidBracketOrBracePair()) { return; } @@ -244,6 +246,24 @@ private void AddBlockIndentationOperation(List list, Synta AddIndentBlockOperation(list, bracePair.openBrace.GetNextToken(includeZeroWidth: true), bracePair.closeBrace.GetPreviousToken(includeZeroWidth: true)); } + private static void AddBracketIndentationOperation(List list, SyntaxNode node) + { + var bracketPair = node.GetBracketPair(); + + if (!bracketPair.IsValidBracketOrBracePair()) + { + return; + } + + if (node.IsKind(SyntaxKind.ListPattern) && node.Parent != null) + { + // Brackets in list patterns are formatted like blocks, so align close bracket with open bracket + AddAlignmentBlockOperationRelativeToFirstTokenOnBaseTokenLine(list, bracketPair); + + AddIndentBlockOperation(list, bracketPair.openBracket.GetNextToken(includeZeroWidth: true), bracketPair.closeBracket.GetPreviousToken(includeZeroWidth: true)); + } + } + private static void AddAlignmentBlockOperationRelativeToFirstTokenOnBaseTokenLine(List list, (SyntaxToken openBrace, SyntaxToken closeBrace) bracePair) { var option = IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentUserSettingsFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentUserSettingsFormattingRule.cs index cd57f7e65608a..4c85966d019b6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentUserSettingsFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentUserSettingsFormattingRule.cs @@ -44,7 +44,7 @@ public override void AddIndentBlockOperations(List list, S var bracePair = node.GetBracePair(); // don't put block indentation operation if the block only contains lambda expression body block - if (node.IsLambdaBodyBlock() || !bracePair.IsValidBracePair()) + if (node.IsLambdaBodyBlock() || !bracePair.IsValidBracketOrBracePair()) { return; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs index f48b4724647e3..790927d6e3cfd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/WrappingFormattingRule.cs @@ -136,7 +136,7 @@ private static void RemoveSuppressOperationForStatementMethodDeclaration(List list, SyntaxNode node) { var bracePair = GetBracePair(node); - if (!bracePair.IsValidBracePair()) + if (!bracePair.IsValidBracketOrBracePair()) { return; } From a42e218545ee88ee80c9a33586ab135c735efd2f Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 18 Feb 2022 09:34:01 -0800 Subject: [PATCH 103/187] Adjust values for raw string syntax kinds (#59642) --- .../CSharp/Portable/PublicAPI.Unshipped.txt | 10 +++++----- src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index d367804586e73..03438ba044b02 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,8 +1,8 @@ -Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedMultiLineRawStringStartToken = 9081 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind -Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedRawStringEndToken = 9082 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind -Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedSingleLineRawStringStartToken = 9080 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind -Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineRawStringLiteralToken = 9073 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind -Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineRawStringLiteralToken = 9072 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedMultiLineRawStringStartToken = 9073 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedRawStringEndToken = 9074 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterpolatedSingleLineRawStringStartToken = 9072 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineRawStringLiteralToken = 8519 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineRawStringLiteralToken = 8518 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConstructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorInitializerSyntax? initializer, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax! body) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorDeclarationSyntax! *REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConstructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorInitializerSyntax! initializer, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax! body) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorDeclarationSyntax! static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConstructorDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorInitializerSyntax? initializer, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax? body, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConstructorDeclarationSyntax! diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index b6252d4c4d2df..dff8fd68224ac 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -491,6 +491,9 @@ public enum SyntaxKind : ushort InterpolatedStringToken = 8515, InterpolatedStringTextToken = 8517, // literal text that is part of an interpolated string + SingleLineRawStringLiteralToken = 8518, + MultiLineRawStringLiteralToken = 8519, + // trivia EndOfLineTrivia = 8539, WhitespaceTrivia = 8540, @@ -871,11 +874,8 @@ public enum SyntaxKind : ushort LineDirectivePosition = 9070, LineSpanDirectiveTrivia = 9071, - SingleLineRawStringLiteralToken = 9072, - MultiLineRawStringLiteralToken = 9073, - - InterpolatedSingleLineRawStringStartToken = 9080, // $""" - InterpolatedMultiLineRawStringStartToken = 9081, // $""" (whitespace and newline are included in the Text for this token) - InterpolatedRawStringEndToken = 9082, // """ (preceding whitespace and newline are included in the Text for this token) + InterpolatedSingleLineRawStringStartToken = 9072, // $""" + InterpolatedMultiLineRawStringStartToken = 9073, // $""" (whitespace and newline are included in the Text for this token) + InterpolatedRawStringEndToken = 9074, // """ (preceding whitespace and newline are included in the Text for this token) } } From 4423c2b75ab36627a9e8a04755ba6d4c4a2f994e Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 18 Feb 2022 10:22:05 -0800 Subject: [PATCH 104/187] Lightup embedded languages inside collections marked with StringSyntax --- .../Classification/SemanticClassifierTests.cs | 230 ++++++++++++++++++ .../AbstractLanguageDetector.cs | 32 ++- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 15 ++ .../Services/SyntaxFacts/CSharpSyntaxKinds.cs | 2 + .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 1 + .../SyntaxFacts/ISyntaxFactsExtensions.cs | 6 + .../Core/Services/SyntaxFacts/ISyntaxKinds.cs | 2 + .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 15 ++ .../SyntaxFacts/VisualBasicSyntaxKinds.vb | 2 + 9 files changed, 296 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs index 2e7e57b3eaf49..a6a9d945b625f 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs @@ -3584,6 +3584,149 @@ void Goo() Regex.Comment("(?#comment)")); } + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ParamsArgument(TestHost testHost) + { + await TestAsync( +@" +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +class Program +{ + private void M([StringSyntax(StringSyntaxAttribute.Regex)] params string[] p) + { + } + + void Goo() + { + [|M(@""$\a(?#comment)"");|] + } +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Method("M"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ArrayArgument(TestHost testHost) + { + await TestAsync( +@" +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +class Program +{ + private void M([StringSyntax(StringSyntaxAttribute.Regex)] string[] p) + { + } + + void Goo() + { + [|M(new string[] { @""$\a(?#comment)"" });|] + } +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Method("M"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ImplicitArrayArgument(TestHost testHost) + { + await TestAsync( +@" +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +class Program +{ + private void M([StringSyntax(StringSyntaxAttribute.Regex)] string[] p) + { + } + + void Goo() + { + [|M(new[] { @""$\a(?#comment)"" });|] + } +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Method("M"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_CollectionArgument(TestHost testHost) + { + await TestAsync( +@" +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +class Program +{ + private void M([StringSyntax(StringSyntaxAttribute.Regex)] List p) + { + } + + void Goo() + { + [|M(new List { @""$\a(?#comment)"" });|] + } +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Method("M"), +Class("List"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ImplicitCollectionArgument(TestHost testHost) + { + await TestAsync( +@" +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +class Program +{ + private void M([StringSyntax(StringSyntaxAttribute.Regex)] List p) + { + } + + void Goo() + { + [|M(new() { @""$\a(?#comment)"" });|] + } +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Method("M"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + [Theory] [CombinatorialData] public async Task TestRegexOnApiWithStringSyntaxAttribute_Argument_Options(TestHost testHost) @@ -3644,6 +3787,93 @@ class Program Regex.Comment("(?#comment)")); } + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ParamsAttribute(TestHost testHost) + { + await TestAsync( +@" +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +[AttributeUsage(AttributeTargets.Field)] +class RegexTestAttribute : Attribute +{ + public RegexTestAttribute([StringSyntax(StringSyntaxAttribute.Regex)] params string[] value) { } +} + +class Program +{ + [|[RegexTest(@""$\a(?#comment)"")]|] + private string field; +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Class("RegexTest"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ArrayAttribute(TestHost testHost) + { + await TestAsync( +@" +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +[AttributeUsage(AttributeTargets.Field)] +class RegexTestAttribute : Attribute +{ + public RegexTestAttribute([StringSyntax(StringSyntaxAttribute.Regex)] string[] value) { } +} + +class Program +{ + [|[RegexTest(new string[] { @""$\a(?#comment)"" })]|] + private string field; +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Class("RegexTest"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + + [Theory] + [CombinatorialData] + public async Task TestRegexOnApiWithStringSyntaxAttribute_ImplicitArrayAttribute(TestHost testHost) + { + await TestAsync( +@" +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text.RegularExpressions; + +[AttributeUsage(AttributeTargets.Field)] +class RegexTestAttribute : Attribute +{ + public RegexTestAttribute([StringSyntax(StringSyntaxAttribute.Regex)] string[] value) { } +} + +class Program +{ + [|[RegexTest(new[] { @""$\a(?#comment)"" })]|] + private string field; +}" + EmbeddedLanguagesTestConstants.StringSyntaxAttributeCodeCSharp, +testHost, +Class("RegexTest"), +Regex.Anchor("$"), +Regex.OtherEscape("\\"), +Regex.OtherEscape("a"), +Regex.Comment("(?#comment)")); + } + [Theory] [CombinatorialData] public async Task TestIncompleteRegexLeadingToStringInsideSkippedTokensInsideADirective(TestHost testHost) diff --git a/src/Features/Core/Portable/EmbeddedLanguages/AbstractLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/AbstractLanguageDetector.cs index ca27223d98a35..2b9ace591faea 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/AbstractLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/AbstractLanguageDetector.cs @@ -138,31 +138,33 @@ private bool IsEmbeddedLanguageStringLiteralToken(SyntaxToken token, SemanticMod if (HasLanguageComment(token, syntaxFacts, out options)) return true; + // If we're a string used in a collection initializer, treat this as a lang string if the collection itself + // is properly annotated. This is for APIs that do things like DateTime.ParseExact(..., string[] formats, ...); + var container = TryFindContainer(token); + if (container is null) + return false; - var parent = syntaxFacts.WalkUpParentheses(token.Parent); - - if (syntaxFacts.IsArgument(parent.Parent)) + if (syntaxFacts.IsArgument(container.Parent)) { - var argument = parent.Parent; + var argument = container.Parent; if (IsArgumentToWellKnownAPI(token, argument, semanticModel, cancellationToken, out options)) return true; if (IsArgumentToParameterWithMatchingStringSyntaxAttribute(semanticModel, argument, cancellationToken, out options)) return true; } - else if (syntaxFacts.IsAttributeArgument(parent.Parent)) + else if (syntaxFacts.IsAttributeArgument(container.Parent)) { - var argument = parent.Parent; - if (IsArgumentToAttributeParameterWithMatchingStringSyntaxAttribute(semanticModel, argument, cancellationToken, out options)) + if (IsArgumentToAttributeParameterWithMatchingStringSyntaxAttribute(semanticModel, container.Parent, cancellationToken, out options)) return true; } else { - var statement = parent.FirstAncestorOrSelf(syntaxFacts.IsStatement); + var statement = container.FirstAncestorOrSelf(syntaxFacts.IsStatement); if (syntaxFacts.IsSimpleAssignmentStatement(statement)) { syntaxFacts.GetPartsOfAssignmentStatement(statement, out var left, out var right); - if (parent == right && + if (container == right && IsFieldOrPropertyWithMatchingStringSyntaxAttribute(semanticModel, left, cancellationToken)) { return true; @@ -173,6 +175,18 @@ private bool IsEmbeddedLanguageStringLiteralToken(SyntaxToken token, SemanticMod return false; } + private SyntaxNode? TryFindContainer(SyntaxToken token) + { + var syntaxFacts = Info.SyntaxFacts; + var node = syntaxFacts.WalkUpParentheses(token.GetRequiredParent()); + + // if we're inside some collection-like initializer, find the instance actually being created. + if (syntaxFacts.IsAnyInitializerExpression(node.Parent, out var instance)) + node = syntaxFacts.WalkUpParentheses(instance); + + return node; + } + private bool IsArgumentToAttributeParameterWithMatchingStringSyntaxAttribute( SemanticModel semanticModel, SyntaxNode argument, CancellationToken cancellationToken, out TOptions options) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 56aa6b3a05661..c56f24bdb3721 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -656,6 +656,21 @@ public bool IsMemberInitializerNamedAssignmentIdentifier( return false; } + public bool IsAnyInitializerExpression([NotNullWhen(true)] SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? creationExpression) + { + if (node is InitializerExpressionSyntax + { + Parent: BaseObjectCreationExpressionSyntax or ArrayCreationExpressionSyntax or ImplicitArrayCreationExpressionSyntax + }) + { + creationExpression = node.Parent; + return true; + } + + creationExpression = null; + return false; + } + public bool IsElementAccessExpression(SyntaxNode? node) => node.IsKind(SyntaxKind.ElementAccessExpression); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 4cb8b914f11cd..23804dbdba773 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -59,10 +59,12 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int TrueLiteralExpression => (int)SyntaxKind.TrueLiteralExpression; public int AnonymousObjectCreationExpression => (int)SyntaxKind.AnonymousObjectCreationExpression; + public int ArrayCreationExpression => (int)SyntaxKind.ArrayCreationExpression; public int AwaitExpression => (int)SyntaxKind.AwaitExpression; public int BaseExpression => (int)SyntaxKind.BaseExpression; public int ConditionalAccessExpression => (int)SyntaxKind.ConditionalAccessExpression; public int ConditionalExpression => (int)SyntaxKind.ConditionalExpression; + public int? ImplicitArrayCreationExpression => (int)SyntaxKind.ImplicitArrayCreationExpression; public int? ImplicitObjectCreationExpression => (int)SyntaxKind.ImplicitObjectCreationExpression; public int? IndexExpression => (int)SyntaxKind.IndexExpression; public int InvocationExpression => (int)SyntaxKind.InvocationExpression; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index 5ce1e17dbb0e0..62f8c92d31526 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -341,6 +341,7 @@ void GetPartsOfInterpolationExpression(SyntaxNode node, bool IsAttributeNamedArgumentIdentifier([NotNullWhen(true)] SyntaxNode? node); bool IsMemberInitializerNamedAssignmentIdentifier([NotNullWhen(true)] SyntaxNode? node); bool IsMemberInitializerNamedAssignmentIdentifier([NotNullWhen(true)] SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? initializedInstance); + bool IsAnyInitializerExpression([NotNullWhen(true)] SyntaxNode? node, [NotNullWhen(true)] out SyntaxNode? creationExpression); bool IsDirective([NotNullWhen(true)] SyntaxNode? node); bool IsStatement([NotNullWhen(true)] SyntaxNode? node); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs index 77c93f05b5acd..11c5e5b63dbd0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFactsExtensions.cs @@ -747,6 +747,9 @@ public static bool IsTrueLiteralExpression(this ISyntaxFacts syntaxFacts, [NotNu #region expressions + public static bool IsArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.ArrayCreationExpression; + public static bool IsAwaitExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.AwaitExpression; @@ -756,6 +759,9 @@ public static bool IsBaseExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen( public static bool IsConditionalAccessExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node?.RawKind == syntaxFacts.SyntaxKinds.ConditionalAccessExpression; + public static bool IsImplicitArrayCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.ImplicitArrayCreationExpression; + public static bool IsImplicitObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) => node != null && node.RawKind == syntaxFacts.SyntaxKinds.ImplicitObjectCreationExpression; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index d9f2f1a59009f..1df75b2a6b05f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -100,10 +100,12 @@ internal interface ISyntaxKinds #region expressions int AnonymousObjectCreationExpression { get; } + int ArrayCreationExpression { get; } int AwaitExpression { get; } int BaseExpression { get; } int ConditionalAccessExpression { get; } int ConditionalExpression { get; } + int? ImplicitArrayCreationExpression { get; } int? ImplicitObjectCreationExpression { get; } int? IndexExpression { get; } int InterpolatedStringExpression { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index f04538864a6f1..ddd91ac2b327c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -685,6 +685,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Return False End Function + Public Function IsAnyInitializerExpression(node As SyntaxNode, ByRef creationExpression As SyntaxNode) As Boolean Implements ISyntaxFacts.IsAnyInitializerExpression + If TypeOf node Is CollectionInitializerSyntax Then + If TypeOf node.Parent Is ArrayCreationExpressionSyntax Then + creationExpression = node.Parent + Return True + ElseIf TypeOf node.Parent Is ObjectCollectionInitializerSyntax AndAlso + TypeOf node.Parent.Parent Is ObjectCreationExpressionSyntax Then + creationExpression = node.Parent.Parent + Return True + End If + End If + + Return False + End Function + Public Function IsNameOfSubpattern(node As SyntaxNode) As Boolean Implements ISyntaxFacts.IsNameOfSubpattern Return False End Function diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index b4975250741d7..1be621e8a6d19 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -60,10 +60,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Public ReadOnly Property TrueLiteralExpression As Integer = SyntaxKind.TrueLiteralExpression Implements ISyntaxKinds.TrueLiteralExpression Public ReadOnly Property AnonymousObjectCreationExpression As Integer = SyntaxKind.AnonymousObjectCreationExpression Implements ISyntaxKinds.AnonymousObjectCreationExpression + Public ReadOnly Property ArrayCreationExpression As Integer = SyntaxKind.ArrayCreationExpression Implements ISyntaxKinds.ArrayCreationExpression Public ReadOnly Property AwaitExpression As Integer = SyntaxKind.AwaitExpression Implements ISyntaxKinds.AwaitExpression Public ReadOnly Property BaseExpression As Integer = SyntaxKind.MyBaseExpression Implements ISyntaxKinds.BaseExpression Public ReadOnly Property ConditionalAccessExpression As Integer = SyntaxKind.ConditionalAccessExpression Implements ISyntaxKinds.ConditionalAccessExpression Public ReadOnly Property ConditionalExpression As Integer = SyntaxKind.TernaryConditionalExpression Implements ISyntaxKinds.ConditionalExpression + Public ReadOnly Property ImplicitArrayCreationExpression As Integer? = Nothing Implements ISyntaxKinds.ImplicitArrayCreationExpression Public ReadOnly Property ImplicitObjectCreationExpression As Integer? = Nothing Implements ISyntaxKinds.ImplicitObjectCreationExpression Public ReadOnly Property IndexExpression As Integer? = Nothing Implements ISyntaxKinds.IndexExpression Public ReadOnly Property InvocationExpression As Integer = SyntaxKind.InvocationExpression Implements ISyntaxKinds.InvocationExpression From 8132596db8f514aff9e5eed7155586788d4671a0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 18 Feb 2022 10:34:07 -0800 Subject: [PATCH 105/187] Improve json error message in particular case --- .../Json/CSharpJsonParserTests_BasicTests.cs | 10 +++++----- .../EmbeddedLanguages/Json/JsonParser.cs | 18 +++++++++++++++++- .../Core/Portable/FeaturesResources.resx | 3 +++ .../Core/Portable/xlf/FeaturesResources.cs.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.de.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.es.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.fr.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.it.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ja.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ko.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.pl.xlf | 5 +++++ .../Portable/xlf/FeaturesResources.pt-BR.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.ru.xlf | 5 +++++ .../Core/Portable/xlf/FeaturesResources.tr.xlf | 5 +++++ .../Portable/xlf/FeaturesResources.zh-Hans.xlf | 5 +++++ .../Portable/xlf/FeaturesResources.zh-Hant.xlf | 5 +++++ 16 files changed, 90 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_BasicTests.cs b/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_BasicTests.cs index 84db8de6c6532..350f9bdec9ddd 100644 --- a/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_BasicTests.cs +++ b/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_BasicTests.cs @@ -1168,7 +1168,7 @@ public void TestMissingColon() ", @" - + ", @" @@ -1328,10 +1328,10 @@ public void TestNestedPropertyMissingColon() ", @" - + ", @" - + "); } @@ -1367,10 +1367,10 @@ public void TestMissingColon2() ", @" - + ", @" - + "); } diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonParser.cs b/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonParser.cs index 9f61c82948ba6..ba00428e86e0a 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonParser.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonParser.cs @@ -213,6 +213,7 @@ private JsonTree ParseTree(JsonOptions options) var diagnostic = node.Kind switch { JsonKind.Array => CheckArray((JsonArrayNode)node), + JsonKind.Object => CheckObject((JsonObjectNode)node), _ => null, }; @@ -246,7 +247,22 @@ private JsonTree ParseTree(JsonOptions options) } } - return CheckChildren(node); + return null; + } + + static EmbeddedDiagnostic? CheckObject(JsonObjectNode node) + { + foreach (var child in node.Sequence) + { + if (child is JsonLiteralNode { LiteralToken.Kind: JsonKind.StringToken }) + { + return new EmbeddedDiagnostic( + FeaturesResources.Property_name_must_be_followed_by_a_colon, + child.GetSpan()); + } + } + + return null; } } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 0b3f3598cc546..2ded6207f80f8 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -3082,6 +3082,9 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Property name must be a string + + Property name must be followed by a ':' + Strings must start with " not ' diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index b17d57b131e40..8292c84c09cb9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -1220,6 +1220,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated Odkaz na vlastnost se nedá aktualizovat. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index d8eba792ed050..9b9d7171f0d41 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -1220,6 +1220,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated Der Eigenschaftenverweis kann nicht aktualisiert werden. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 7833a29910cae..fca888dc734b0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -1220,6 +1220,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated No se puede actualizar la referencia de propiedad diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 7e28c69e6e8c5..1fe750400d573 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -1220,6 +1220,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated La référence de propriété ne peut pas être mise à jour diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 1b07cde1483b7..8802fa66e1cc4 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -1220,6 +1220,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated Non è possibile aggiornare il riferimento alla proprietà diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 3518e47ac51d7..ffad8467d62c0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -1220,6 +1220,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated プロパティ参照を更新できません diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index d5cd4943a0a70..49c0f7158674e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -1220,6 +1220,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated 속성 참조를 업데이트할 수 없습니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 50a84b6da2dfa..a5ad926f8fb34 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -1220,6 +1220,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated Nie można zaktualizować referencji właściwości diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index fa5d7ad61c39c..37314d6f58b0c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -1220,6 +1220,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated A referência de propriedade não pode ser atualizada diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 3476b7e7900b7..64b532d087a02 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -1220,6 +1220,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated Не удается обновить ссылку на свойство diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 1922e8a8861e3..b63f182a925fe 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -1220,6 +1220,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated Özellik başvurusu güncelleştirilemiyor diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 385cee9491017..9a4298a456241 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -1220,6 +1220,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated 无法更新属性引用 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index fdfb9cf7132b8..6e6011af9b13e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -1220,6 +1220,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Property name must be a string + + Property name must be followed by a ':' + Property name must be followed by a ':' + + Property reference cannot be updated 無法更新屬性參考 From ded237d5824d86f1ed7a92e567ca2792e4de269b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 18 Feb 2022 12:55:07 -0800 Subject: [PATCH 106/187] Update tests --- .../Json/CSharpJsonParserTests_NstTests.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs b/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs index 579cc5e8eb4b9..673578b9e7e0a 100644 --- a/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs +++ b/src/EditorFeatures/CSharpTest2/EmbeddedLanguages/Json/CSharpJsonParserTests_NstTests.cs @@ -3028,10 +3028,10 @@ public void n_object_comma_instead_of_colon_json() ", @" - + ", @" - + "); } @@ -3188,10 +3188,10 @@ public void n_object_missing_colon_json() ", @" - + ", @" - + "); } @@ -3250,10 +3250,10 @@ public void n_object_missing_semicolon_json() ", @" - + ", @" - + "); } @@ -3308,10 +3308,10 @@ public void n_object_no_colon_json() ", @" - + ", @" - + "); } @@ -3762,10 +3762,10 @@ public void n_object_with_single_string_json() ", @" - + ", @" - + "); } @@ -5299,10 +5299,10 @@ public void n_structure_open_object_string_with_apostrophes_json() ", @" - + ", @" - + "); } From 6206f0d2e60ac52d43f7b373e612e94887c40499 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Fri, 18 Feb 2022 09:48:11 -0800 Subject: [PATCH 107/187] Apply test trait to containing type --- .../Formatting/FormattingEngineTests.cs | 134 ++++++++---------- 1 file changed, 62 insertions(+), 72 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index 4d20802ff3c66..1db1098022187 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -27,6 +27,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting { + [Trait(Traits.Feature, Traits.Features.Formatting)] public class FormattingEngineTests : CSharpFormattingEngineTestBase { public FormattingEngineTests(ITestOutputHelper output) : base(output) { } @@ -43,7 +44,6 @@ private static Dictionary SmartIndentButDoNotFormatWhileTypi [WpfFact] [WorkItem(539682, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539682")] - [Trait(Traits.Feature, Traits.Features.Formatting)] public void FormatDocumentCommandHandler() { var code = @"class Program @@ -71,7 +71,6 @@ static void Main(string[] args) [WpfFact] [WorkItem(539682, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539682")] - [Trait(Traits.Feature, Traits.Features.Formatting)] public void FormatDocumentPasteCommandHandler() { var code = @"class Program @@ -99,7 +98,6 @@ static void Main(string[] args) [WpfFact] [WorkItem(547261, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/547261")] - [Trait(Traits.Feature, Traits.Features.Formatting)] public void FormatDocumentReadOnlyWorkspacePasteCommandHandler() { var code = @"class Program @@ -127,7 +125,6 @@ static void Main(string[] args) [WpfFact] [WorkItem(912965, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912965")] - [Trait(Traits.Feature, Traits.Features.Formatting)] public void DoNotFormatUsingStatementOnReturn() { var code = @"class Program @@ -155,7 +152,6 @@ static void Main(string[] args) [WpfFact] [WorkItem(912965, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912965")] - [Trait(Traits.Feature, Traits.Features.Formatting)] public void FormatUsingStatementWhenTypingCloseParen() { var code = @"class Program @@ -183,7 +179,6 @@ static void Main(string[] args) [WpfFact] [WorkItem(912965, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/912965")] - [Trait(Traits.Feature, Traits.Features.Formatting)] public void FormatNotUsingStatementOnReturn() { var code = @"class Program @@ -210,7 +205,7 @@ static void Main(string[] args) } [WorkItem(977133, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/977133")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatRangeOrFormatTokenOnOpenBraceOnSameLine() { var code = @"class C @@ -231,7 +226,7 @@ public void M() } [WorkItem(14491, "https://github.com/dotnet/roslyn/pull/14491")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatRangeButFormatTokenOnOpenBraceOnNextLine() { var code = @"class C @@ -254,7 +249,7 @@ public void M() } [WorkItem(1007071, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1007071")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatPragmaWarningInbetweenDelegateDeclarationStatement() { var code = @"using System; @@ -287,7 +282,7 @@ static void Main(string[] args) } [WorkItem(771761, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/771761")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatHashRegion() { var code = @"using System; @@ -312,7 +307,7 @@ static void Main(string[] args) } [WorkItem(771761, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/771761")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatHashEndRegion() { var code = @"using System; @@ -339,7 +334,7 @@ static void Main(string[] args) } [WorkItem(987373, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/987373")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public async Task FormatSpansIndividuallyWithoutCollapsing() { var code = @"class C @@ -432,7 +427,7 @@ public void M() } [WorkItem(1044118, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1044118")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void SemicolonInCommentOnLastLineDoesNotFormat() { var code = @"using System; @@ -459,7 +454,7 @@ static void Main(string[] args) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideSingleLineRegularComment_1() { var code = @"class Program @@ -484,7 +479,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideSingleLineRegularComment_2() { var code = @"class Program @@ -509,7 +504,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideMultiLineRegularComment_1() { var code = @"class Program @@ -532,7 +527,7 @@ static void Main(int a/* { */, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideMultiLineRegularComment_2() { var code = @"class Program @@ -557,7 +552,7 @@ static void Main(int a/* { [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideMultiLineRegularComment_3() { var code = @"class Program @@ -582,7 +577,7 @@ static void Main(int a/* { [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideSingleLineDocComment_1() { var code = @"class Program @@ -607,7 +602,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideSingleLineDocComment_2() { var code = @"class Program @@ -632,7 +627,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideMultiLineDocComment_1() { var code = @"class Program @@ -657,7 +652,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideMultiLineDocComment_2() { var code = @"class Program @@ -684,7 +679,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideMultiLineDocComment_3() { var code = @"class Program @@ -711,7 +706,7 @@ static void Main(int a, int b) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideInactiveCode() { var code = @"class Program @@ -742,7 +737,7 @@ static void Main(string[] args) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideStringLiteral() { var code = @"class Program @@ -765,7 +760,7 @@ static void Main(string[] args) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideCharLiteral() { var code = @"class Program @@ -788,7 +783,7 @@ static void Main(string[] args) [WorkItem(449, "https://github.com/dotnet/roslyn/issues/449")] [WorkItem(1077103, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1077103")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void NoFormattingInsideCommentsOfPreprocessorDirectives() { var code = @"class Program @@ -815,7 +810,7 @@ static void Main(string[] args) [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void ColonInSwitchCase() { var code = @"class Program @@ -846,7 +841,7 @@ static void Main(string[] args) [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void ColonInDefaultSwitchCase() { var code = @"class Program @@ -878,7 +873,7 @@ static void Main(string[] args) } [WorkItem(9097, "https://github.com/dotnet/roslyn/issues/9097")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void ColonInPatternSwitchCase01() { var code = @"class Program @@ -907,7 +902,7 @@ static void Main() [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void ColonInLabeledStatement() { var code = @"class Program @@ -930,7 +925,7 @@ static void Main(string[] args) [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatColonInTargetAttribute() { var code = @"using System; @@ -949,7 +944,7 @@ class C : Attribute [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatColonInBaseList() { var code = @"class C :$$ Attribute @@ -964,7 +959,7 @@ public void DoNotFormatColonInBaseList() [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatColonInThisConstructor() { var code = @"class Goo @@ -993,7 +988,7 @@ public void DoNotFormatColonInThisConstructor() [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatColonInConditionalOperator() { var code = @"class Program @@ -1016,7 +1011,7 @@ static void Main(string[] args) [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatColonInArgument() { var code = @"class Program @@ -1039,7 +1034,7 @@ static void Main(string[] args) [WorkItem(464, "https://github.com/dotnet/roslyn/issues/464")] [WorkItem(908729, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/908729")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatColonInTypeParameter() { var code = @"class Program @@ -1063,7 +1058,7 @@ class C1 } [WorkItem(2224, "https://github.com/dotnet/roslyn/issues/2224")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DontSmartFormatBracesOnSmartIndentNone() { var code = @"class Program @@ -1318,7 +1313,7 @@ public int P AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatIncompleteBlockOnSingleLineIfNotTypingCloseCurly1() { var code = @"namespace ConsoleApplication1 @@ -1342,7 +1337,7 @@ static bool Property AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatIncompleteBlockOnSingleLineIfNotTypingCloseCurly2() { var code = @"namespace ConsoleApplication1 @@ -1362,7 +1357,7 @@ class Program AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatIncompleteBlockOnSingleLineIfNotTypingCloseCurly3() { var code = @"namespace ConsoleApplication1 @@ -1382,7 +1377,7 @@ class Program AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatCompleteBlockOnSingleLineIfTypingCloseCurly1() { var code = @"namespace ConsoleApplication1 @@ -1404,7 +1399,7 @@ static bool Property AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatCompleteBlockOnSingleLineIfTypingCloseCurly2() { var code = @"namespace ConsoleApplication1 @@ -1422,7 +1417,7 @@ class Program AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatIncompleteBlockOnMultipleLinesIfTypingCloseCurly1() { var code = @"namespace ConsoleApplication1 @@ -1448,7 +1443,7 @@ static bool Property AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatIncompleteBlockOnMultipleLinesIfTypingCloseCurly2() { var code = @"namespace ConsoleApplication1 @@ -1474,7 +1469,7 @@ static bool Property AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoNotFormatCompleteBlockOnSingleLineIfTypingSemicolon() { var code = @@ -1500,7 +1495,7 @@ void M() AssertFormatAfterTypeChar(code, expected); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatCompleteBlockOnSingleLineIfTypingCloseCurlyOnLaterLine() { var code = @@ -1531,7 +1526,7 @@ void M() } [WorkItem(7900, "https://github.com/dotnet/roslyn/issues/7900")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatLockStatementWithEmbeddedStatementOnSemicolonDifferentLine() { var code = @"class C @@ -1556,7 +1551,7 @@ public void M() } [WorkItem(7900, "https://github.com/dotnet/roslyn/issues/7900")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatLockStatementWithEmbeddedStatementOnSemicolonSameLine() { var code = @"class C @@ -1579,7 +1574,7 @@ public void M() } [WorkItem(11642, "https://github.com/dotnet/roslyn/issues/11642")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatArbitraryNodeParenthesizedLambdaExpression() { // code equivalent to an expression synthesized like so: @@ -1591,7 +1586,7 @@ public void FormatArbitraryNodeParenthesizedLambdaExpression() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff1() { var code = @@ -1618,7 +1613,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff2() { var code = @@ -1645,7 +1640,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff3() { // We only smart indent the { if it's on it's own line. @@ -1671,7 +1666,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff4() { // We only smart indent the { if it's on it's own line. @@ -1697,7 +1692,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff5() { // Typing the { should not affect the formating of the preceding tokens. @@ -1725,7 +1720,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff6() { // Typing the { should not affect the formating of the preceding tokens. @@ -1751,7 +1746,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff7() { var code = @@ -1772,7 +1767,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff1() { var code = @@ -1801,7 +1796,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff2() { // Note that the { is not updated since we are not formatting. @@ -1829,7 +1824,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff3() { var code = @@ -1852,7 +1847,7 @@ void M() } [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void DoSmartIndentCloseBraceEvenWithFormatWhileTypingOff4() { // Should not affect formatting of open brace @@ -1874,7 +1869,6 @@ void M() { } [WpfFact] - [Trait(Traits.Feature, Traits.Features.Formatting)] [WorkItem(31907, "https://github.com/dotnet/roslyn/issues/31907")] public async Task NullableReferenceTypes() { @@ -1901,7 +1895,7 @@ void MyMethod() } [WorkItem(30518, "https://github.com/dotnet/roslyn/issues/30518")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void FormatGeneratedNodeInInitializer() { var code = @"new bool[] { @@ -1923,7 +1917,6 @@ public void FormatGeneratedNodeInInitializer() } [WpfFact] - [Trait(Traits.Feature, Traits.Features.Formatting)] [WorkItem(27268, "https://github.com/dotnet/roslyn/issues/27268")] public async Task PositionalPattern() { @@ -1958,7 +1951,6 @@ void MyMethod() } [WpfFact] - [Trait(Traits.Feature, Traits.Features.Formatting)] public async Task WithExpression() { var code = @"[| @@ -1984,7 +1976,6 @@ void M() } [WpfFact] - [Trait(Traits.Feature, Traits.Features.Formatting)] public async Task WithExpression_MultiLine() { var code = @"[| @@ -2018,7 +2009,6 @@ void M() } [WpfFact] - [Trait(Traits.Feature, Traits.Features.Formatting)] public async Task WithExpression_MultiLine_UserPositionedBraces() { var code = @"[| @@ -2052,7 +2042,7 @@ void M() } [WorkItem(25003, "https://github.com/dotnet/roslyn/issues/25003")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void SeparateGroups_KeepMultipleLinesBetweenGroups() { var code = @"$$ @@ -2077,7 +2067,7 @@ public void SeparateGroups_KeepMultipleLinesBetweenGroups() } [WorkItem(25003, "https://github.com/dotnet/roslyn/issues/25003")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void SeparateGroups_KeepMultipleLinesBetweenGroups_FileScopedNamespace() { var code = @"$$ @@ -2106,7 +2096,7 @@ namespace N; } [WorkItem(25003, "https://github.com/dotnet/roslyn/issues/25003")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void SeparateGroups_DoNotGroupIfNotSorted() { var code = @"$$ @@ -2127,7 +2117,7 @@ public void SeparateGroups_DoNotGroupIfNotSorted() } [WorkItem(25003, "https://github.com/dotnet/roslyn/issues/25003")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void SeparateGroups_GroupIfSorted() { var code = @"$$ @@ -2149,7 +2139,7 @@ public void SeparateGroups_GroupIfSorted() } [WorkItem(25003, "https://github.com/dotnet/roslyn/issues/25003")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WpfFact] public void SeparateGroups_GroupIfSorted_RecognizeSystemNotFirst() { var code = @"$$ From 3733cc9110f9c8195b8f3291133befabe250e0c0 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 18 Feb 2022 15:10:38 -0800 Subject: [PATCH 108/187] Fix text structure navigation for raw strings. --- ...> CSharpTextStructureNavigatorProvider.cs} | 43 +- .../TextStructureNavigatorTests.cs | 452 +++++++++--------- .../AbstractTextStructureNavigationTests.cs | 122 +++++ ...ualBasicTextStructureNavigatorProvider.vb} | 2 +- .../TextStructureNavigatorTests.vb | 2 +- 5 files changed, 390 insertions(+), 231 deletions(-) rename src/EditorFeatures/CSharp/TextStructureNavigation/{TextStructureNavigatorProvider.cs => CSharpTextStructureNavigatorProvider.cs} (64%) create mode 100644 src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs rename src/EditorFeatures/VisualBasic/TextStructureNavigation/{TextStructureNavigatorProvider.vb => VisualBasicTextStructureNavigatorProvider.vb} (98%) diff --git a/src/EditorFeatures/CSharp/TextStructureNavigation/TextStructureNavigatorProvider.cs b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs similarity index 64% rename from src/EditorFeatures/CSharp/TextStructureNavigation/TextStructureNavigatorProvider.cs rename to src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs index e55bbc6760482..8cef5deb3a2b4 100644 --- a/src/EditorFeatures/CSharp/TextStructureNavigation/TextStructureNavigatorProvider.cs +++ b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs @@ -2,13 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Implementation.TextStructureNavigation; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Text; @@ -19,11 +16,11 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.TextStructureNavigation { [Export(typeof(ITextStructureNavigatorProvider))] [ContentType(ContentTypeNames.CSharpContentType)] - internal class TextStructureNavigatorProvider : AbstractTextStructureNavigatorProvider + internal class CSharpTextStructureNavigatorProvider : AbstractTextStructureNavigatorProvider { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TextStructureNavigatorProvider( + public CSharpTextStructureNavigatorProvider( ITextStructureNavigatorSelectorService selectorService, IContentTypeRegistryService contentTypeService, IUIThreadOperationExecutor uIThreadOperationExecutor) @@ -41,13 +38,17 @@ protected override bool IsWithinNaturalLanguage(SyntaxToken token, int position) case SyntaxKind.StringLiteralToken: // This, in combination with the override of GetExtentOfWordFromToken() below, treats the closing // quote as a separate token. This maintains behavior with VS2013. - if (position == token.Span.End - 1 && token.Text.EndsWith("\"", StringComparison.Ordinal)) + return !IsAtClosingQuote(token, position); + + case SyntaxKind.SingleLineRawStringLiteralToken: + case SyntaxKind.MultiLineRawStringLiteralToken: { - return false; + // Like with normal string literals, treat the closing quotes as as the end of the string so that + // navigation ends there and doesn't go past them. + var end = GetStartOfRawStringLiteralEndDelimiter(token); + return position < end; } - return true; - case SyntaxKind.CharacterLiteralToken: // Before the ' is considered outside the character return position != token.SpanStart; @@ -60,9 +61,26 @@ protected override bool IsWithinNaturalLanguage(SyntaxToken token, int position) return false; } + private static int GetStartOfRawStringLiteralEndDelimiter(SyntaxToken token) + { + var text = token.ToString(); + var start = 0; + var end = text.Length; + while (start < end && text[start] == '"') + start++; + + while (end > start && text[end - 1] == '"') + end--; + + return token.SpanStart + end; + } + + private static bool IsAtClosingQuote(SyntaxToken token, int position) + => position == token.Span.End - 1 && token.Text[^1] == '"'; + protected override TextExtent GetExtentOfWordFromToken(SyntaxToken token, SnapshotPoint position) { - if (token.Kind() == SyntaxKind.StringLiteralToken && position.Position == token.Span.End - 1 && token.Text.EndsWith("\"", StringComparison.Ordinal)) + if (token.Kind() == SyntaxKind.StringLiteralToken && IsAtClosingQuote(token, position.Position)) { // Special case to treat the closing quote of a string literal as a separate token. This allows the // cursor to stop during word navigation (Ctrl+LeftArrow, etc.) immediately before AND after the @@ -70,6 +88,11 @@ protected override TextExtent GetExtentOfWordFromToken(SyntaxToken token, Snapsh var span = new Span(position.Position, 1); return new TextExtent(new SnapshotSpan(position.Snapshot, span), isSignificant: true); } + else if (token.Kind() is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) + { + var delimiterStart = GetStartOfRawStringLiteralEndDelimiter(token); + return new TextExtent(new SnapshotSpan(position.Snapshot, Span.FromBounds(delimiterStart, token.Span.End)), isSignificant: true); + } else { return base.GetExtentOfWordFromToken(token, position); diff --git a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs index de37907b4c725..7165b41a1fb42 100644 --- a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs +++ b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.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 Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editor.CSharp.TextStructureNavigation; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities.TextStructureNavigation; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; using Roslyn.Test.Utilities; @@ -18,356 +17,371 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.TextStructureNavigation { [UseExportProvider] - public class TextStructureNavigatorTests + public class TextStructureNavigatorTests : AbstractTextStructureNavigatorTests { + protected override string ContentType => ContentTypeNames.CSharpContentType; + + protected override TestWorkspace CreateWorkspace(string code) + => TestWorkspace.CreateCSharp(code); + [Fact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void Empty() { - AssertExtent( - string.Empty, - pos: 0, - isSignificant: false, - start: 0, length: 0); + AssertExtent("$${|Insignificant:|}"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void Whitespace() { AssertExtent( - " ", - pos: 0, - isSignificant: false, - start: 0, length: 3); + "$${|Insignificant: |}"); AssertExtent( - " ", - pos: 1, - isSignificant: false, - start: 0, length: 3); + "{|Insignificant: $$ |}"); AssertExtent( - " ", - pos: 3, - isSignificant: false, - start: 0, length: 3); + "{|Insignificant: $$|}"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void EndOfFile() { AssertExtent( - "using System;", - pos: 13, - isSignificant: true, - start: 12, length: 1); + "using System{|Significant:;|}$$"); } [Fact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void NewLine() { AssertExtent( - "class Class1 {\r\n\r\n}", - pos: 14, - isSignificant: false, - start: 14, length: 2); - - AssertExtent( - "class Class1 {\r\n\r\n}", - pos: 15, - isSignificant: false, - start: 14, length: 2); - + "class Class1 {$${|Insignificant:\r\n|}\r\n}"); AssertExtent( - "class Class1 {\r\n\r\n}", - pos: 16, - isSignificant: false, - start: 16, length: 2); - - AssertExtent( - "class Class1 {\r\n\r\n}", - pos: 17, - isSignificant: false, - start: 16, length: 2); + "class Class1 {\r\n$${|Insignificant:\r\n|}}"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void SingleLineComment() { AssertExtent( - "// Comment ", - pos: 0, - isSignificant: true, - start: 0, length: 12); + "$${|Significant:// Comment |}"); // It is important that this returns just the comment banner. Returning the whole comment // means Ctrl+Right before the slash will cause it to jump across the entire comment AssertExtent( - "// Comment ", - pos: 1, - isSignificant: true, - start: 0, length: 2); + "{|Significant:/$$/|} Comment "); AssertExtent( - "// Comment ", - pos: 5, - isSignificant: true, - start: 3, length: 7); + "// {|Significant:Co$$mment|} "); AssertExtent( - "// () test", - pos: 4, - isSignificant: true, - start: 3, length: 2); + "// {|Significant:($$)|} test"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void MultiLineComment() { AssertExtent( - "/* Comment */", - pos: 0, - isSignificant: true, - start: 0, length: 13); + @"{|Significant:$$/* Comment */|}"); // It is important that this returns just the comment banner. Returning the whole comment // means Ctrl+Right before the slash will cause it to jump across the entire comment AssertExtent( - "/* Comment */", - pos: 1, - isSignificant: true, - start: 0, length: 2); + @"{|Significant:/$$*|} Comment */"); AssertExtent( - "/* Comment */", - pos: 5, - isSignificant: true, - start: 3, length: 7); + @"/* {|Significant:Co$$mment|} */"); AssertExtent( - "/* () test */", - pos: 4, - isSignificant: true, - start: 3, length: 2); + @"/* {|Significant:($$)|} test */"); AssertExtent( - "/* () test */", - pos: 11, - isSignificant: true, - start: 11, length: 2); + @"/* () test {|Significant:$$*/|}"); // It is important that this returns just the comment banner. Returning the whole comment // means Ctrl+Left after the slash will cause it to jump across the entire comment AssertExtent( - "/* () test */", - pos: 12, - isSignificant: true, - start: 11, length: 2); + @"/* () test {|Significant:*$$/|}"); AssertExtent( - "/* () test */", - pos: 13, - isSignificant: true, - start: 11, length: 2); + @"/* () test {|Significant:*/|}$$"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void Keyword() { - for (var i = 7; i <= 7 + 4; i++) - { - AssertExtent( - "public class Class1", - pos: i, - isSignificant: true, - start: 7, length: 5); - } + AssertExtent( + @"public {|Significant:$$class|} Class1"); + + AssertExtent( + @"public {|Significant:c$$lass|} Class1"); + + AssertExtent( + @"public {|Significant:cl$$ass|} Class1"); + + AssertExtent( + @"public {|Significant:cla$$ss|} Class1"); + + AssertExtent( + @"public {|Significant:clas$$s|} Class1"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void Identifier() { - for (var i = 13; i <= 13 + 8; i++) - { - AssertExtent( - "public class SomeClass : IDisposable", - pos: i, - isSignificant: true, - start: 13, length: 9); - } + AssertExtent( + @"public class {|Significant:$$SomeClass|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:S$$omeClass|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:So$$meClass|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:Som$$eClass|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:Some$$Class|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:SomeC$$lass|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:SomeCl$$ass|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:SomeCla$$ss|} : IDisposable"); + + AssertExtent( + @"public class {|Significant:SomeClas$$s|} : IDisposable"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void EscapedIdentifier() { - for (var i = 12; i <= 12 + 9; i++) - { - AssertExtent( - "public enum @interface : int", - pos: i, - isSignificant: true, - start: 12, length: 10); - } + AssertExtent( + @"public enum {|Significant:$$@interface|} : int"); + + AssertExtent( + @"public enum {|Significant:@$$interface|} : int"); + + AssertExtent( + @"public enum {|Significant:@i$$nterface|} : int"); + + AssertExtent( + @"public enum {|Significant:@in$$terface|} : int"); + + AssertExtent( + @"public enum {|Significant:@int$$erface|} : int"); + + AssertExtent( + @"public enum {|Significant:@inte$$rface|} : int"); + + AssertExtent( + @"public enum {|Significant:@inter$$face|} : int"); + + AssertExtent( + @"public enum {|Significant:@interf$$ace|} : int"); + + AssertExtent( + @"public enum {|Significant:@interfa$$ce|} : int"); + + AssertExtent( + @"public enum {|Significant:@interfac$$e|} : int"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void Number() { - for (var i = 37; i <= 37 + 10; i++) - { - AssertExtent( - "class Test { private double num = -1.234678e10; }", - pos: i, - isSignificant: true, - start: 37, length: 11); - } + AssertExtent( + @"class Test { private double num = -{|Significant:$$1.234678e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1$$.234678e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.$$234678e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.2$$34678e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.23$$4678e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.234$$678e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.2346$$78e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.23467$$8e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.234678$$e10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.234678e$$10|}; }"); + + AssertExtent( + @"class Test { private double num = -{|Significant:1.234678e1$$0|}; }"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void String() { - const string TestString = "class Test { private string s1 = \" () test \"; }"; - var startOfString = TestString.IndexOf('"'); - var lengthOfStringIncludingQuotes = TestString.LastIndexOf('"') - startOfString + 1; - AssertExtent( - TestString, - pos: startOfString, - isSignificant: true, - start: startOfString, length: 1); + @"class Test { private string s1 = {|Significant:$$""|} () test ""; }"); - // Selects whitespace AssertExtent( - TestString, - pos: startOfString + 1, - isSignificant: false, - start: startOfString + 1, length: 1); + @"class Test { private string s1 = ""{|Insignificant:$$ |}() test ""; }"); AssertExtent( - TestString, - pos: startOfString + 2, - isSignificant: true, - start: startOfString + 2, length: 2); + @"class Test { private string s1 = "" {|Significant:$$()|} test ""; }"); AssertExtent( - TestString, - pos: TestString.IndexOf(" \"", StringComparison.Ordinal), - isSignificant: false, - start: TestString.IndexOf(" \"", StringComparison.Ordinal), length: 2); + @"class Test { private string s1 = "" () test{|Insignificant:$$ |}""; }"); AssertExtent( - TestString, - pos: TestString.LastIndexOf('"'), - isSignificant: true, - start: startOfString + lengthOfStringIncludingQuotes - 1, length: 1); + @"class Test { private string s1 = "" () test {|Significant:$$""|}; }"); AssertExtent( - TestString, - pos: TestString.LastIndexOf('"') + 1, - isSignificant: true, - start: TestString.LastIndexOf('"') + 1, length: 1); + @"class Test { private string s1 = "" () test ""{|Significant:$$;|} }"); } [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] public void InterpolatedString1() { - const string TestString = "class Test { string x = \"hello\"; string s = $\" { x } hello\"; }"; + AssertExtent( + @"class Test { string x = ""hello""; string s = {|Significant:$$$""|} { x } hello""; }"); - var startOfFirstString = TestString.IndexOf('"'); - var endOfFirstString = TestString.IndexOf('"', startOfFirstString + 1); - var startOfString = TestString.IndexOf("$\"", endOfFirstString + 1, StringComparison.Ordinal); + AssertExtent( + @"class Test { string x = ""hello""; string s = $""{|Insignificant:$$ |}{ x } hello""; }"); - // Selects interpolated string start token AssertExtent( - TestString, - pos: startOfString, - isSignificant: true, - start: startOfString, length: 2); + @"class Test { string x = ""hello""; string s = $"" {|Significant:$${|} x } hello""; }"); - // Selects whitespace AssertExtent( - TestString, - pos: startOfString + 2, - isSignificant: false, - start: startOfString + 2, length: 1); + @"class Test { string x = ""hello""; string s = $"" {{|Insignificant:$$ |}x } hello""; }"); - // Selects the opening curly brace AssertExtent( - TestString, - pos: startOfString + 3, - isSignificant: true, - start: startOfString + 3, length: 1); + @"class Test { string x = ""hello""; string s = $"" { {|Significant:$$x|} } hello""; }"); - // Selects whitespace AssertExtent( - TestString, - pos: startOfString + 4, - isSignificant: false, - start: startOfString + 4, length: 1); + @"class Test { string x = ""hello""; string s = $"" { x{|Insignificant:$$ |}} hello""; }"); - // Selects identifier AssertExtent( - TestString, - pos: startOfString + 5, - isSignificant: true, - start: startOfString + 5, length: 1); + @"class Test { string x = ""hello""; string s = $"" { x {|Significant:$$}|} hello""; }"); - // Selects whitespace AssertExtent( - TestString, - pos: startOfString + 6, - isSignificant: false, - start: startOfString + 6, length: 1); + @"class Test { string x = ""hello""; string s = $"" { x }{|Insignificant:$$ |}hello""; }"); - // Selects the closing curly brace AssertExtent( - TestString, - pos: startOfString + 7, - isSignificant: true, - start: startOfString + 7, length: 1); + @"class Test { string x = ""hello""; string s = $"" { x } {|Significant:$$hello|}""; }"); - // Selects whitespace AssertExtent( - TestString, - pos: startOfString + 8, - isSignificant: false, - start: startOfString + 8, length: 1); + @"class Test { string x = ""hello""; string s = $"" { x } hello{|Significant:$$""|}; }"); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] + [WorkItem(59581, "https://github.com/dotnet/roslyn/issues/59581")] + public void TestRawStringContent() + { + AssertExtent( + @"string s = """""" + Hello + {|Significant:$$World|}! + :) + """""";"); + + AssertExtent( + @"string s = """""" + Hello + {|Significant:W$$orld|}! + :) + """""";"); - // Selects hello AssertExtent( - TestString, - pos: startOfString + 9, - isSignificant: true, - start: startOfString + 9, length: 5); + @"string s = """""" + Hello + {|Significant:Wo$$rld|}! + :) + """""";"); - // Selects closing quote AssertExtent( - TestString, - pos: startOfString + 14, - isSignificant: true, - start: startOfString + 14, length: 1); + @"string s = """""" + Hello + {|Significant:Wor$$ld|}! + :) + """""";"); + + AssertExtent( + @"string s = """""" + Hello + {|Significant:Worl$$d|}! + :) + """""";"); + + AssertExtent( + @"string s = """""" + Hello + World{|Significant:$$!|} + :) + """""";"); } - private static void AssertExtent(string code, int pos, bool isSignificant, int start, int length) + [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] + [WorkItem(59581, "https://github.com/dotnet/roslyn/issues/59581")] + public void TestRawStringDelimeter1() { - AssertExtent(code, pos, isSignificant, start, length, null); - AssertExtent(code, pos, isSignificant, start, length, Options.Script); + AssertExtent( + @"string s = {|Significant:$$""""""|} + Hello + World! + :) + """""";"); + + AssertExtent( + @"string s = {|Significant:""$$""""|} + Hello + World! + :) + """""";"); + + AssertExtent( + @"string s = {|Significant:""""$$""|} + Hello + World! + :) + """""";"); } - private static void AssertExtent(string code, int pos, bool isSignificant, int start, int length, CSharpParseOptions options) + [WpfFact, Trait(Traits.Feature, Traits.Features.TextStructureNavigator)] + [WorkItem(59581, "https://github.com/dotnet/roslyn/issues/59581")] + public void TestRawStringDelimeter2() { - using var workspace = TestWorkspace.CreateCSharp(code, options); - var buffer = workspace.Documents.First().GetTextBuffer(); + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:$$""""""|};"); - var provider = Assert.IsType( - workspace.GetService(ContentTypeNames.CSharpContentType)); + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:""$$""""|};"); - var navigator = provider.CreateTextStructureNavigator(buffer); + AssertExtent( + @"string s = """""" + Hello + World! + :) + {|Significant:""""$$""|};"); - var extent = navigator.GetExtentOfWord(new SnapshotPoint(buffer.CurrentSnapshot, pos)); - Assert.Equal(isSignificant, extent.IsSignificant); - var expectedSpan = new SnapshotSpan(buffer.CurrentSnapshot, start, length); - Assert.Equal(expectedSpan, extent.Span); } private static void TestNavigator( @@ -389,12 +403,12 @@ private static void TestNavigator( int startLength, int endPosition, int endLength, - CSharpParseOptions options) + CSharpParseOptions? options) { using var workspace = TestWorkspace.CreateCSharp(code, options); var buffer = workspace.Documents.First().GetTextBuffer(); - var provider = Assert.IsType( + var provider = Assert.IsType( workspace.GetService(ContentTypeNames.CSharpContentType)); var navigator = provider.CreateTextStructureNavigator(buffer); diff --git a/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs b/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs new file mode 100644 index 0000000000000..3b1e08559521e --- /dev/null +++ b/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs @@ -0,0 +1,122 @@ +// 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.Linq; +using System.Text; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Operations; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Test.Utilities.TextStructureNavigation +{ + public abstract class AbstractTextStructureNavigatorTests + { + protected abstract string ContentType { get; } + protected abstract TestWorkspace CreateWorkspace(string code); + + protected StringBuilder result = new StringBuilder(); + + protected void AssertExtent(string code, int pos) + { + using var workspace = CreateWorkspace(code); + var document = workspace.Documents.First(); + var buffer = document.GetTextBuffer(); + + var provider = workspace.GetService(this.ContentType); + + var navigator = provider.CreateTextStructureNavigator(buffer); + + var extent = navigator.GetExtentOfWord(new SnapshotPoint(buffer.CurrentSnapshot, pos)); + + result.AppendLine(" AssertExtent("); + result.Append(" @\""); + + var spanStart = extent.Span.Span.Start; + var spanEnd = extent.Span.Span.End; + + result.Append(code[..spanStart].Replace("\"", "\"\"")); + + if (pos == spanStart) + { + result.Append("{|"); + result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); + result.Append(":$$"); + result.Append(code[spanStart..spanEnd].Replace("\"", "\"\"")); + result.Append("|}"); + result.Append(code[spanEnd..].Replace("\"", "\"\"")); + } + else if (pos < spanStart) + { + result.Append("$$"); + result.Append(code[pos..spanStart].Replace("\"", "\"\"")); + result.Append("{|"); + result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); + result.Append(":"); + result.Append(code[spanStart..spanEnd].Replace("\"", "\"\"")); + result.Append("|}"); + result.Append(code[spanEnd..].Replace("\"", "\"\"")); + } + else if (pos < spanEnd) + { + result.Append("{|"); + result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); + result.Append(":"); + result.Append(code[spanStart..pos].Replace("\"", "\"\"")); + result.Append("$$"); + result.Append(code[pos..spanEnd].Replace("\"", "\"\"")); + result.Append("|}"); + result.Append(code[spanEnd..].Replace("\"", "\"\"")); + } + else + { + result.Append("{|"); + result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); + result.Append(":"); + result.Append(code[spanStart..spanEnd].Replace("\"", "\"\"")); + result.Append("|}"); + result.Append(code[spanEnd..pos].Replace("\"", "\"\"")); + result.Append("$$"); + result.Append(code[spanEnd..].Replace("\"", "\"\"")); + } + + result.Append("\");"); + + result.AppendLine(); + result.AppendLine(); + } + + protected void AssertExtent(string code) + { + using var workspace = CreateWorkspace(code); + var document = workspace.Documents.First(); + var buffer = document.GetTextBuffer(); + + var provider = workspace.GetService(this.ContentType); + + var navigator = provider.CreateTextStructureNavigator(buffer); + + var position = document.CursorPosition!.Value; + var extent = navigator.GetExtentOfWord(new SnapshotPoint(buffer.CurrentSnapshot, position)); + + var annotatedSpans = document.AnnotatedSpans; + + var (key, expectedSpans) = annotatedSpans.Single(); + Assert.Equal(expectedSpans.Single(), extent.Span.Span.ToTextSpan()); + + if (extent.IsSignificant) + { + Assert.Equal("Significant", key); + } + else + { + Assert.Equal("Insignificant", key); + } + } + } +} diff --git a/src/EditorFeatures/VisualBasic/TextStructureNavigation/TextStructureNavigatorProvider.vb b/src/EditorFeatures/VisualBasic/TextStructureNavigation/VisualBasicTextStructureNavigatorProvider.vb similarity index 98% rename from src/EditorFeatures/VisualBasic/TextStructureNavigation/TextStructureNavigatorProvider.vb rename to src/EditorFeatures/VisualBasic/TextStructureNavigation/VisualBasicTextStructureNavigatorProvider.vb index 9549cc060db46..140f423b5166a 100644 --- a/src/EditorFeatures/VisualBasic/TextStructureNavigation/TextStructureNavigatorProvider.vb +++ b/src/EditorFeatures/VisualBasic/TextStructureNavigation/VisualBasicTextStructureNavigatorProvider.vb @@ -14,7 +14,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.TextStructureNavigation - Friend Class TextStructureNavigatorProvider + Friend Class VisualBasicTextStructureNavigatorProvider Inherits AbstractTextStructureNavigatorProvider diff --git a/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb b/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb index 7428a914a3d79..af51c14e8a646 100644 --- a/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb +++ b/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb @@ -231,7 +231,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.TextStructureNavig Using workspace = TestWorkspace.CreateVisualBasic(code) Dim buffer = workspace.Documents.First().GetTextBuffer() - Dim provider = Assert.IsType(Of TextStructureNavigatorProvider)( + Dim provider = Assert.IsType(Of VisualBasicTextStructureNavigatorProvider)( workspace.GetService(Of ITextStructureNavigatorProvider)(ContentTypeNames.VisualBasicContentType)) Dim navigator = provider.CreateTextStructureNavigator(buffer) From 1e90ff8f59c8b94340a16be6997314628865c6c8 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 18 Feb 2022 15:25:10 -0800 Subject: [PATCH 109/187] Update VB tests --- .../AbstractTextStructureNavigationTests.cs | 6 +- .../TextStructureNavigatorTests.vb | 292 ++++++++---------- 2 files changed, 139 insertions(+), 159 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs b/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs index 3b1e08559521e..a3d518f90810f 100644 --- a/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs +++ b/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs @@ -22,7 +22,7 @@ public abstract class AbstractTextStructureNavigatorTests protected StringBuilder result = new StringBuilder(); - protected void AssertExtent(string code, int pos) + protected void AssertExtent(string code, int pos, bool isSignificant = false, int start = 0, int length = 0) { using var workspace = CreateWorkspace(code); var document = workspace.Documents.First(); @@ -35,7 +35,7 @@ protected void AssertExtent(string code, int pos) var extent = navigator.GetExtentOfWord(new SnapshotPoint(buffer.CurrentSnapshot, pos)); result.AppendLine(" AssertExtent("); - result.Append(" @\""); + result.Append(" \""); var spanStart = extent.Span.Span.Start; var spanEnd = extent.Span.Span.End; @@ -85,7 +85,7 @@ protected void AssertExtent(string code, int pos) result.Append(code[spanEnd..].Replace("\"", "\"\"")); } - result.Append("\");"); + result.Append("\")"); result.AppendLine(); result.AppendLine(); diff --git a/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb b/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb index af51c14e8a646..1da43ed54db25 100644 --- a/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb +++ b/src/EditorFeatures/VisualBasicTest/TextStructureNavigation/TextStructureNavigatorTests.vb @@ -2,249 +2,229 @@ ' 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 Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces -Imports Microsoft.CodeAnalysis.Editor.VisualBasic.TextStructureNavigation -Imports Microsoft.VisualStudio.Text -Imports Microsoft.VisualStudio.Text.Operations +Imports Microsoft.CodeAnalysis.Test.Utilities.TextStructureNavigation Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.TextStructureNavigation <[UseExportProvider]> Public Class TextStructureNavigatorTests + Inherits AbstractTextStructureNavigatorTests + + Protected Overrides ReadOnly Property ContentType As String = ContentTypeNames.VisualBasicContentType + + Protected Overrides Function CreateWorkspace(code As String) As TestWorkspace + Return TestWorkspace.CreateVisualBasic(code) + End Function Public Sub TestEmpty() AssertExtent( - String.Empty, - pos:=0, - isSignificant:=False, - start:=0, length:=0) + "{|Insignificant:$$|}") End Sub Public Sub TestWhitespace() AssertExtent( - " ", - pos:=0, - isSignificant:=False, - start:=0, length:=3) + "{|Insignificant:$$ |}") AssertExtent( - " ", - pos:=1, - isSignificant:=False, - start:=0, length:=3) + "{|Insignificant: $$ |}") AssertExtent( - " ", - pos:=3, - isSignificant:=False, - start:=0, length:=3) + "{|Insignificant: |}$$") End Sub Public Sub TestEndOfFile() AssertExtent( - "Imports System", - pos:=14, - isSignificant:=True, - start:=8, length:=6) + "Imports {|Significant:System|}$$") End Sub Public Sub TestNewLine() AssertExtent( - "Module Module1" & vbCrLf & vbCrLf & "End Module", - pos:=14, - isSignificant:=False, - start:=14, length:=2) - - AssertExtent( - "Module Module1" & vbCrLf & vbCrLf & "End Module", - pos:=15, - isSignificant:=False, - start:=14, length:=2) - - AssertExtent( - "Module Module1" & vbCrLf & vbCrLf & "End Module", - pos:=16, - isSignificant:=False, - start:=16, length:=2) + "Module Module1{|Insignificant:$$ +|} +End Module") AssertExtent( - "Module Module1" & vbCrLf & vbCrLf & "End Module", - pos:=17, - isSignificant:=False, - start:=16, length:=2) + "Module Module1 +{|Insignificant:$$ +|}End Module") End Sub Public Sub TestComment() AssertExtent( - " ' Comment ", - pos:=1, - isSignificant:=True, - start:=1, length:=11) + " {|Significant:$$' Comment |}") AssertExtent( - " ' Comment ", - pos:=5, - isSignificant:=True, - start:=3, length:=7) + " ' {|Significant:Co$$mment|} ") AssertExtent( - " ' () test", - pos:=4, - isSignificant:=True, - start:=3, length:=2) + " ' {|Significant:($$)|} test") AssertExtent( - " REM () test", - pos:=1, - isSignificant:=True, - start:=1, length:=11) + " {|Significant:$$REM () test|}") AssertExtent( - " rem () test", - pos:=6, - isSignificant:=True, - start:=5, length:=2) + " rem {|Significant:($$)|} test") End Sub Public Sub TestKeyword() - For i = 7 To 12 - AssertExtent( - "Public Module Module1", - pos:=i, - isSignificant:=True, - start:=7, length:=6) - Next + AssertExtent( + "Public {|Significant:$$Module|} Module1") + + AssertExtent( + "Public {|Significant:M$$odule|} Module1") + + AssertExtent( + "Public {|Significant:Mo$$dule|} Module1") + + AssertExtent( + "Public {|Significant:Mod$$ule|} Module1") + + AssertExtent( + "Public {|Significant:Modu$$le|} Module1") + + AssertExtent( + "Public {|Significant:Modul$$e|} Module1") End Sub Public Sub TestIdentifier() - For i = 13 To 13 + 8 - AssertExtent( - "Public Class SomeClass : Inherits Object", - pos:=i, - isSignificant:=True, - start:=13, length:=9) - Next + AssertExtent( + "Public Class {|Significant:$$SomeClass|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:S$$omeClass|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:So$$meClass|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:Som$$eClass|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:Some$$Class|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:SomeC$$lass|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:SomeCl$$ass|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:SomeCla$$ss|} : Inherits Object") + + AssertExtent( + "Public Class {|Significant:SomeClas$$s|} : Inherits Object") End Sub Public Sub TestEscapedIdentifier() - For i = 12 To 12 + 7 - AssertExtent( - "Friend Enum [Module] As Long", - pos:=i, - isSignificant:=True, - start:=12, length:=8) - Next + AssertExtent( + "Friend Enum {|Significant:$$[Module]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[$$Module]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[M$$odule]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[Mo$$dule]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[Mod$$ule]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[Modu$$le]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[Modul$$e]|} As Long") + + AssertExtent( + "Friend Enum {|Significant:[Module$$]|} As Long") End Sub Public Sub TestNumber() - For i = 37 To 37 + 12 - AssertExtent( - "Class Test : Dim number As Double = -1.234678E-120 : End Class", - pos:=i, - isSignificant:=True, - start:=37, length:=13) - Next - End Sub + AssertExtent( + "Class Test : Dim number As Double = -{|Significant:$$1.234678E-120|} : End Class") - - Public Sub TestString() AssertExtent( - "Class Test : Dim str As String = "" () test "" : End Class", - pos:=33, - isSignificant:=True, - start:=33, length:=1) + "Class Test : Dim number As Double = -{|Significant:1$$.234678E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = "" () test "" : End Class", - pos:=34, - isSignificant:=False, - start:=34, length:=1) + "Class Test : Dim number As Double = -{|Significant:1.$$234678E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = "" () test "" : End Class", - pos:=35, - isSignificant:=True, - start:=35, length:=2) + "Class Test : Dim number As Double = -{|Significant:1.2$$34678E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = "" () test "" : End Class", - pos:=43, - isSignificant:=False, - start:=42, length:=2) + "Class Test : Dim number As Double = -{|Significant:1.23$$4678E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = "" () test "" : End Class", - pos:=44, - isSignificant:=True, - start:=44, length:=1) - End Sub + "Class Test : Dim number As Double = -{|Significant:1.234$$678E-120|} : End Class") - - Public Sub TestInterpolatedString() AssertExtent( - "Class Test : Dim str As String = $"" () test "" : End Class", - pos:=33, - isSignificant:=True, - start:=33, length:=2) + "Class Test : Dim number As Double = -{|Significant:1.2346$$78E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = $"" () test "" : End Class", - pos:=35, - isSignificant:=False, - start:=35, length:=1) + "Class Test : Dim number As Double = -{|Significant:1.23467$$8E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = $"" () test "" : End Class", - pos:=36, - isSignificant:=True, - start:=36, length:=2) + "Class Test : Dim number As Double = -{|Significant:1.234678$$E-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = $"" () test "" : End Class", - pos:=44, - isSignificant:=False, - start:=43, length:=2) + "Class Test : Dim number As Double = -{|Significant:1.234678E$$-120|} : End Class") AssertExtent( - "Class Test : Dim str As String = "" () test "" : End Class", - pos:=45, - isSignificant:=False, - start:=45, length:=1) - End Sub + "Class Test : Dim number As Double = -{|Significant:1.234678E-$$120|} : End Class") - Private Shared Sub AssertExtent( - code As String, - pos As Integer, - isSignificant As Boolean, - start As Integer, - length As Integer) + AssertExtent( + "Class Test : Dim number As Double = -{|Significant:1.234678E-1$$20|} : End Class") - Using workspace = TestWorkspace.CreateVisualBasic(code) - Dim buffer = workspace.Documents.First().GetTextBuffer() + AssertExtent( + "Class Test : Dim number As Double = -{|Significant:1.234678E-12$$0|} : End Class") + End Sub - Dim provider = Assert.IsType(Of VisualBasicTextStructureNavigatorProvider)( - workspace.GetService(Of ITextStructureNavigatorProvider)(ContentTypeNames.VisualBasicContentType)) + + Public Sub TestString() + AssertExtent( + "Class Test : Dim str As String = {|Significant:$$""|} () test "" : End Class") - Dim navigator = provider.CreateTextStructureNavigator(buffer) + AssertExtent( + "Class Test : Dim str As String = ""{|Insignificant:$$ |}() test "" : End Class") - Dim extent = navigator.GetExtentOfWord(New SnapshotPoint(buffer.CurrentSnapshot, pos)) + AssertExtent( + "Class Test : Dim str As String = "" {|Significant:$$()|} test "" : End Class") - Assert.Equal(isSignificant, extent.IsSignificant) + AssertExtent( + "Class Test : Dim str As String = "" () test{|Insignificant: $$ |}"" : End Class") - Dim expectedSpan As New SnapshotSpan(buffer.CurrentSnapshot, start, length) - Assert.Equal(expectedSpan, extent.Span) - End Using + AssertExtent( + "Class Test : Dim str As String = "" () test {|Significant:$$""|} : End Class") End Sub - End Class + + Public Sub TestInterpolatedString() + AssertExtent( + "Class Test : Dim str As String = {|Significant:$$$""|} () test "" : End Class") + + AssertExtent( + "Class Test : Dim str As String = $""{|Insignificant:$$ |}() test "" : End Class") + + AssertExtent( + "Class Test : Dim str As String = $"" {|Significant:$$()|} test "" : End Class") + AssertExtent( + "Class Test : Dim str As String = $"" () test{|Insignificant: $$ |}"" : End Class") + + AssertExtent( + "Class Test : Dim str As String = "" () test ""{|Insignificant:$$ |}: End Class") + End Sub + End Class End Namespace From b8b5b9d3ce14636a6dc5dbc1ad7bad812eb7404b Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Fri, 18 Feb 2022 15:25:51 -0800 Subject: [PATCH 110/187] Delete codegenerator --- .../TextStructureNavigatorTests.cs | 2 - .../AbstractTextStructureNavigationTests.cs | 69 ------------------- 2 files changed, 71 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs index 7165b41a1fb42..4c431f1d41e9a 100644 --- a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs +++ b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs @@ -380,8 +380,6 @@ public void TestRawStringDelimeter2() World! :) {|Significant:""""$$""|};"); - - } private static void TestNavigator( diff --git a/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs b/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs index a3d518f90810f..f97b1cff3d975 100644 --- a/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs +++ b/src/EditorFeatures/TestUtilities/TextStructureNavigation/AbstractTextStructureNavigationTests.cs @@ -22,75 +22,6 @@ public abstract class AbstractTextStructureNavigatorTests protected StringBuilder result = new StringBuilder(); - protected void AssertExtent(string code, int pos, bool isSignificant = false, int start = 0, int length = 0) - { - using var workspace = CreateWorkspace(code); - var document = workspace.Documents.First(); - var buffer = document.GetTextBuffer(); - - var provider = workspace.GetService(this.ContentType); - - var navigator = provider.CreateTextStructureNavigator(buffer); - - var extent = navigator.GetExtentOfWord(new SnapshotPoint(buffer.CurrentSnapshot, pos)); - - result.AppendLine(" AssertExtent("); - result.Append(" \""); - - var spanStart = extent.Span.Span.Start; - var spanEnd = extent.Span.Span.End; - - result.Append(code[..spanStart].Replace("\"", "\"\"")); - - if (pos == spanStart) - { - result.Append("{|"); - result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); - result.Append(":$$"); - result.Append(code[spanStart..spanEnd].Replace("\"", "\"\"")); - result.Append("|}"); - result.Append(code[spanEnd..].Replace("\"", "\"\"")); - } - else if (pos < spanStart) - { - result.Append("$$"); - result.Append(code[pos..spanStart].Replace("\"", "\"\"")); - result.Append("{|"); - result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); - result.Append(":"); - result.Append(code[spanStart..spanEnd].Replace("\"", "\"\"")); - result.Append("|}"); - result.Append(code[spanEnd..].Replace("\"", "\"\"")); - } - else if (pos < spanEnd) - { - result.Append("{|"); - result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); - result.Append(":"); - result.Append(code[spanStart..pos].Replace("\"", "\"\"")); - result.Append("$$"); - result.Append(code[pos..spanEnd].Replace("\"", "\"\"")); - result.Append("|}"); - result.Append(code[spanEnd..].Replace("\"", "\"\"")); - } - else - { - result.Append("{|"); - result.Append(extent.IsSignificant ? "Significant" : "Insignificant"); - result.Append(":"); - result.Append(code[spanStart..spanEnd].Replace("\"", "\"\"")); - result.Append("|}"); - result.Append(code[spanEnd..pos].Replace("\"", "\"\"")); - result.Append("$$"); - result.Append(code[spanEnd..].Replace("\"", "\"\"")); - } - - result.Append("\")"); - - result.AppendLine(); - result.AppendLine(); - } - protected void AssertExtent(string code) { using var workspace = CreateWorkspace(code); From fab4fb3d84789a06e02c7f60eba32f0f6c87adaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Fri, 18 Feb 2022 16:25:11 -0800 Subject: [PATCH 111/187] Propagate CodeActionOptions through fix all (#59575) * Tidy up argument validation in CodeFixContext public API * Propagate CodeActionOptions through fix all * Fix FixAllState.With to preserve invariant --- eng/config/BannedSymbols.txt | 4 +- .../AsyncSuggestedActionsSource.cs | 2 +- .../FixAll/FixMultipleOccurrencesService.cs | 18 ++- .../Suggestions/SuggestedActionsSource.cs | 9 +- .../CodeActions/CodeActionOptionsStorage.cs | 15 +- .../CodeActions/CodeActionResolveHandler.cs | 2 +- .../CodeActions/CodeActionsHandler.cs | 2 +- .../CodeActions/RunCodeActionHandler.cs | 2 +- .../Diagnostics/AbstractUserDiagnosticTest.cs | 11 +- .../Test/CodeFixes/CodeFixServiceTests.cs | 4 +- .../SyncNamespacesServiceTests.vb | 11 +- .../Core/Portable/CodeFixes/CodeFixService.cs | 52 +++--- .../IFixMultipleOccurrencesService.cs | 3 + .../Portable/CodeFixes/ICodeFixService.cs | 2 +- .../AbstractSyncNamespacesSevice.cs | 32 ++-- .../SyncNamespaces/ISyncNamespacesService.cs | 3 +- .../CodeCleanup/AbstractCodeCleanUpFixer.cs | 6 +- .../SyncNamespacesCommandHandler.cs | 10 +- .../VisualStudioSuppressionFixService.cs | 11 +- .../FixAllOccurrences/BatchFixAllProvider.cs | 4 +- .../DefaultFixAllProviderHelpers.cs | 4 +- .../FixAllOccurrences/FixAllContext.cs | 44 +++--- .../Compiler/Core/Utilities/PublicContract.cs | 4 +- .../Core/CodeFixes}/CodeActionOptions.cs | 11 ++ .../Core/CodeFixes/FixAll/FixAllState.cs | 148 +++++++----------- .../Core/WorkspaceExtensions.projitems | 1 + 26 files changed, 239 insertions(+), 176 deletions(-) rename src/Workspaces/{Core/Portable/CodeActions => SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes}/CodeActionOptions.cs (79%) diff --git a/eng/config/BannedSymbols.txt b/eng/config/BannedSymbols.txt index 90dc1530bd4d1..ada110f216658 100644 --- a/eng/config/BannedSymbols.txt +++ b/eng/config/BannedSymbols.txt @@ -5,4 +5,6 @@ M:Microsoft.CodeAnalysis.Completion.CompletionProvider.GetDescriptionAsync(Micro M:Microsoft.CodeAnalysis.Completion.CompletionService.GetCompletionsAsync(Microsoft.CodeAnalysis.Document,System.Int32,Microsoft.CodeAnalysis.Completion.CompletionTrigger,System.Collections.Immutable.ImmutableHashSet{System.String},Microsoft.CodeAnalysis.Options.OptionSet,System.Threading.CancellationToken); Use internal overload instead M:Microsoft.CodeAnalysis.Completion.CompletionService.GetDescriptionAsync(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.Completion.CompletionItem,System.Threading.CancellationToken); Use internal overload instead M:Microsoft.CodeAnalysis.Completion.CompletionService.GetRules; Use internal overload instead -M:Microsoft.CodeAnalysis.QuickInfo.QuickInfoService.GetQuickInfoAsync(Microsoft.CodeAnalysis.Document,System.Int32,System.Threading.CancellationToken); Use internal overload instead \ No newline at end of file +M:Microsoft.CodeAnalysis.QuickInfo.QuickInfoService.GetQuickInfoAsync(Microsoft.CodeAnalysis.Document,System.Int32,System.Threading.CancellationToken); Use internal overload instead +M:Microsoft.CodeAnalysis.CodeFixes.FixAllContext.#ctor(Microsoft.CodeAnalysis.Document,Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider,Microsoft.CodeAnalysis.CodeFixes.FixAllScope,System.String,System.Collections.Generic.IEnumerable{System.String},Microsoft.CodeAnalysis.CodeFixes.FixAllContext.DiagnosticProvider,System.Threading.CancellationToken); Use internal overload instead +M:Microsoft.CodeAnalysis.CodeFixes.FixAllContext.#ctor(Microsoft.CodeAnalysis.Project,Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider,Microsoft.CodeAnalysis.CodeFixes.FixAllScope,System.String,System.Collections.Generic.IEnumerable{System.String},Microsoft.CodeAnalysis.CodeFixes.FixAllContext.DiagnosticProvider,System.Threading.CancellationToken); Use internal overload instead diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs index ed537090ce98e..cb22c62204420 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/AsyncSuggestedActionsSource.cs @@ -168,7 +168,7 @@ private async IAsyncEnumerable GetCodeFixesAndRefactoringsAs var workspace = document.Project.Solution.Workspace; var supportsFeatureService = workspace.Services.GetRequiredService(); - var options = GlobalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = GlobalOptions.GetCodeActionOptions(document.Project.Language); var fixesTask = GetCodeFixesAsync( state, supportsFeatureService, requestedActionCategories, workspace, document, range, diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixMultipleOccurrencesService.cs b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixMultipleOccurrencesService.cs index a8d4ac11673d2..97cb4ed4b1ea9 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixMultipleOccurrencesService.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/FixAll/FixMultipleOccurrencesService.cs @@ -5,14 +5,16 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; +using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Extensions; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Roslyn.Utilities; @@ -22,25 +24,28 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions /// Service to compute and apply code fixes. /// [ExportWorkspaceService(typeof(IFixMultipleOccurrencesService), ServiceLayer.Host), Shared] - internal class FixMultipleOccurrencesService : IFixMultipleOccurrencesService + internal sealed class FixMultipleOccurrencesService : IFixMultipleOccurrencesService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public FixMultipleOccurrencesService(IAsynchronousOperationListenerProvider listenerProvider) - => listenerProvider.GetListener(FeatureAttribute.LightBulb); + { + listenerProvider.GetListener(FeatureAttribute.LightBulb); + } public Solution GetFix( ImmutableDictionary> diagnosticsToFix, Workspace workspace, CodeFixProvider fixProvider, FixAllProvider fixAllProvider, + CodeActionOptionsProvider optionsProvider, string equivalenceKey, string waitDialogTitle, string waitDialogMessage, CancellationToken cancellationToken) { var fixMultipleState = FixAllState.Create( - fixAllProvider, diagnosticsToFix, fixProvider, equivalenceKey); + fixAllProvider, diagnosticsToFix, fixProvider, equivalenceKey, optionsProvider); return GetFixedSolution( fixMultipleState, workspace, waitDialogTitle, @@ -52,13 +57,14 @@ public Solution GetFix( Workspace workspace, CodeFixProvider fixProvider, FixAllProvider fixAllProvider, + CodeActionOptionsProvider optionsProvider, string equivalenceKey, string waitDialogTitle, string waitDialogMessage, CancellationToken cancellationToken) { var fixMultipleState = FixAllState.Create( - fixAllProvider, diagnosticsToFix, fixProvider, equivalenceKey); + fixAllProvider, diagnosticsToFix, fixProvider, equivalenceKey, optionsProvider); return GetFixedSolution( fixMultipleState, workspace, waitDialogTitle, diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 0d2a67374b859..332cf7daf6362 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -177,7 +177,7 @@ public bool TryGetTelemetryId(out Guid telemetryId) Func addOperationScope = description => operationContext?.AddScope(allowCancellation: true, string.Format(EditorFeaturesResources.Gathering_Suggestions_0, description)); - var options = GlobalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: true); + var options = GlobalOptions.GetBlockingCodeActionOptions(document.Project.Language); // We convert the code fixes and refactorings to UnifiedSuggestedActionSets instead of // SuggestedActionSets so that we can share logic between local Roslyn and LSP. @@ -414,13 +414,14 @@ await InvokeBelowInputPriorityAsync(() => ReferenceCountedDisposable state, Document document, SnapshotSpan range, + CodeActionOptions options, CancellationToken cancellationToken) { if (state.Target.Owner._codeFixService != null && state.Target.SubjectBuffer.SupportsCodeFixes()) { var result = await state.Target.Owner._codeFixService.GetMostSevereFixableDiagnosticAsync( - document, range.Span.ToTextSpan(), cancellationToken).ConfigureAwait(false); + document, range.Span.ToTextSpan(), options, cancellationToken).ConfigureAwait(false); if (result.HasFix) { @@ -614,12 +615,12 @@ private void OnSuggestedActionsChanged(Workspace currentWorkspace, DocumentId? c if (document == null) return null; - var options = GlobalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = GlobalOptions.GetCodeActionOptions(document.Project.Language); using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); var linkedToken = linkedTokenSource.Token; - var errorTask = Task.Run(() => GetFixLevelAsync(state, document, range, linkedToken), linkedToken); + var errorTask = Task.Run(() => GetFixLevelAsync(state, document, range, options, linkedToken), linkedToken); var selection = await GetSpanAsync(state, range, linkedToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs b/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs index bc5363d278095..c4a5784c02f94 100644 --- a/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs +++ b/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SymbolSearch; @@ -10,10 +11,22 @@ namespace Microsoft.CodeAnalysis.CodeActions { internal static class CodeActionOptionsStorage { - internal static CodeActionOptions GetCodeActionOptions(this IGlobalOptionService globalOptions, string language, bool isBlocking) + internal static CodeActionOptions GetCodeActionOptions(this IGlobalOptionService globalOptions, string language) + => GetCodeActionOptions(globalOptions, language, isBlocking: false); + + internal static CodeActionOptions GetBlockingCodeActionOptions(this IGlobalOptionService globalOptions, string language) + => GetCodeActionOptions(globalOptions, language, isBlocking: true); + + private static CodeActionOptions GetCodeActionOptions(this IGlobalOptionService globalOptions, string language, bool isBlocking) => new( SearchOptions: globalOptions.GetSymbolSearchOptions(language), HideAdvancedMembers: globalOptions.GetOption(CompletionOptionsStorage.HideAdvancedMembers, language), IsBlocking: isBlocking); + + internal static CodeActionOptionsProvider GetCodeActionOptionsProvider(this IGlobalOptionService globalOptions) + { + var cache = ImmutableDictionary.Empty; + return language => ImmutableInterlocked.GetOrAdd(ref cache, language, (language, options) => GetCodeActionOptions(options, language), globalOptions); + } } } diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs index 66a3f2f7d29e2..46b40d708f983 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionResolveHandler.cs @@ -66,7 +66,7 @@ public CodeActionResolveHandler( var data = ((JToken)codeAction.Data!).ToObject(); Assumes.Present(data); - var options = _globalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = _globalOptions.GetCodeActionOptions(document.Project.Language); var codeActions = await CodeActionHelpers.GetCodeActionsAsync( _codeActionsCache, diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs index 1553c5424a8ac..c059f005a7ee0 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/CodeActionsHandler.cs @@ -57,7 +57,7 @@ public CodeActionsHandler( var document = context.Document; Contract.ThrowIfNull(document); - var options = _globalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = _globalOptions.GetCodeActionOptions(document.Project.Language); var codeActions = await CodeActionHelpers.GetVSCodeActionsAsync( request, _codeActionsCache, document, options, _codeFixService, _codeRefactoringService, cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs index 4a0224579340c..1e82bc010dc39 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/CodeActions/RunCodeActionHandler.cs @@ -74,7 +74,7 @@ public override async Task HandleRequestAsync(LSP.ExecuteCommandParams r var runRequest = ((JToken)request.Arguments.Single()).ToObject(); Assumes.Present(runRequest); - var options = _globalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = _globalOptions.GetCodeActionOptions(document.Project.Language); var codeActions = await CodeActionHelpers.GetCodeActionsAsync( _codeActionsCache, document, runRequest.Range, options, _codeFixService, _codeRefactoringService, cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs index 4a5445c0d774d..1fa120d85c82e 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest.cs @@ -219,7 +219,7 @@ protected static Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, o var fixAllState = GetFixAllState( fixAllProvider, diagnostics, fixer, testDriver, document, - scope.Value, equivalenceKey); + scope.Value, equivalenceKey, _ => options); var fixAllContext = new FixAllContext(fixAllState, new ProgressTracker(), CancellationToken.None); var fixAllFix = await fixAllProvider.GetFixAsync(fixAllContext); @@ -235,7 +235,8 @@ private static FixAllState GetFixAllState( TestDiagnosticAnalyzerDriver testDriver, Document document, FixAllScope scope, - string equivalenceKey) + string equivalenceKey, + CodeActionOptionsProvider optionsProvider) { Assert.NotEmpty(diagnostics); @@ -243,7 +244,7 @@ private static FixAllState GetFixAllState( { // Bulk fixing diagnostics in selected scope. var diagnosticsToFix = ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable(KeyValuePairUtil.Create(document, diagnostics.ToImmutableArray()))); - return FixAllState.Create(fixAllProvider, diagnosticsToFix, fixer, equivalenceKey); + return FixAllState.Create(fixAllProvider, diagnosticsToFix, fixer, equivalenceKey, optionsProvider); } var diagnostic = diagnostics.First(); @@ -251,8 +252,8 @@ private static FixAllState GetFixAllState( var fixAllDiagnosticProvider = new FixAllDiagnosticProvider(testDriver, diagnosticIds); return diagnostic.Location.IsInSource - ? new FixAllState(fixAllProvider, document, fixer, scope, equivalenceKey, diagnosticIds, fixAllDiagnosticProvider) - : new FixAllState(fixAllProvider, document.Project, fixer, scope, equivalenceKey, diagnosticIds, fixAllDiagnosticProvider); + ? new FixAllState(fixAllProvider, document, document.Project, fixer, scope, equivalenceKey, diagnosticIds, fixAllDiagnosticProvider, optionsProvider) + : new FixAllState(fixAllProvider, document: null, document.Project, fixer, scope, equivalenceKey, diagnosticIds, fixAllDiagnosticProvider, optionsProvider); } private protected Task TestActionCountInAllFixesAsync( diff --git a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs index 0f0f4f1a4e01b..d29746f7892a1 100644 --- a/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs +++ b/src/EditorFeatures/Test/CodeFixes/CodeFixServiceTests.cs @@ -60,7 +60,7 @@ public async Task TestGetFirstDiagnosticWithFixAsync() var reference = new MockAnalyzerReference(); var project = workspace.CurrentSolution.Projects.Single().AddAnalyzerReference(reference); var document = project.Documents.Single(); - var unused = await fixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None); + var unused = await fixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), CodeActionOptions.Default, CancellationToken.None); var fixer1 = (MockFixer)fixers.Single().Value; var fixer2 = (MockFixer)reference.Fixer!; @@ -297,7 +297,7 @@ private static async Task GetFirstDiagnosticWithFixWithExceptionValidationAsync( errorReportingService.OnError = message => errorReported = true; GetDocumentAndExtensionManager(tuple.analyzerService, workspace, out var document, out var extensionManager); - var unused = await tuple.codeFixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), cancellationToken: CancellationToken.None); + var unused = await tuple.codeFixService.GetMostSevereFixableDiagnosticAsync(document, TextSpan.FromBounds(0, 0), CodeActionOptions.Default, CancellationToken.None); Assert.True(extensionManager.IsDisabled(codefix)); Assert.False(extensionManager.IsIgnored(codefix)); Assert.True(errorReported); diff --git a/src/EditorFeatures/Test2/SyncNamespaces/SyncNamespacesServiceTests.vb b/src/EditorFeatures/Test2/SyncNamespaces/SyncNamespacesServiceTests.vb index 4f9e3cb964cc5..19e4ca34cac16 100644 --- a/src/EditorFeatures/Test2/SyncNamespaces/SyncNamespacesServiceTests.vb +++ b/src/EditorFeatures/Test2/SyncNamespaces/SyncNamespacesServiceTests.vb @@ -4,6 +4,7 @@ Imports System.Collections.Immutable Imports System.Threading +Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.SyncNamespaces @@ -39,7 +40,7 @@ namespace Test.Namespace.App Dim document = project.Documents.Single() Dim syncService = project.GetLanguageService(Of ISyncNamespacesService)() - Dim newSolution = Await syncService.SyncNamespacesAsync(ImmutableArray.Create(project), CancellationToken.None) + Dim newSolution = Await syncService.SyncNamespacesAsync(ImmutableArray.Create(project), CodeActionOptions.Default, CancellationToken.None) Dim solutionChanges = workspace.CurrentSolution.GetChanges(newSolution) @@ -75,7 +76,7 @@ namespace Test Dim document = project.Documents.Single() Dim syncService = project.GetLanguageService(Of ISyncNamespacesService)() - Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CancellationToken.None) + Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CodeActionOptions.Default, CancellationToken.None) Dim solutionChanges = workspace.CurrentSolution.GetChanges(newSolution) Dim projectChanges = solutionChanges.GetProjectChanges().Single() @@ -132,7 +133,7 @@ namespace Test2.Namespace.App Dim project = projects(0) Dim syncService = project.GetLanguageService(Of ISyncNamespacesService)() - Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CancellationToken.None) + Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CodeActionOptions.Default, CancellationToken.None) Dim solutionChanges = workspace.CurrentSolution.GetChanges(newSolution) @@ -186,7 +187,7 @@ namespace Test2.Namespace.App Dim document = project.Documents.Single() Dim syncService = project.GetLanguageService(Of ISyncNamespacesService)() - Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CancellationToken.None) + Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CodeActionOptions.Default, CancellationToken.None) Dim solutionChanges = workspace.CurrentSolution.GetChanges(newSolution) Dim projectChanges = solutionChanges.GetProjectChanges().Single() @@ -251,7 +252,7 @@ namespace Test2.Namespace Dim document2 = project2.Documents.Single() Dim syncService = project.GetLanguageService(Of ISyncNamespacesService)() - Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CancellationToken.None) + Dim newSolution = Await syncService.SyncNamespacesAsync(projects, CodeActionOptions.Default, CancellationToken.None) Dim solutionChanges = workspace.CurrentSolution.GetChanges(newSolution) Dim projectChanges = solutionChanges.GetProjectChanges().ToImmutableArray() diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 28d2a8ba5578a..401517ac5ebe0 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -74,11 +74,11 @@ public CodeFixService( } public async Task GetMostSevereFixableDiagnosticAsync( - Document document, TextSpan range, CancellationToken cancellationToken) + Document document, TextSpan range, CodeActionOptions options, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - if (document == null || !document.IsOpen()) + if (!document.IsOpen()) { return default; } @@ -98,12 +98,12 @@ public async Task GetMostSevereFixableDiagnosticAsync( // Kick off a task that will determine there's an Error Diagnostic with a fixer var errorDiagnosticsTask = Task.Run( - () => GetFirstDiagnosticWithFixAsync(document, errorDiagnostics, range, linkedToken), + () => GetFirstDiagnosticWithFixAsync(document, errorDiagnostics, range, options, linkedToken), linkedToken); // Kick off a task that will determine if any non-Error Diagnostic has a fixer var otherDiagnosticsTask = Task.Run( - () => GetFirstDiagnosticWithFixAsync(document, otherDiagnostics, range, linkedToken), + () => GetFirstDiagnosticWithFixAsync(document, otherDiagnostics, range, options, linkedToken), linkedToken); // If the error diagnostics task happens to complete with a non-null result before @@ -121,6 +121,7 @@ public async Task GetMostSevereFixableDiagnosticAsync( Document document, IEnumerable severityGroup, TextSpan range, + CodeActionOptions options, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -132,7 +133,7 @@ public async Task GetMostSevereFixableDiagnosticAsync( continue; } - if (await ContainsAnyFixAsync(document, diagnostic, cancellationToken).ConfigureAwait(false)) + if (await ContainsAnyFixAsync(document, diagnostic, options, cancellationToken).ConfigureAwait(false)) { return diagnostic; } @@ -214,7 +215,7 @@ public async IAsyncEnumerable StreamFixesAsync( foreach (var (span, diagnosticList) in aggregatedDiagnostics) { await foreach (var codeFixCollection in StreamConfigurationFixesAsync( - document, span, diagnosticList, registeredConfigurationFixTitles, cancellationToken).ConfigureAwait(false)) + document, span, diagnosticList, registeredConfigurationFixTitles, options, cancellationToken).ConfigureAwait(false)) { yield return codeFixCollection; } @@ -458,6 +459,7 @@ private async IAsyncEnumerable StreamFixesAsync( } } }, + options, cancellationToken).ConfigureAwait(false); if (codeFixCollection != null) @@ -604,6 +606,7 @@ private async IAsyncEnumerable StreamConfigurationFixesAsync( TextSpan diagnosticsSpan, IEnumerable diagnostics, PooledHashSet registeredConfigurationFixTitles, + CodeActionOptions options, [EnumeratorCancellation] CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -627,6 +630,7 @@ private async IAsyncEnumerable StreamConfigurationFixesAsync( var fixes = await provider.GetFixesAsync(document, diagnosticsSpan, dxs, cancellationToken).ConfigureAwait(false); return fixes.WhereAsArray(f => registeredConfigurationFixTitles.Add(f.Action.Title)); }, + options, cancellationToken).ConfigureAwait(false); if (codeFixCollection != null) yield return codeFixCollection; @@ -642,6 +646,7 @@ private async IAsyncEnumerable StreamConfigurationFixesAsync( TCodeFixProvider fixer, Func hasFix, Func, Task>> getFixes, + CodeActionOptions options, CancellationToken cancellationToken) where TCodeFixProvider : notnull { @@ -701,13 +706,19 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) : (FixAllContext.DiagnosticProvider)new FixAllDiagnosticProvider(this, diagnosticIdsForDiagnosticProvider, includeSuppressedDiagnostics); fixAllState = new FixAllState( - fixAllProvider: fixAllProviderInfo.FixAllProvider, - document: document, - codeFixProvider: codeFixProvider, - scope: FixAllScope.Document, - codeActionEquivalenceKey: fixes[0].Action.EquivalenceKey, - diagnosticIds: diagnosticIds, - fixAllDiagnosticProvider: diagnosticProvider); + fixAllProviderInfo.FixAllProvider, + document, + document.Project, + codeFixProvider, + FixAllScope.Document, + fixes[0].Action.EquivalenceKey, + diagnosticIds, + diagnosticProvider, + codeActionOptionsProvider: language => + { + Contract.ThrowIfFalse(language == document.Project.Language); + return options; + }); supportedScopes = fixAllProviderInfo.SupportedScopes; } @@ -769,13 +780,13 @@ private async Task> GetProjectDiagnosticsAsync(Project p } private async Task ContainsAnyFixAsync( - Document document, DiagnosticData diagnostic, CancellationToken cancellationToken) + Document document, DiagnosticData diagnosticData, CodeActionOptions options, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var workspaceFixers = ImmutableArray.Empty; - var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap) && fixerMap.Value.TryGetValue(diagnostic.Id, out workspaceFixers); - var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnostic.Id, out var projectFixers); + var hasAnySharedFixer = TryGetWorkspaceFixersMap(document, out var fixerMap) && fixerMap.Value.TryGetValue(diagnosticData.Id, out workspaceFixers); + var hasAnyProjectFixer = GetProjectFixers(document.Project).TryGetValue(diagnosticData.Id, out var projectFixers); // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive if (hasAnySharedFixer && document.Project.Solution.Workspace.Kind == WorkspaceKind.Interactive) @@ -804,13 +815,13 @@ private async Task ContainsAnyFixAsync( allFixers = allFixers.AddRange(projectFixers!); } - var dx = await diagnostic.ToDiagnosticAsync(document.Project, cancellationToken).ConfigureAwait(false); + var diagnostic = await diagnosticData.ToDiagnosticAsync(document.Project, cancellationToken).ConfigureAwait(false); if (hasConfigurationFixer) { foreach (var lazyConfigurationProvider in lazyConfigurationProviders!.Value) { - if (lazyConfigurationProvider.IsFixableDiagnostic(dx)) + if (lazyConfigurationProvider.IsFixableDiagnostic(diagnostic)) { return true; } @@ -818,7 +829,7 @@ private async Task ContainsAnyFixAsync( } var fixes = new List(); - var context = new CodeFixContext(document, dx, + var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), // TODO: Can we share code between similar lambdas that we pass to this API in BatchFixAllProvider.cs, CodeFixService.cs and CodeRefactoringService.cs? (action, applicableDiagnostics) => @@ -829,7 +840,8 @@ private async Task ContainsAnyFixAsync( fixes.Add(new CodeFix(document.Project, action, applicableDiagnostics)); } }, - cancellationToken: cancellationToken); + options, + cancellationToken); var extensionManager = document.Project.Solution.Workspace.Services.GetRequiredService(); diff --git a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs index 4e0da11fa4ddd..73409995108b4 100644 --- a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs +++ b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Threading; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.CodeFixes @@ -21,6 +22,7 @@ Solution GetFix( Workspace workspace, CodeFixProvider fixProvider, FixAllProvider fixAllProvider, + CodeActionOptionsProvider optionsProvider, string equivalenceKey, string waitDialogTitle, string waitDialogMessage, @@ -35,6 +37,7 @@ Solution GetFix( Workspace workspace, CodeFixProvider fixProvider, FixAllProvider fixAllProvider, + CodeActionOptionsProvider optionsProvider, string equivalenceKey, string waitDialogTitle, string waitDialogMessage, diff --git a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs b/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs index fc04f0104fd90..1f2109bc9fe6c 100644 --- a/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/ICodeFixService.cs @@ -20,7 +20,7 @@ internal interface ICodeFixService Task GetDocumentFixAllForIdInSpanAsync(Document document, TextSpan textSpan, string diagnosticId, CodeActionOptions options, CancellationToken cancellationToken); Task ApplyCodeFixesForSpecificDiagnosticIdAsync(Document document, string diagnosticId, IProgressTracker progressTracker, CodeActionOptions options, CancellationToken cancellationToken); CodeFixProvider? GetSuppressionFixer(string language, IEnumerable diagnosticIds); - Task GetMostSevereFixableDiagnosticAsync(Document document, TextSpan range, CancellationToken cancellationToken); + Task GetMostSevereFixableDiagnosticAsync(Document document, TextSpan range, CodeActionOptions options, CancellationToken cancellationToken); } internal static class ICodeFixServiceExtensions diff --git a/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesSevice.cs b/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesSevice.cs index c03fd846ed1ed..922d3db1a794d 100644 --- a/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesSevice.cs +++ b/src/Features/Core/Portable/SyncNamespaces/AbstractSyncNamespacesSevice.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,6 +14,7 @@ using Microsoft.CodeAnalysis.CodeFixes.MatchFolderAndNamespace; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SyncNamespaces @@ -28,8 +30,12 @@ internal abstract class AbstractSyncNamespacesSevice public async Task SyncNamespacesAsync( ImmutableArray projects, + CodeActionOptions options, CancellationToken cancellationToken) { + // all projects must be of the same language + Debug.Assert(projects.All(project => project.Language == projects[0].Language)); + var solution = projects[0].Solution; var diagnosticAnalyzers = ImmutableArray.Create(DiagnosticAnalyzer); var diagnosticsByProject = await GetDiagnosticsByProjectAsync(projects, diagnosticAnalyzers, cancellationToken).ConfigureAwait(false); @@ -40,7 +46,7 @@ public async Task SyncNamespacesAsync( return solution; } - var fixAllContext = await GetFixAllContextAsync(solution, CodeFixProvider, diagnosticsByProject, cancellationToken).ConfigureAwait(false); + var fixAllContext = await GetFixAllContextAsync(solution, CodeFixProvider, diagnosticsByProject, options, cancellationToken).ConfigureAwait(false); var fixAllProvider = CodeFixProvider.GetFixAllProvider(); RoslynDebug.AssertNotNull(fixAllProvider); @@ -86,6 +92,7 @@ private static async Task GetFixAllContextAsync( Solution solution, CodeFixProvider codeFixProvider, ImmutableDictionary> diagnosticsByProject, + CodeActionOptions options, CancellationToken cancellationToken) { var diagnosticProvider = new DiagnosticProvider(diagnosticsByProject); @@ -101,19 +108,26 @@ private static async Task GetFixAllContextAsync( CodeAction? action = null; var context = new CodeFixContext( document, - firstDiagnostic, + firstDiagnostic.Location.SourceSpan, + ImmutableArray.Create(firstDiagnostic), (a, _) => action ??= a, + options, cancellationToken); await codeFixProvider.RegisterCodeFixesAsync(context).ConfigureAwait(false); return new FixAllContext( - document: document, - codeFixProvider: codeFixProvider, - scope: FixAllScope.Solution, - codeActionEquivalenceKey: action?.EquivalenceKey!, // FixAllState supports null equivalence key. This should still be supported. - diagnosticIds: codeFixProvider.FixableDiagnosticIds, - fixAllDiagnosticProvider: diagnosticProvider, - cancellationToken: cancellationToken); + new FixAllState( + fixAllProvider: null, + document, + document.Project, + codeFixProvider, + FixAllScope.Solution, + codeActionEquivalenceKey: action?.EquivalenceKey!, // FixAllState supports null equivalence key. This should still be supported. + diagnosticIds: codeFixProvider.FixableDiagnosticIds, + fixAllDiagnosticProvider: diagnosticProvider, + _ => options), + new ProgressTracker(), + cancellationToken); } private static async Task ApplyCodeFixAsync( diff --git a/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs b/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs index c309e8391ecdb..a408b4f60b5c7 100644 --- a/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs +++ b/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.SyncNamespaces @@ -15,6 +16,6 @@ internal interface ISyncNamespacesService : ILanguageService /// This will update documents in the specified projects so that their namespace matches the RootNamespace /// and their relative folder path. /// - Task SyncNamespacesAsync(ImmutableArray projects, CancellationToken cancellationToken); + Task SyncNamespacesAsync(ImmutableArray projects, CodeActionOptions options, CancellationToken cancellationToken); } } diff --git a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/AbstractCodeCleanUpFixer.cs b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/AbstractCodeCleanUpFixer.cs index 347f758405c71..341662e19165b 100644 --- a/src/VisualStudio/Core/Def/Implementation/CodeCleanup/AbstractCodeCleanUpFixer.cs +++ b/src/VisualStudio/Core/Def/Implementation/CodeCleanup/AbstractCodeCleanUpFixer.cs @@ -133,7 +133,7 @@ private async Task FixHierarchyContentAsync(IVsHierarchyCodeCleanupScope h } var document = solution.GetRequiredDocument(documentId); - var options = _globalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = _globalOptions.GetCodeActionOptions(document.Project.Language); return await FixDocumentAsync(document, options, context).ConfigureAwait(true); } } @@ -202,7 +202,7 @@ async Task ApplyFixAsync(ProgressTracker progressTracker, Cancellation var document = buffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); Contract.ThrowIfNull(document); - var options = _globalOptions.GetCodeActionOptions(document.Project.Language, isBlocking: false); + var options = _globalOptions.GetCodeActionOptions(document.Project.Language); var newDoc = await FixDocumentAsync(document, context.EnabledFixIds, progressTracker, options, cancellationToken).ConfigureAwait(true); return newDoc.Project.Solution; } @@ -289,7 +289,7 @@ private async Task FixProjectAsync( progressTracker.AddItems(project.DocumentIds.Count); } - var options = _globalOptions.GetCodeActionOptions(project.Language, isBlocking: false); + var options = _globalOptions.GetCodeActionOptions(project.Language); foreach (var documentId in project.DocumentIds) { diff --git a/src/VisualStudio/Core/Def/Implementation/SyncNamespaces/SyncNamespacesCommandHandler.cs b/src/VisualStudio/Core/Def/Implementation/SyncNamespaces/SyncNamespacesCommandHandler.cs index 768fb57a1bf65..431282f6f70d9 100644 --- a/src/VisualStudio/Core/Def/Implementation/SyncNamespaces/SyncNamespacesCommandHandler.cs +++ b/src/VisualStudio/Core/Def/Implementation/SyncNamespaces/SyncNamespacesCommandHandler.cs @@ -8,7 +8,9 @@ using System.Composition; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SyncNamespaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; @@ -25,16 +27,19 @@ internal sealed class SyncNamespacesCommandHandler { private readonly VisualStudioWorkspace _workspace; private readonly IUIThreadOperationExecutor _threadOperationExecutor; + private readonly IGlobalOptionService _globalOptions; private IServiceProvider? _serviceProvider; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public SyncNamespacesCommandHandler( IUIThreadOperationExecutor threadOperationExecutor, - VisualStudioWorkspace workspace) + VisualStudioWorkspace workspace, + IGlobalOptionService globalOptions) { _threadOperationExecutor = threadOperationExecutor; _workspace = workspace; + _globalOptions = globalOptions; } public void Initialize(IServiceProvider serviceProvider) @@ -120,11 +125,12 @@ private void SyncNamespaces(ImmutableArray projects) } var syncService = projects[0].GetRequiredLanguageService(); + var options = _globalOptions.GetCodeActionOptions(projects[0].Language); Solution? solution = null; var status = _threadOperationExecutor.Execute(ServicesVSResources.Sync_Namespaces, ServicesVSResources.Updating_namspaces, allowCancellation: true, showProgress: true, (operationContext) => { - solution = ThreadHelper.JoinableTaskFactory.Run(() => syncService.SyncNamespacesAsync(projects, operationContext.UserCancellationToken)); + solution = ThreadHelper.JoinableTaskFactory.Run(() => syncService.SyncNamespacesAsync(projects, options, operationContext.UserCancellationToken)); }); if (status != UIThreadOperationStatus.Canceled && solution is not null) diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index 04908186f14ae..7b1536d7f6a5e 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Editor.Implementation.Suggestions; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; @@ -52,6 +53,7 @@ internal sealed class VisualStudioSuppressionFixService : IVisualStudioSuppressi private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor; private readonly IVsHierarchyItemManager _vsHierarchyItemManager; private readonly IHierarchyItemToProjectIdMap _projectMap; + private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -64,7 +66,8 @@ public VisualStudioSuppressionFixService( IVisualStudioDiagnosticListSuppressionStateService suppressionStateService, IUIThreadOperationExecutor uiThreadOperationExecutor, IVsHierarchyItemManager vsHierarchyItemManager, - IAsynchronousOperationListenerProvider listenerProvider) + IAsynchronousOperationListenerProvider listenerProvider, + IGlobalOptionService globalOptions) { _workspace = workspace; _diagnosticService = diagnosticService; @@ -80,6 +83,7 @@ public VisualStudioSuppressionFixService( var errorList = serviceProvider.GetService(typeof(SVsErrorList)) as IErrorList; _tableControl = errorList?.TableControl; _listener = listenerProvider.GetListener(FeatureAttribute.ErrorList); + _globalOptions = globalOptions; } public bool AddSuppressions(IVsHierarchy? projectHierarchy) @@ -295,6 +299,9 @@ private async Task ApplySuppressionFixAsync(IEnumerable? diagnos cancellationToken.ThrowIfCancellationRequested(); + var options = _globalOptions.GetCodeActionOptions(language); + var optionsProvider = new CodeActionOptionsProvider(_ => options); + var documentDiagnosticsPerLanguage = GetDocumentDiagnosticsMappedToNewSolution(documentDiagnosticsToFixMap, newSolution, language); if (!documentDiagnosticsPerLanguage.IsEmpty) { @@ -307,6 +314,7 @@ private async Task ApplySuppressionFixAsync(IEnumerable? diagnos _workspace, suppressionFixer, suppressionFixAllProvider, + optionsProvider, equivalenceKey, title, waitDialogMessage, @@ -331,6 +339,7 @@ private async Task ApplySuppressionFixAsync(IEnumerable? diagnos _workspace, suppressionFixer, suppressionFixAllProvider, + optionsProvider, equivalenceKey, title, waitDialogMessage, diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs index b92e886034764..d439d0a007fff 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/BatchFixAllProvider.cs @@ -141,13 +141,15 @@ private static async Task> GetAllChangedDocumentsInDiag foreach (var diagnostic in orderedDiagnostics) { var document = solution.GetRequiredDocument(diagnostic.Location.SourceTree!); + var options = fixAllContext.State.CodeActionOptionsProvider(document.Project.Language) with { IsBlocking = false }; cancellationToken.ThrowIfCancellationRequested(); tasks.Add(Task.Run(async () => { // Create a context that will add the reported code actions into this using var _2 = ArrayBuilder.GetInstance(out var codeActions); - var context = new CodeFixContext(document, diagnostic, GetRegisterCodeFixAction(fixAllContext.CodeActionEquivalenceKey, codeActions), cancellationToken); + var action = GetRegisterCodeFixAction(fixAllContext.CodeActionEquivalenceKey, codeActions); + var context = new CodeFixContext(document, diagnostic.Location.SourceSpan, ImmutableArray.Create(diagnostic), action, options, cancellationToken); // Wait for the all the code actions to be reported for this diagnostic. var registerTask = fixAllContext.CodeFixProvider.RegisterCodeFixesAsync(context) ?? Task.CompletedTask; diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs index ed5dac078c342..04d8e28ab02bd 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/DefaultFixAllProviderHelpers.cs @@ -48,7 +48,7 @@ internal static class DefaultFixAllProviderHelpers => fixAllContextsAsync(fixAllContext, ImmutableArray.Create(fixAllContext)); private static Task GetProjectFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) - => fixAllContextsAsync(fixAllContext, ImmutableArray.Create(fixAllContext.WithDocument(null))); + => fixAllContextsAsync(fixAllContext, ImmutableArray.Create(fixAllContext.WithDocumentAndProject(document: null, fixAllContext.Project))); private static Task GetSolutionFixesAsync(FixAllContext fixAllContext, FixAllContexts fixAllContextsAsync) { @@ -72,7 +72,7 @@ internal static class DefaultFixAllProviderHelpers .Where(p => p.Language == fixAllContext.Project.Language); return fixAllContextsAsync( fixAllContext, - sortedProjects.SelectAsArray(p => fixAllContext.WithScope(FixAllScope.Project).WithProject(p).WithDocument(null))); + sortedProjects.SelectAsArray(p => fixAllContext.WithScope(FixAllScope.Project).WithDocumentAndProject(document: null, p))); } } } diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs index bdec6d9d3d771..eec325ceba25f 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs @@ -34,8 +34,7 @@ public partial class FixAllContext public Project Project => State.Project; /// - /// Document within which fix all occurrences was triggered. - /// Can be null if the context was created using . + /// Document within which fix all occurrences was triggered, null if the is scoped to a project. /// public Document? Document => State.Document; @@ -85,17 +84,22 @@ public FixAllContext( Document document, CodeFixProvider codeFixProvider, FixAllScope scope, - string codeActionEquivalenceKey, + string? codeActionEquivalenceKey, IEnumerable diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) - : this(new FixAllState(null, document, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider), + : this(new FixAllState( + fixAllProvider: null, + document ?? throw new ArgumentNullException(nameof(document)), + document.Project, + codeFixProvider ?? throw new ArgumentNullException(nameof(codeFixProvider)), + scope, + codeActionEquivalenceKey, + PublicContract.RequireNonNullItems(diagnosticIds, nameof(diagnosticIds)), + fixAllDiagnosticProvider ?? throw new ArgumentNullException(nameof(fixAllDiagnosticProvider)), + _ => CodeActionOptions.Default), new ProgressTracker(), cancellationToken) { - if (document == null) - { - throw new ArgumentNullException(nameof(document)); - } } /// @@ -115,17 +119,22 @@ public FixAllContext( Project project, CodeFixProvider codeFixProvider, FixAllScope scope, - string codeActionEquivalenceKey, + string? codeActionEquivalenceKey, IEnumerable diagnosticIds, DiagnosticProvider fixAllDiagnosticProvider, CancellationToken cancellationToken) - : this(new FixAllState(null, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider), + : this(new FixAllState( + fixAllProvider: null, + document: null, + project ?? throw new ArgumentNullException(nameof(project)), + codeFixProvider ?? throw new ArgumentNullException(nameof(codeFixProvider)), + scope, + codeActionEquivalenceKey, + PublicContract.RequireNonNullItems(diagnosticIds, nameof(diagnosticIds)), + fixAllDiagnosticProvider ?? throw new ArgumentNullException(nameof(fixAllDiagnosticProvider)), + _ => CodeActionOptions.Default), new ProgressTracker(), cancellationToken) { - if (project == null) - { - throw new ArgumentNullException(nameof(project)); - } } internal FixAllContext( @@ -235,11 +244,8 @@ public FixAllContext WithCancellationToken(CancellationToken cancellationToken) internal FixAllContext WithScope(FixAllScope scope) => this.WithState(State.WithScope(scope)); - internal FixAllContext WithProject(Project project) - => this.WithState(State.WithProject(project)); - - internal FixAllContext WithDocument(Document? document) - => this.WithState(State.WithDocument(document)); + internal FixAllContext WithDocumentAndProject(Document? document, Project project) + => this.WithState(State.WithDocumentAndProject(document, project)); private FixAllContext WithState(FixAllState state) => this.State == state ? this : new FixAllContext(state, ProgressTracker, CancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PublicContract.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PublicContract.cs index c0867d061073c..d8acb0c5d347b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PublicContract.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PublicContract.cs @@ -24,7 +24,7 @@ internal static class PublicContract // while keeping the rarely executed code in a separate method. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void RequireNonNullItems([NotNull] IEnumerable? sequence, string argumentName) where T : class + internal static IEnumerable RequireNonNullItems([NotNull] IEnumerable? sequence, string argumentName) where T : class { if (sequence == null) { @@ -35,6 +35,8 @@ internal static void RequireNonNullItems([NotNull] IEnumerable? sequence, { ThrowArgumentItemNullException(sequence, argumentName); } + + return sequence; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeActionOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs similarity index 79% rename from src/Workspaces/Core/Portable/CodeActions/CodeActionOptions.cs rename to src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs index 449e09b2c74c7..7a27888f664b8 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeActionOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs @@ -7,6 +7,14 @@ namespace Microsoft.CodeAnalysis.CodeActions { +#if CODE_STYLE + /// + /// Empty type to avoid excessive ifdefs. + /// + internal readonly struct CodeActionOptions + { + } +#else /// /// Options available to code fixes that are supplied by the IDE (i.e. not stored in editorconfig). /// @@ -23,4 +31,7 @@ public CodeActionOptions() public static readonly CodeActionOptions Default = new(); } +#endif + + internal delegate CodeActionOptions CodeActionOptionsProvider(string language); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/FixAll/FixAllState.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/FixAll/FixAllState.cs index 3d41be7ecef7c..1b0fdac271bba 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/FixAll/FixAllState.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/FixAll/FixAllState.cs @@ -5,17 +5,19 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Internal.Log; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes { - internal partial class FixAllState + internal sealed partial class FixAllState { - internal readonly int CorrelationId = LogAggregator.GetNextId(); + public readonly int CorrelationId = LogAggregator.GetNextId(); - internal FixAllContext.DiagnosticProvider DiagnosticProvider { get; } + public FixAllContext.DiagnosticProvider DiagnosticProvider { get; } public FixAllProvider? FixAllProvider { get; } public string? CodeActionEquivalenceKey { get; } @@ -24,41 +26,9 @@ internal partial class FixAllState public Document? Document { get; } public Project Project { get; } public FixAllScope Scope { get; } - public Solution Solution => this.Project.Solution; + public CodeActionOptionsProvider CodeActionOptionsProvider { get; } internal FixAllState( - FixAllProvider? fixAllProvider, - Document document, - CodeFixProvider codeFixProvider, - FixAllScope scope, - string? codeActionEquivalenceKey, - IEnumerable diagnosticIds, - FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) - : this(fixAllProvider, document, document.Project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider) - { - if (document == null) - { - throw new ArgumentNullException(nameof(document)); - } - } - - internal FixAllState( - FixAllProvider? fixAllProvider, - Project project, - CodeFixProvider codeFixProvider, - FixAllScope scope, - string? codeActionEquivalenceKey, - IEnumerable diagnosticIds, - FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) - : this(fixAllProvider, null, project, codeFixProvider, scope, codeActionEquivalenceKey, diagnosticIds, fixAllDiagnosticProvider) - { - if (project == null) - { - throw new ArgumentNullException(nameof(project)); - } - } - - private FixAllState( FixAllProvider? fixAllProvider, Document? document, Project project, @@ -66,71 +36,61 @@ private FixAllState( FixAllScope scope, string? codeActionEquivalenceKey, IEnumerable diagnosticIds, - FixAllContext.DiagnosticProvider fixAllDiagnosticProvider) + FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, + CodeActionOptionsProvider codeActionOptionsProvider) { - Contract.ThrowIfNull(project); - if (diagnosticIds == null) - { - throw new ArgumentNullException(nameof(diagnosticIds)); - } - - if (diagnosticIds.Any(d => d == null)) - { - throw new ArgumentException(WorkspaceExtensionsResources.Supplied_diagnostic_cannot_be_null, nameof(diagnosticIds)); - } - - this.FixAllProvider = fixAllProvider; - this.Document = document; - this.Project = project; - this.CodeFixProvider = codeFixProvider ?? throw new ArgumentNullException(nameof(codeFixProvider)); - this.Scope = scope; - this.CodeActionEquivalenceKey = codeActionEquivalenceKey; - this.DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); - this.DiagnosticProvider = fixAllDiagnosticProvider ?? throw new ArgumentNullException(nameof(fixAllDiagnosticProvider)); + Debug.Assert(document == null || document.Project == project); + + FixAllProvider = fixAllProvider; + Document = document; + Project = project; + CodeFixProvider = codeFixProvider; + Scope = scope; + CodeActionEquivalenceKey = codeActionEquivalenceKey; + DiagnosticIds = ImmutableHashSet.CreateRange(diagnosticIds); + DiagnosticProvider = fixAllDiagnosticProvider; + CodeActionOptionsProvider = codeActionOptionsProvider; } - internal bool IsFixMultiple => this.DiagnosticProvider is FixMultipleDiagnosticProvider; + public Solution Solution => Project.Solution; + internal bool IsFixMultiple => DiagnosticProvider is FixMultipleDiagnosticProvider; public FixAllState WithScope(FixAllScope scope) - => this.With(scope: scope); + => With(scope: scope); - public FixAllState WithCodeActionEquivalenceKey(string codeActionEquivalenceKey) - => this.With(codeActionEquivalenceKey: codeActionEquivalenceKey); + public FixAllState WithCodeActionEquivalenceKey(string? codeActionEquivalenceKey) + => With(codeActionEquivalenceKey: codeActionEquivalenceKey); - public FixAllState WithProject(Project project) - => this.With(project: project); - - public FixAllState WithDocument(Document? document) - => this.With(document: document); + public FixAllState WithDocumentAndProject(Document? document, Project project) + => With(documentAndProject: (document, project)); public FixAllState With( - Optional document = default, - Optional project = default, + Optional<(Document? document, Project project)> documentAndProject = default, Optional scope = default, Optional codeActionEquivalenceKey = default) { - var newDocument = document.HasValue ? document.Value : this.Document; - var newProject = project.HasValue ? project.Value : this.Project; - var newScope = scope.HasValue ? scope.Value : this.Scope; - var newCodeActionEquivalenceKey = codeActionEquivalenceKey.HasValue ? codeActionEquivalenceKey.Value : this.CodeActionEquivalenceKey; - - if (newDocument == this.Document && - newProject == this.Project && - newScope == this.Scope && - newCodeActionEquivalenceKey == this.CodeActionEquivalenceKey) + var (newDocument, newProject) = documentAndProject.HasValue ? documentAndProject.Value : (Document, Project); + var newScope = scope.HasValue ? scope.Value : Scope; + var newCodeActionEquivalenceKey = codeActionEquivalenceKey.HasValue ? codeActionEquivalenceKey.Value : CodeActionEquivalenceKey; + + if (newDocument == Document && + newProject == Project && + newScope == Scope && + newCodeActionEquivalenceKey == CodeActionEquivalenceKey) { return this; } return new FixAllState( - this.FixAllProvider, + FixAllProvider, newDocument, newProject, - this.CodeFixProvider, + CodeFixProvider, newScope, newCodeActionEquivalenceKey, - this.DiagnosticIds, - this.DiagnosticProvider); + DiagnosticIds, + DiagnosticProvider, + CodeActionOptionsProvider); } #region FixMultiple @@ -139,32 +99,44 @@ internal static FixAllState Create( FixAllProvider fixAllProvider, ImmutableDictionary> diagnosticsToFix, CodeFixProvider codeFixProvider, - string codeActionEquivalenceKey) + string? codeActionEquivalenceKey, + CodeActionOptionsProvider codeActionOptionsProvider) { var triggerDocument = diagnosticsToFix.First().Key; var diagnosticIds = GetDiagnosticsIds(diagnosticsToFix.Values); var diagnosticProvider = new FixMultipleDiagnosticProvider(diagnosticsToFix); return new FixAllState( fixAllProvider, - triggerDocument, codeFixProvider, - FixAllScope.Custom, codeActionEquivalenceKey, - diagnosticIds, diagnosticProvider); + triggerDocument, + triggerDocument.Project, + codeFixProvider, + FixAllScope.Custom, + codeActionEquivalenceKey, + diagnosticIds, + diagnosticProvider, + codeActionOptionsProvider); } internal static FixAllState Create( FixAllProvider fixAllProvider, ImmutableDictionary> diagnosticsToFix, CodeFixProvider codeFixProvider, - string codeActionEquivalenceKey) + string? codeActionEquivalenceKey, + CodeActionOptionsProvider codeActionOptionsProvider) { var triggerProject = diagnosticsToFix.First().Key; var diagnosticIds = GetDiagnosticsIds(diagnosticsToFix.Values); var diagnosticProvider = new FixMultipleDiagnosticProvider(diagnosticsToFix); return new FixAllState( fixAllProvider, - triggerProject, codeFixProvider, - FixAllScope.Custom, codeActionEquivalenceKey, - diagnosticIds, diagnosticProvider); + document: null, + triggerProject, + codeFixProvider, + FixAllScope.Custom, + codeActionEquivalenceKey, + diagnosticIds, + diagnosticProvider, + codeActionOptionsProvider); } private static ImmutableHashSet GetDiagnosticsIds(IEnumerable> diagnosticsCollection) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems index fd07f372126b2..dbc41c00a72b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems @@ -9,6 +9,7 @@ Microsoft.CodeAnalysis.Shared + From abd4cae00496231925ac50c6118d9126201c608c Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 18 Feb 2022 16:55:11 -0800 Subject: [PATCH 112/187] Fix null-ref in `AliasSymbol.Equals` (#59649) --- .../CSharp/Portable/Symbols/AliasSymbol.cs | 17 ++++---- .../Compilation/SemanticModelAPITests.cs | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs index 6a1dd783477c9..35ede5f6be75e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs @@ -50,11 +50,12 @@ internal abstract class AliasSymbol : Symbol private readonly ImmutableArray _locations; // NOTE: can be empty for the "global" alias. private readonly string _aliasName; private readonly bool _isExtern; - private readonly Symbol? _containingSymbol; + private readonly Symbol _containingSymbol; - protected AliasSymbol(string aliasName, Symbol? containingSymbol, ImmutableArray locations, bool isExtern) + protected AliasSymbol(string aliasName, Symbol containingSymbol, ImmutableArray locations, bool isExtern) { Debug.Assert(locations.Length == 1 || (locations.IsEmpty && aliasName == "global")); // It looks like equality implementation depends on this condition. + Debug.Assert(containingSymbol is not null); _locations = locations; _aliasName = aliasName; @@ -69,7 +70,7 @@ internal static AliasSymbol CreateGlobalNamespaceAlias(NamespaceSymbol globalNam return new AliasSymbolFromResolvedTarget(globalNamespace, "global", globalNamespace, ImmutableArray.Empty, isExtern: false); } - internal static AliasSymbol CreateCustomDebugInfoAlias(NamespaceOrTypeSymbol targetSymbol, SyntaxToken aliasToken, Symbol? containingSymbol, bool isExtern) + internal static AliasSymbol CreateCustomDebugInfoAlias(NamespaceOrTypeSymbol targetSymbol, SyntaxToken aliasToken, Symbol containingSymbol, bool isExtern) { return new AliasSymbolFromResolvedTarget(targetSymbol, aliasToken.ValueText, containingSymbol, ImmutableArray.Create(aliasToken.GetLocation()), isExtern); } @@ -200,7 +201,7 @@ public override Accessibility DeclaredAccessibility /// return that as the "containing" symbol, even though the alias isn't a member of the /// namespace as such. /// - public sealed override Symbol? ContainingSymbol + public sealed override Symbol ContainingSymbol { get { @@ -253,7 +254,7 @@ public override bool Equals(Symbol? obj, TypeCompareKind compareKind) return (object?)other != null && Equals(this.Locations.FirstOrDefault(), other.Locations.FirstOrDefault()) && - this.ContainingAssembly.Equals(other.ContainingAssembly, compareKind); + Equals(this.ContainingSymbol, other.ContainingSymbol, compareKind); } public override int GetHashCode() @@ -358,7 +359,7 @@ internal BindingDiagnosticBag AliasTargetDiagnostics private NamespaceSymbol ResolveExternAliasTarget(BindingDiagnosticBag diagnostics) { NamespaceSymbol? target; - if (!ContainingSymbol!.DeclaringCompilation.GetExternAliasTarget(Name, out target)) + if (!ContainingSymbol.DeclaringCompilation.GetExternAliasTarget(Name, out target)) { diagnostics.Add(ErrorCode.ERR_BadExternAlias, Locations[0], Name); } @@ -371,7 +372,7 @@ private NamespaceSymbol ResolveExternAliasTarget(BindingDiagnosticBag diagnostic private NamespaceOrTypeSymbol ResolveAliasTarget(NameSyntax syntax, BindingDiagnosticBag diagnostics, ConsList? basesBeingResolved) { - var declarationBinder = ContainingSymbol!.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax).WithAdditionalFlags(BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressObsoleteChecks); + var declarationBinder = ContainingSymbol.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax).WithAdditionalFlags(BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressObsoleteChecks); return declarationBinder.BindNamespaceOrTypeSymbol(syntax, diagnostics, basesBeingResolved).NamespaceOrTypeSymbol; } @@ -385,7 +386,7 @@ internal sealed class AliasSymbolFromResolvedTarget : AliasSymbol { private readonly NamespaceOrTypeSymbol _aliasTarget; - internal AliasSymbolFromResolvedTarget(NamespaceOrTypeSymbol target, string aliasName, Symbol? containingSymbol, ImmutableArray locations, bool isExtern) + internal AliasSymbolFromResolvedTarget(NamespaceOrTypeSymbol target, string aliasName, Symbol containingSymbol, ImmutableArray locations, bool isExtern) : base(aliasName, containingSymbol, locations, isExtern) { _aliasTarget = target; diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index 975370bae1e53..6387a4000b808 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -4450,6 +4450,46 @@ void M() { } Assert.Equal("DEBUG", model.GetConstantValue(root.DescendantNodes().OfType().Single())); } + [Fact] + public void EqualsOnAliasSymbolWithNullContainingAssembly_NotEquals() + { + var text = @" +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp, Version = v6.0"", FrameworkDisplayName = """")] +"; + var alias1 = getGlobalAlias(CreateCompilation(text)); + var alias2 = getGlobalAlias(CreateCompilation(text)); + + Assert.Equal("", alias1.ContainingSymbol.ToTestDisplayString()); + Assert.Null(alias1.ContainingAssembly); + Assert.False(alias1.Equals(alias2)); + + static IAliasSymbol getGlobalAlias(CSharpCompilation compilation) + { + var tree = compilation.SyntaxTrees.Single(); + var node = tree.GetRoot().DescendantNodes().OfType().Where(ident => ident.Identifier.Text == "global").Single(); + return compilation.GetSemanticModel(tree).GetAliasInfo(node); + } + } + + [Fact] + public void EqualsOnAliasSymbolWithNullContainingAssembly_Equals() + { + var text = @" +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp, Version = v6.0"", FrameworkDisplayName = """")] +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp, Version = v6.0"", FrameworkDisplayName = """")] +"; + var compilation = CreateCompilation(text); + var tree = compilation.SyntaxTrees.Single(); + var nodes = tree.GetRoot().DescendantNodes().OfType().Where(ident => ident.Identifier.Text == "global").ToArray(); + var model = compilation.GetSemanticModel(tree); + var alias1 = model.GetAliasInfo(nodes[0]); + var alias2 = model.GetAliasInfo(nodes[1]); + + Assert.Equal("", alias1.ContainingSymbol.ToTestDisplayString()); + Assert.Null(alias1.ContainingAssembly); + Assert.True(alias1.Equals(alias2)); + } + #region "regression helper" private void Regression(string text) { From 37d9daf0e3c2327973adf00d6cd078a14e3a21dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Fri, 18 Feb 2022 20:01:14 -0800 Subject: [PATCH 113/187] Move GenerateOverridesOptions and BlockStructureOptions to global options (#59508) * GenerateOverridesOptions * BlockStructureOptionsStorage --- .../InvalidIdentifierStructureTests.cs | 2 +- .../AbstractStructureTaggerProvider.cs | 16 +-- .../LegacyGlobalOptionsWorkspaceService.cs | 26 +++++ .../Structure/BlockStructureServiceTests.cs | 2 +- .../Test/Structure/StructureTaggerTests.cs | 48 ++++---- ...bstractSyntaxNodeStructureProviderTests.cs | 2 +- .../AbstractSyntaxStructureProviderTests.cs | 48 ++++---- ...tractSyntaxTriviaStructureProviderTests.cs | 2 +- .../InvalidIdentifierStructureTests.vb | 2 +- .../Structure/OverallStructureTests.vb | 2 +- .../GenerateOverridesOptions.cs | 33 ------ .../GenerateOverridesWithDialogCodeAction.cs | 26 +++-- .../Structure/BlockStructureOptions.cs | 108 +++--------------- .../Options/BlockStructureOptionsStorage.cs | 60 ++++++++++ .../FoldingRanges/FoldingRangesHandler.cs | 8 +- ...odeAnalysis.LanguageServer.Protocol.csproj | 4 +- .../Options/AdvancedOptionPageControl.xaml.cs | 12 +- .../Options/AdvancedOptionPageControl.xaml.vb | 12 +- .../ILegacyGlobalOptionsWorkspaceService.cs | 17 +++ 19 files changed, 215 insertions(+), 215 deletions(-) create mode 100644 src/EditorFeatures/Core/Options/LegacyGlobalOptionsWorkspaceService.cs delete mode 100644 src/Features/Core/Portable/GenerateOverrides/GenerateOverridesOptions.cs create mode 100644 src/Features/LanguageServer/Protocol/Features/Options/BlockStructureOptionsStorage.cs create mode 100644 src/Workspaces/Core/Portable/Options/ILegacyGlobalOptionsWorkspaceService.cs diff --git a/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.cs index 531afa24ce496..51eaa4d96c0af 100644 --- a/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.cs @@ -29,7 +29,7 @@ public class InvalidIdentifierStructureTests : AbstractSyntaxStructureProviderTe internal override async Task> GetBlockSpansWorkerAsync(Document document, int position) { var outliningService = document.GetLanguageService(); - var options = BlockStructureOptions.From(document.Project); + var options = GetOptions(); return (await outliningService.GetBlockStructureAsync(document, options, CancellationToken.None)).Spans; } diff --git a/src/EditorFeatures/Core/Implementation/Structure/AbstractStructureTaggerProvider.cs b/src/EditorFeatures/Core/Implementation/Structure/AbstractStructureTaggerProvider.cs index 0b0e2db7167b8..db1fad90122a2 100644 --- a/src/EditorFeatures/Core/Implementation/Structure/AbstractStructureTaggerProvider.cs +++ b/src/EditorFeatures/Core/Implementation/Structure/AbstractStructureTaggerProvider.cs @@ -117,13 +117,13 @@ protected sealed override ITaggerEventSource CreateEventSource(ITextView textVie TaggerEventSources.OnTextChanged(subjectBuffer), TaggerEventSources.OnParseOptionChanged(subjectBuffer), TaggerEventSources.OnWorkspaceRegistrationChanged(subjectBuffer), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.ShowOutliningForCodeLevelConstructs), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.ShowOutliningForDeclarationLevelConstructs), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.ShowOutliningForCommentsAndPreprocessorRegions), - TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions)); + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs), + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs), + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions), + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.ShowOutliningForCodeLevelConstructs), + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.ShowOutliningForDeclarationLevelConstructs), + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.ShowOutliningForCommentsAndPreprocessorRegions), + TaggerEventSources.OnOptionChanged(subjectBuffer, BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions)); } protected sealed override async Task ProduceTagsAsync( @@ -143,7 +143,7 @@ protected sealed override async Task ProduceTagsAsync( if (outliningService == null) return; - var options = BlockStructureOptions.From(document.Project); + var options = GlobalOptions.GetBlockStructureOptions(document.Project); var blockStructure = await outliningService.GetBlockStructureAsync( documentSnapshotSpan.Document, options, cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/Core/Options/LegacyGlobalOptionsWorkspaceService.cs b/src/EditorFeatures/Core/Options/LegacyGlobalOptionsWorkspaceService.cs new file mode 100644 index 0000000000000..c51e3a6f6fc16 --- /dev/null +++ b/src/EditorFeatures/Core/Options/LegacyGlobalOptionsWorkspaceService.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.Options +{ + /// + /// Enables legacy APIs to access global options from workspace. + /// + [ExportWorkspaceService(typeof(ILegacyGlobalOptionsWorkspaceService)), Shared] + internal sealed class LegacyGlobalOptionsWorkspaceService : ILegacyGlobalOptionsWorkspaceService + { + public IGlobalOptionService GlobalOptions { get; } + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public LegacyGlobalOptionsWorkspaceService(IGlobalOptionService globalOptions) + { + GlobalOptions = globalOptions; + } + } +} diff --git a/src/EditorFeatures/Test/Structure/BlockStructureServiceTests.cs b/src/EditorFeatures/Test/Structure/BlockStructureServiceTests.cs index 99919f10f2e7b..00b8df6a0efcc 100644 --- a/src/EditorFeatures/Test/Structure/BlockStructureServiceTests.cs +++ b/src/EditorFeatures/Test/Structure/BlockStructureServiceTests.cs @@ -97,7 +97,7 @@ private static async Task> GetSpansFromWorkspaceAsync( var hostDocument = workspace.Documents.First(); var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); var outliningService = document.GetLanguageService(); - var options = BlockStructureOptions.From(document.Project); + var options = BlockStructureOptions.Default; var structure = await outliningService.GetBlockStructureAsync(document, options, CancellationToken.None); return structure.Spans; diff --git a/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs b/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs index fa71b6035e0b2..f8f24b73f8a47 100644 --- a/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs +++ b/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Text.Adornments; @@ -52,10 +53,11 @@ static void Main(string[] args) }"; using var workspace = TestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp, collapseRegionsWhenCollapsingToDefinitions) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForDeclarationLevelConstructs) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForCodeLevelConstructs))); + var globalOptions = workspace.GlobalOptions; + + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp), collapseRegionsWhenCollapsingToDefinitions); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForDeclarationLevelConstructs); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForCodeLevelConstructs); var tags = await GetTagsFromWorkspaceAsync(workspace); @@ -117,10 +119,11 @@ public class Bar "; using var workspace = TestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp, collapseRegionsWhenCollapsingToDefinitions) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForDeclarationLevelConstructs) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForCodeLevelConstructs))); + var globalOptions = workspace.GlobalOptions; + + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp), collapseRegionsWhenCollapsingToDefinitions); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForDeclarationLevelConstructs); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForCodeLevelConstructs); var tags = await GetTagsFromWorkspaceAsync(workspace); @@ -161,11 +164,12 @@ public class Bar "; using var workspace = TestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp, collapseRegionsWhenCollapsingToDefinitions) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForDeclarationLevelConstructs) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForCodeLevelConstructs) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, LanguageNames.CSharp, showBlockStructureGuidesForCommentsAndPreprocessorRegions))); + var globalOptions = workspace.GlobalOptions; + + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp), collapseRegionsWhenCollapsingToDefinitions); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForDeclarationLevelConstructs); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForCodeLevelConstructs); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, LanguageNames.CSharp), showBlockStructureGuidesForCommentsAndPreprocessorRegions); var tags = await GetTagsFromWorkspaceAsync(workspace); @@ -205,10 +209,11 @@ public class Bar "; using var workspace = TestWorkspace.CreateCSharp(code, composition: EditorTestCompositions.EditorFeaturesWpf); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp, collapseRegionsWhenCollapsingToDefinitions) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForDeclarationLevelConstructs) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp, showBlockStructureGuidesForCodeLevelConstructs))); + var globalOptions = workspace.GlobalOptions; + + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp), collapseRegionsWhenCollapsingToDefinitions); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForDeclarationLevelConstructs); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp), showBlockStructureGuidesForCodeLevelConstructs); var tags = await GetTagsFromWorkspaceAsync(workspace); @@ -256,10 +261,11 @@ End Module End Namespace"; using var workspace = TestWorkspace.CreateVisualBasic(code, composition: EditorTestCompositions.EditorFeaturesWpf); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.VisualBasic, collapseRegionsWhenCollapsingToDefinitions) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.VisualBasic, showBlockStructureGuidesForDeclarationLevelConstructs) - .WithChangedOption(BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.VisualBasic, showBlockStructureGuidesForCodeLevelConstructs))); + var globalOptions = workspace.GlobalOptions; + + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.VisualBasic), collapseRegionsWhenCollapsingToDefinitions); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.VisualBasic), showBlockStructureGuidesForDeclarationLevelConstructs); + globalOptions.SetGlobalOption(new OptionKey(BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.VisualBasic), showBlockStructureGuidesForCodeLevelConstructs); var tags = await GetTagsFromWorkspaceAsync(workspace); diff --git a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs index e4fff33a9b564..22ce86f000b9e 100644 --- a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs @@ -42,7 +42,7 @@ internal sealed override async Task> GetBlockSpansWork var outliner = CreateProvider(); using var actualRegions = TemporaryArray.Empty; - var options = BlockStructureOptions.From(document.Project); + var options = GetOptions(); // Calculate previousToken for tests the same way it is derived in production code var previousToken = root.DescendantNodesAndTokens(descendIntoTrivia: true).TakeWhile(nodeOrToken => nodeOrToken != node).LastOrDefault(nodeOrToken => nodeOrToken.IsToken).AsToken(); outliner.CollectBlockSpans(previousToken, node, ref actualRegions.AsRef(), options, CancellationToken.None); diff --git a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs index 46235413dbfe4..9174f91a13cb9 100644 --- a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs @@ -8,8 +8,8 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; @@ -24,8 +24,8 @@ public abstract class AbstractSyntaxStructureProviderTests protected virtual string WorkspaceKind => CodeAnalysis.WorkspaceKind.Host; - protected virtual OptionSet UpdateOptions(OptionSet options) - => options.WithChangedOption(BlockStructureOptions.Metadata.MaximumBannerLength, LanguageName, 120); + internal virtual BlockStructureOptions GetOptions() + => new(MaximumBannerLength: 120, IsMetadataAsSource: WorkspaceKind == CodeAnalysis.WorkspaceKind.MetadataAsSource); private Task> GetBlockSpansAsync(Document document, int position) => GetBlockSpansWorkerAsync(document, position); @@ -34,43 +34,37 @@ private Task> GetBlockSpansAsync(Document document, in private protected async Task VerifyBlockSpansAsync(string markupCode, params RegionData[] expectedRegionData) { - using (var workspace = TestWorkspace.Create(WorkspaceKind, LanguageName, compilationOptions: null, parseOptions: null, content: markupCode)) - { - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(UpdateOptions(workspace.CurrentSolution.Options))); + using var workspace = TestWorkspace.Create(WorkspaceKind, LanguageName, compilationOptions: null, parseOptions: null, content: markupCode); - var hostDocument = workspace.Documents.Single(); - Assert.True(hostDocument.CursorPosition.HasValue, "Test must specify a position."); - var position = hostDocument.CursorPosition.Value; + var hostDocument = workspace.Documents.Single(); + Assert.True(hostDocument.CursorPosition.HasValue, "Test must specify a position."); + var position = hostDocument.CursorPosition.Value; - var expectedRegions = expectedRegionData.Select(data => CreateBlockSpan(data, hostDocument.AnnotatedSpans)).ToArray(); + var expectedRegions = expectedRegionData.Select(data => CreateBlockSpan(data, hostDocument.AnnotatedSpans)).ToArray(); - var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); - var actualRegions = await GetBlockSpansAsync(document, position); + var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); + var actualRegions = await GetBlockSpansAsync(document, position); - Assert.True(expectedRegions.Length == actualRegions.Length, $"Expected {expectedRegions.Length} regions but there were {actualRegions.Length}"); + Assert.True(expectedRegions.Length == actualRegions.Length, $"Expected {expectedRegions.Length} regions but there were {actualRegions.Length}"); - for (var i = 0; i < expectedRegions.Length; i++) - { - AssertRegion(expectedRegions[i], actualRegions[i]); - } + for (var i = 0; i < expectedRegions.Length; i++) + { + AssertRegion(expectedRegions[i], actualRegions[i]); } } protected async Task VerifyNoBlockSpansAsync(string markupCode) { - using (var workspace = TestWorkspace.Create(WorkspaceKind, LanguageName, compilationOptions: null, parseOptions: null, content: markupCode)) - { - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(UpdateOptions(workspace.Options))); + using var workspace = TestWorkspace.Create(WorkspaceKind, LanguageName, compilationOptions: null, parseOptions: null, content: markupCode); - var hostDocument = workspace.Documents.Single(); - Assert.True(hostDocument.CursorPosition.HasValue, "Test must specify a position."); - var position = hostDocument.CursorPosition.Value; + var hostDocument = workspace.Documents.Single(); + Assert.True(hostDocument.CursorPosition.HasValue, "Test must specify a position."); + var position = hostDocument.CursorPosition.Value; - var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); - var actualRegions = await GetBlockSpansAsync(document, position); + var document = workspace.CurrentSolution.GetDocument(hostDocument.Id); + var actualRegions = await GetBlockSpansAsync(document, position); - Assert.True(actualRegions.Length == 0, $"Expected no regions but found {actualRegions.Length}."); - } + Assert.True(actualRegions.Length == 0, $"Expected no regions but found {actualRegions.Length}."); } protected static RegionData Region(string textSpanName, string hintSpanName, string bannerText, bool autoCollapse, bool isDefaultCollapsed = false) diff --git a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs index 680daebf86cd0..1d535b787984c 100644 --- a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs @@ -23,7 +23,7 @@ internal sealed override async Task> GetBlockSpansWork var outliner = CreateProvider(); using var actualRegions = TemporaryArray.Empty; - var options = BlockStructureOptions.From(document.Project); + var options = GetOptions(); outliner.CollectBlockSpans(trivia, ref actualRegions.AsRef(), options, CancellationToken.None); // TODO: Determine why we get null outlining spans. diff --git a/src/EditorFeatures/VisualBasicTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.vb b/src/EditorFeatures/VisualBasicTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.vb index bcbeef95fe486..7d9f11a75654b 100644 --- a/src/EditorFeatures/VisualBasicTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Structure/MetadataAsSource/InvalidIdentifierStructureTests.vb @@ -30,7 +30,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Outlining.Metadata Friend Overrides Async Function GetBlockSpansWorkerAsync(document As Document, position As Integer) As Task(Of ImmutableArray(Of BlockSpan)) Dim outliningService = document.GetLanguageService(Of BlockStructureService)() - Dim options = BlockStructureOptions.From(document.Project) + Dim options = GetOptions() Return (Await outliningService.GetBlockStructureAsync(document, options, CancellationToken.None)).Spans End Function diff --git a/src/EditorFeatures/VisualBasicTest/Structure/OverallStructureTests.vb b/src/EditorFeatures/VisualBasicTest/Structure/OverallStructureTests.vb index 28fc10b63a6fc..27165d7f36bf9 100644 --- a/src/EditorFeatures/VisualBasicTest/Structure/OverallStructureTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Structure/OverallStructureTests.vb @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Outlining Friend Overrides Async Function GetBlockSpansWorkerAsync(document As Document, position As Integer) As Task(Of ImmutableArray(Of BlockSpan)) Dim outliningService = document.GetLanguageService(Of BlockStructureService)() - Dim options = BlockStructureOptions.From(document.Project) + Dim options = GetOptions() Return (Await outliningService.GetBlockStructureAsync(document, options, CancellationToken.None)).Spans End Function diff --git a/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesOptions.cs b/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesOptions.cs deleted file mode 100644 index 61acfc73862de..0000000000000 --- a/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Immutable; -using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; - -namespace Microsoft.CodeAnalysis.GenerateOverrides -{ - internal class GenerateOverridesOptions - { - public static readonly Option2 SelectAll = new( - nameof(GenerateOverridesOptions), nameof(SelectAll), defaultValue: true, - storageLocation: new RoamingProfileStorageLocation($"TextEditor.Specific.{nameof(GenerateOverridesOptions)}.{nameof(SelectAll)}")); - - [ExportSolutionOptionProvider, Shared] - internal class GenerateOverridesOptionsProvider : IOptionProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public GenerateOverridesOptionsProvider() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - GenerateOverridesOptions.SelectAll); - } - } -} diff --git a/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesWithDialogCodeAction.cs b/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesWithDialogCodeAction.cs index 101eec10fb902..69278befe05f7 100644 --- a/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/GenerateOverrides/GenerateOverridesWithDialogCodeAction.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PickMembers; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -18,8 +19,12 @@ namespace Microsoft.CodeAnalysis.GenerateOverrides { internal partial class GenerateOverridesCodeRefactoringProvider { - private class GenerateOverridesWithDialogCodeAction : CodeActionWithOptions + private sealed class GenerateOverridesWithDialogCodeAction : CodeActionWithOptions { + public static readonly Option2 s_globalOption = new( + "GenerateOverridesOptions", "SelectAll", defaultValue: true, + storageLocation: new RoamingProfileStorageLocation($"TextEditor.Specific.GenerateOverridesOptions.SelectAll")); + private readonly GenerateOverridesCodeRefactoringProvider _service; private readonly Document _document; private readonly INamedTypeSymbol _containingType; @@ -44,11 +49,14 @@ public GenerateOverridesWithDialogCodeAction( public override object GetOptions(CancellationToken cancellationToken) { - var service = _service._pickMembersService_forTestingPurposes ?? _document.Project.Solution.Workspace.Services.GetRequiredService(); - return service.PickMembers( + var services = _document.Project.Solution.Workspace.Services; + var pickMembersService = _service._pickMembersService_forTestingPurposes ?? services.GetRequiredService(); + var globalOptionService = services.GetService(); + + return pickMembersService.PickMembers( FeaturesResources.Pick_members_to_override, _viableMembers, - selectAll: _document.Project.Solution.Options.GetOption(GenerateOverridesOptions.SelectAll)); + selectAll: globalOptionService?.GlobalOptions.GetOption(s_globalOption) ?? s_globalOption.DefaultValue); } protected override async Task> ComputeOperationsAsync(object options, CancellationToken cancellationToken) @@ -97,7 +105,7 @@ private Task GenerateOverrideAsync( cancellationToken: cancellationToken); } - private class ChangeOptionValueOperation : CodeActionOperation + private sealed class ChangeOptionValueOperation : CodeActionOperation { private readonly bool _selectedAll; @@ -106,12 +114,8 @@ public ChangeOptionValueOperation(bool selectedAll) public override void Apply(Workspace workspace, CancellationToken cancellationToken) { - if (workspace.Options.GetOption(GenerateOverridesOptions.SelectAll) == _selectedAll) - return; - - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions( - workspace.CurrentSolution.Options.WithChangedOption( - GenerateOverridesOptions.SelectAll, _selectedAll))); + var service = workspace.Services.GetService(); + service?.GlobalOptions.SetGlobalOption(new OptionKey(s_globalOption), _selectedAll); } } } diff --git a/src/Features/Core/Portable/Structure/BlockStructureOptions.cs b/src/Features/Core/Portable/Structure/BlockStructureOptions.cs index 7cfed666438f4..e13649d8ea6d9 100644 --- a/src/Features/Core/Portable/Structure/BlockStructureOptions.cs +++ b/src/Features/Core/Portable/Structure/BlockStructureOptions.cs @@ -2,104 +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. -using System; -using System.Composition; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options.Providers; - namespace Microsoft.CodeAnalysis.Structure { - internal record struct BlockStructureOptions( - bool ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, - bool ShowBlockStructureGuidesForDeclarationLevelConstructs, - bool ShowBlockStructureGuidesForCodeLevelConstructs, - bool ShowOutliningForCommentsAndPreprocessorRegions, - bool ShowOutliningForDeclarationLevelConstructs, - bool ShowOutliningForCodeLevelConstructs, - bool CollapseRegionsWhenCollapsingToDefinitions, - int MaximumBannerLength, - bool IsMetadataAsSource) + internal readonly record struct BlockStructureOptions( + bool ShowBlockStructureGuidesForCommentsAndPreprocessorRegions = false, + bool ShowBlockStructureGuidesForDeclarationLevelConstructs = true, + bool ShowBlockStructureGuidesForCodeLevelConstructs = true, + bool ShowOutliningForCommentsAndPreprocessorRegions = true, + bool ShowOutliningForDeclarationLevelConstructs = true, + bool ShowOutliningForCodeLevelConstructs = true, + bool CollapseRegionsWhenCollapsingToDefinitions = false, + int MaximumBannerLength = 80, + bool IsMetadataAsSource = false) { - public static readonly BlockStructureOptions Default = - new(ShowBlockStructureGuidesForCommentsAndPreprocessorRegions: Metadata.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions.DefaultValue, - ShowBlockStructureGuidesForDeclarationLevelConstructs: Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs.DefaultValue, - ShowBlockStructureGuidesForCodeLevelConstructs: Metadata.ShowBlockStructureGuidesForCodeLevelConstructs.DefaultValue, - ShowOutliningForCommentsAndPreprocessorRegions: Metadata.ShowOutliningForCommentsAndPreprocessorRegions.DefaultValue, - ShowOutliningForDeclarationLevelConstructs: Metadata.ShowOutliningForDeclarationLevelConstructs.DefaultValue, - ShowOutliningForCodeLevelConstructs: Metadata.ShowOutliningForCodeLevelConstructs.DefaultValue, - CollapseRegionsWhenCollapsingToDefinitions: Metadata.CollapseRegionsWhenCollapsingToDefinitions.DefaultValue, - MaximumBannerLength: Metadata.MaximumBannerLength.DefaultValue, - IsMetadataAsSource: false); - - public static BlockStructureOptions From(Project project) - => From(project.Solution.Options, project.Language, isMetadataAsSource: project.Solution.Workspace.Kind == WorkspaceKind.MetadataAsSource); - - public static BlockStructureOptions From(OptionSet options, string language, bool isMetadataAsSource) - => new( - ShowBlockStructureGuidesForCommentsAndPreprocessorRegions: options.GetOption(Metadata.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, language), - ShowBlockStructureGuidesForDeclarationLevelConstructs: options.GetOption(Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, language), - ShowBlockStructureGuidesForCodeLevelConstructs: options.GetOption(Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, language), - ShowOutliningForCommentsAndPreprocessorRegions: options.GetOption(Metadata.ShowOutliningForCommentsAndPreprocessorRegions, language), - ShowOutliningForDeclarationLevelConstructs: options.GetOption(Metadata.ShowOutliningForDeclarationLevelConstructs, language), - ShowOutliningForCodeLevelConstructs: options.GetOption(Metadata.ShowOutliningForCodeLevelConstructs, language), - CollapseRegionsWhenCollapsingToDefinitions: options.GetOption(Metadata.CollapseRegionsWhenCollapsingToDefinitions, language), - MaximumBannerLength: options.GetOption(Metadata.MaximumBannerLength, language), - IsMetadataAsSource: isMetadataAsSource); - - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider + public BlockStructureOptions() + : this(ShowBlockStructureGuidesForCommentsAndPreprocessorRegions: false) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, - ShowBlockStructureGuidesForDeclarationLevelConstructs, - ShowBlockStructureGuidesForCodeLevelConstructs, - ShowOutliningForCommentsAndPreprocessorRegions, - ShowOutliningForDeclarationLevelConstructs, - ShowOutliningForCodeLevelConstructs, - CollapseRegionsWhenCollapsingToDefinitions, - MaximumBannerLength); - - private const string FeatureName = "BlockStructureOptions"; - - public static readonly PerLanguageOption2 ShowBlockStructureGuidesForCommentsAndPreprocessorRegions = new( - FeatureName, "ShowBlockStructureGuidesForCommentsAndPreprocessorRegions", defaultValue: false, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions")); - - public static readonly PerLanguageOption2 ShowBlockStructureGuidesForDeclarationLevelConstructs = new( - FeatureName, "ShowBlockStructureGuidesForDeclarationLevelConstructs", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowBlockStructureGuidesForDeclarationLevelConstructs")); - - public static readonly PerLanguageOption2 ShowBlockStructureGuidesForCodeLevelConstructs = new( - FeatureName, "ShowBlockStructureGuidesForCodeLevelConstructs", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowBlockStructureGuidesForCodeLevelConstructs")); - - public static readonly PerLanguageOption2 ShowOutliningForCommentsAndPreprocessorRegions = new( - FeatureName, "ShowOutliningForCommentsAndPreprocessorRegions", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowOutliningForCommentsAndPreprocessorRegions")); - - public static readonly PerLanguageOption2 ShowOutliningForDeclarationLevelConstructs = new( - FeatureName, "ShowOutliningForDeclarationLevelConstructs", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowOutliningForDeclarationLevelConstructs")); - - public static readonly PerLanguageOption2 ShowOutliningForCodeLevelConstructs = new( - FeatureName, "ShowOutliningForCodeLevelConstructs", defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowOutliningForCodeLevelConstructs")); - - public static readonly PerLanguageOption2 CollapseRegionsWhenCollapsingToDefinitions = new( - FeatureName, "CollapseRegionsWhenCollapsingToDefinitions", defaultValue: false, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.CollapseRegionsWhenCollapsingToDefinitions")); - - public static readonly PerLanguageOption2 MaximumBannerLength = new( - FeatureName, "MaximumBannerLength", defaultValue: 80, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.MaximumBannerLength")); } + + public static readonly BlockStructureOptions Default = new(); } } diff --git a/src/Features/LanguageServer/Protocol/Features/Options/BlockStructureOptionsStorage.cs b/src/Features/LanguageServer/Protocol/Features/Options/BlockStructureOptionsStorage.cs new file mode 100644 index 0000000000000..ab630ee962014 --- /dev/null +++ b/src/Features/LanguageServer/Protocol/Features/Options/BlockStructureOptionsStorage.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.Structure +{ + internal static class BlockStructureOptionsStorage + { + public static BlockStructureOptions GetBlockStructureOptions(this IGlobalOptionService globalOptions, Project project) + => GetBlockStructureOptions(globalOptions, project.Language, isMetadataAsSource: project.Solution.Workspace.Kind == WorkspaceKind.MetadataAsSource); + + public static BlockStructureOptions GetBlockStructureOptions(this IGlobalOptionService globalOptions, string language, bool isMetadataAsSource) + => new( + ShowBlockStructureGuidesForCommentsAndPreprocessorRegions: globalOptions.GetOption(ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, language), + ShowBlockStructureGuidesForDeclarationLevelConstructs: globalOptions.GetOption(ShowBlockStructureGuidesForDeclarationLevelConstructs, language), + ShowBlockStructureGuidesForCodeLevelConstructs: globalOptions.GetOption(ShowBlockStructureGuidesForCodeLevelConstructs, language), + ShowOutliningForCommentsAndPreprocessorRegions: globalOptions.GetOption(ShowOutliningForCommentsAndPreprocessorRegions, language), + ShowOutliningForDeclarationLevelConstructs: globalOptions.GetOption(ShowOutliningForDeclarationLevelConstructs, language), + ShowOutliningForCodeLevelConstructs: globalOptions.GetOption(ShowOutliningForCodeLevelConstructs, language), + CollapseRegionsWhenCollapsingToDefinitions: globalOptions.GetOption(CollapseRegionsWhenCollapsingToDefinitions, language), + MaximumBannerLength: globalOptions.GetOption(MaximumBannerLength, language), + IsMetadataAsSource: isMetadataAsSource); + + private const string FeatureName = "BlockStructureOptions"; + + public static readonly PerLanguageOption2 ShowBlockStructureGuidesForCommentsAndPreprocessorRegions = new( + FeatureName, "ShowBlockStructureGuidesForCommentsAndPreprocessorRegions", BlockStructureOptions.Default.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions")); + + public static readonly PerLanguageOption2 ShowBlockStructureGuidesForDeclarationLevelConstructs = new( + FeatureName, "ShowBlockStructureGuidesForDeclarationLevelConstructs", BlockStructureOptions.Default.ShowBlockStructureGuidesForDeclarationLevelConstructs, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowBlockStructureGuidesForDeclarationLevelConstructs")); + + public static readonly PerLanguageOption2 ShowBlockStructureGuidesForCodeLevelConstructs = new( + FeatureName, "ShowBlockStructureGuidesForCodeLevelConstructs", BlockStructureOptions.Default.ShowBlockStructureGuidesForCodeLevelConstructs, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowBlockStructureGuidesForCodeLevelConstructs")); + + public static readonly PerLanguageOption2 ShowOutliningForCommentsAndPreprocessorRegions = new( + FeatureName, "ShowOutliningForCommentsAndPreprocessorRegions", BlockStructureOptions.Default.ShowOutliningForCommentsAndPreprocessorRegions, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowOutliningForCommentsAndPreprocessorRegions")); + + public static readonly PerLanguageOption2 ShowOutliningForDeclarationLevelConstructs = new( + FeatureName, "ShowOutliningForDeclarationLevelConstructs", BlockStructureOptions.Default.ShowOutliningForDeclarationLevelConstructs, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowOutliningForDeclarationLevelConstructs")); + + public static readonly PerLanguageOption2 ShowOutliningForCodeLevelConstructs = new( + FeatureName, "ShowOutliningForCodeLevelConstructs", BlockStructureOptions.Default.ShowOutliningForCodeLevelConstructs, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ShowOutliningForCodeLevelConstructs")); + + public static readonly PerLanguageOption2 CollapseRegionsWhenCollapsingToDefinitions = new( + FeatureName, "CollapseRegionsWhenCollapsingToDefinitions", BlockStructureOptions.Default.CollapseRegionsWhenCollapsingToDefinitions, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.CollapseRegionsWhenCollapsingToDefinitions")); + + public static readonly PerLanguageOption2 MaximumBannerLength = new( + FeatureName, "MaximumBannerLength", BlockStructureOptions.Default.MaximumBannerLength, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.MaximumBannerLength")); + } +} diff --git a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs index 8a128c73832cf..60256349ba9c0 100644 --- a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Text; @@ -19,13 +20,16 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler [Method(Methods.TextDocumentFoldingRangeName)] internal sealed class FoldingRangesHandler : AbstractStatelessRequestHandler { + private readonly IGlobalOptionService _globalOptions; + public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FoldingRangesHandler() + public FoldingRangesHandler(IGlobalOptionService globalOptions) { + _globalOptions = globalOptions; } public override TextDocumentIdentifier? GetTextDocumentIdentifier(FoldingRangeParams request) => request.TextDocument; @@ -42,7 +46,7 @@ public FoldingRangesHandler() return Array.Empty(); } - var options = BlockStructureOptions.From(document.Project); + var options = _globalOptions.GetBlockStructureOptions(document.Project); var blockStructure = await blockStructureService.GetBlockStructureAsync(document, options, cancellationToken).ConfigureAwait(false); if (blockStructure == null) { diff --git a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj index 82d0f6bd23084..9f91a96c8fdc0 100644 --- a/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj +++ b/src/Features/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj @@ -34,13 +34,15 @@ + + + - diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index a7b51832ac605..fbc2016f006c2 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -77,16 +77,16 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(Split_string_literals_on_enter, SplitStringLiteralOptions.Enabled, LanguageNames.CSharp); BindToOption(EnterOutliningMode, FeatureOnOffOptions.Outlining, LanguageNames.CSharp); - BindToOption(Show_outlining_for_declaration_level_constructs, BlockStructureOptions.Metadata.ShowOutliningForDeclarationLevelConstructs, LanguageNames.CSharp); - BindToOption(Show_outlining_for_code_level_constructs, BlockStructureOptions.Metadata.ShowOutliningForCodeLevelConstructs, LanguageNames.CSharp); - BindToOption(Show_outlining_for_comments_and_preprocessor_regions, BlockStructureOptions.Metadata.ShowOutliningForCommentsAndPreprocessorRegions, LanguageNames.CSharp); - BindToOption(Collapse_regions_when_collapsing_to_definitions, BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp); + BindToOption(Show_outlining_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowOutliningForDeclarationLevelConstructs, LanguageNames.CSharp); + BindToOption(Show_outlining_for_code_level_constructs, BlockStructureOptionsStorage.ShowOutliningForCodeLevelConstructs, LanguageNames.CSharp); + BindToOption(Show_outlining_for_comments_and_preprocessor_regions, BlockStructureOptionsStorage.ShowOutliningForCommentsAndPreprocessorRegions, LanguageNames.CSharp); + BindToOption(Collapse_regions_when_collapsing_to_definitions, BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.CSharp); BindToOption(Fade_out_unused_usings, FadingOptions.Metadata.FadeOutUnusedImports, LanguageNames.CSharp); BindToOption(Fade_out_unreachable_code, FadingOptions.Metadata.FadeOutUnreachableCode, LanguageNames.CSharp); - BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp); - BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp); + BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp); + BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp); BindToOption(GenerateXmlDocCommentsForTripleSlash, DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, LanguageNames.CSharp); BindToOption(InsertSlashSlashAtTheStartOfNewLinesWhenWritingSingleLineComments, SplitStringLiteralOptions.Enabled, LanguageNames.CSharp); diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index 5c080a2bdb7cf..59c1f2df469c8 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -87,17 +87,17 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options ' Outlining BindToOption(EnableOutlining, FeatureOnOffOptions.Outlining, LanguageNames.VisualBasic) BindToOption(DisplayLineSeparators, FeatureOnOffOptions.LineSeparator, LanguageNames.VisualBasic) - BindToOption(Show_outlining_for_declaration_level_constructs, BlockStructureOptions.Metadata.ShowOutliningForDeclarationLevelConstructs, LanguageNames.VisualBasic) - BindToOption(Show_outlining_for_code_level_constructs, BlockStructureOptions.Metadata.ShowOutliningForCodeLevelConstructs, LanguageNames.VisualBasic) - BindToOption(Show_outlining_for_comments_and_preprocessor_regions, BlockStructureOptions.Metadata.ShowOutliningForCommentsAndPreprocessorRegions, LanguageNames.VisualBasic) - BindToOption(Collapse_regions_when_collapsing_to_definitions, BlockStructureOptions.Metadata.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.VisualBasic) + BindToOption(Show_outlining_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowOutliningForDeclarationLevelConstructs, LanguageNames.VisualBasic) + BindToOption(Show_outlining_for_code_level_constructs, BlockStructureOptionsStorage.ShowOutliningForCodeLevelConstructs, LanguageNames.VisualBasic) + BindToOption(Show_outlining_for_comments_and_preprocessor_regions, BlockStructureOptionsStorage.ShowOutliningForCommentsAndPreprocessorRegions, LanguageNames.VisualBasic) + BindToOption(Collapse_regions_when_collapsing_to_definitions, BlockStructureOptionsStorage.CollapseRegionsWhenCollapsingToDefinitions, LanguageNames.VisualBasic) ' Fading BindToOption(Fade_out_unused_imports, FadingOptions.Metadata.FadeOutUnusedImports, LanguageNames.VisualBasic) ' Block structure guides - BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.VisualBasic) - BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.VisualBasic) + BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.VisualBasic) + BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.VisualBasic) ' Comments BindToOption(GenerateXmlDocCommentsForTripleApostrophes, DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, LanguageNames.VisualBasic) diff --git a/src/Workspaces/Core/Portable/Options/ILegacyGlobalOptionsWorkspaceService.cs b/src/Workspaces/Core/Portable/Options/ILegacyGlobalOptionsWorkspaceService.cs new file mode 100644 index 0000000000000..28ef99b1777dc --- /dev/null +++ b/src/Workspaces/Core/Portable/Options/ILegacyGlobalOptionsWorkspaceService.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.Options +{ + /// + /// Enables legacy APIs to access global options from workspace. + /// Not available OOP. Only use in client code and when IGlobalOptionService can't be MEF imported. + /// + internal interface ILegacyGlobalOptionsWorkspaceService : IWorkspaceService + { + public IGlobalOptionService GlobalOptions { get; } + } +} From ef639bd072b88029727d1c06343827bd13d19407 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 18 Feb 2022 20:10:14 -0800 Subject: [PATCH 114/187] Fix issue with curlies being innapropriately unescaped in raw string literals. (#59607) * Do not unescape the text in a raw string literal * Set up the right constant value only once * Update src/Compilers/CSharp/Test/Syntax/Parsing/RawInterpolatedStringLiteralCompilingTests.cs * Update comment * Fix * Escape before creating string.Format call * Unescape * Unify * Simplify * Update src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs Co-authored-by: Julien Couvreur * Add tsts and inline helpers * Add concat tests * PR feedback Co-authored-by: Julien Couvreur --- .../Binder/Binder_InterpolatedString.cs | 48 +++- .../LocalRewriter_StringInterpolation.cs | 45 +++- .../Portable/Symbols/ConstantValueUtils.cs | 17 -- ...ationTests_IInterpolatedStringOperation.cs | 85 ++++++- .../Semantic/Semantics/InterpolationTests.cs | 220 +++++++++++++++++- .../RawInterpolationTests_Handler.cs | 114 +++++++++ ...InterpolatedStringLiteralCompilingTests.cs | 75 +++++- 7 files changed, 560 insertions(+), 44 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs index b279cb12e7dc6..1272ea8d2d096 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs @@ -49,6 +49,7 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta else { var isNonVerbatimInterpolatedString = node.StringStartToken.Kind() != SyntaxKind.InterpolatedVerbatimStringStartToken; + var isRawInterpolatedString = node.StringStartToken.Kind() is SyntaxKind.InterpolatedSingleLineRawStringStartToken or SyntaxKind.InterpolatedMultiLineRawStringStartToken; var newLinesInInterpolationsAllowed = this.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNewLinesInInterpolations); var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, node); @@ -153,13 +154,23 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta case SyntaxKind.InterpolatedStringText: { var text = ((InterpolatedStringTextSyntax)content).TextToken.ValueText; - builder.Add(new BoundLiteral(content, ConstantValue.Create(text, SpecialType.System_String), stringType)); + // Raw string literals have no escapes. So there is no need to manipulate their value texts. + // We have to unescape normal interpolated strings as the parser stores their text without + // interpreting {{ and }} sequences (as '{' and '}') respectively. Changing that at the syntax + // level might potentially be a breaking change, so we do the conversion here when creating the + // bound nodes. + if (!isRawInterpolatedString) + { + text = unescapeInterpolatedStringLiteral(text); + } + + var constantValue = ConstantValue.Create(text, SpecialType.System_String); + builder.Add(new BoundLiteral(content, constantValue, stringType)); if (isResultConstant) { - var constantVal = ConstantValue.Create(ConstantValueUtils.UnescapeInterpolatedStringLiteral(text), SpecialType.System_String); - resultConstant = (resultConstant is null) - ? constantVal - : FoldStringConcatenation(BinaryOperatorKind.StringConcatenation, resultConstant, constantVal); + resultConstant = resultConstant is null + ? constantValue + : FoldStringConcatenation(BinaryOperatorKind.StringConcatenation, resultConstant, constantValue); } continue; } @@ -176,6 +187,31 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta Debug.Assert(isResultConstant == (resultConstant != null)); return new BoundUnconvertedInterpolatedString(node, builder.ToImmutableAndFree(), resultConstant, stringType); + + static string unescapeInterpolatedStringLiteral(string value) + { + var builder = PooledStringBuilder.GetInstance(); + var stringBuilder = builder.Builder; + for (int i = 0, formatLength = value.Length; i < formatLength; i++) + { + var c = value[i]; + stringBuilder.Append(c); + if (c is '{' or '}' && + i + 1 < formatLength && + value[i + 1] == c) + { + i++; + } + } + + // Avoid unnecessary allocation in the common case of no escaped curlies. + var result = builder.Length == value.Length + ? value + : builder.Builder.ToString(); + builder.Free(); + + return result; + } } private BoundInterpolatedString BindUnconvertedInterpolatedStringToString(BoundUnconvertedInterpolatedString unconvertedInterpolatedString, BindingDiagnosticBag diagnostics) @@ -748,7 +784,7 @@ private ImmutableArray BindInterpolatedStringParts(BoundUnconve { var boundLiteral = (BoundLiteral)part; Debug.Assert(boundLiteral.ConstantValue != null && boundLiteral.ConstantValue.IsString); - var literalText = ConstantValueUtils.UnescapeInterpolatedStringLiteral(boundLiteral.ConstantValue.StringValue); + var literalText = boundLiteral.ConstantValue.StringValue; methodName = BoundInterpolatedString.AppendLiteralMethod; argumentsBuilder.Add(boundLiteral.Update(ConstantValue.Create(literalText), boundLiteral.Type)); isLiteral = true; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs index ffb5979b910db..f6041ec5193fa 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs @@ -216,14 +216,7 @@ private void MakeInterpolatedStringFormat(BoundInterpolatedString node, out Boun for (int i = 0; i <= n; i++) { var part = node.Parts[i]; - var fillin = part as BoundStringInsert; - if (fillin == null) - { - Debug.Assert(part is BoundLiteral && part.ConstantValue != null); - // this is one of the literal parts - stringBuilder.Append(part.ConstantValue.StringValue); - } - else + if (part is BoundStringInsert fillin) { // this is one of the expression holes stringBuilder.Append('{').Append(nextFormatPosition++); @@ -246,9 +239,39 @@ private void MakeInterpolatedStringFormat(BoundInterpolatedString node, out Boun expressions.Add(value); // NOTE: must still be lowered } + else + { + Debug.Assert(part is BoundLiteral && part.ConstantValue?.StringValue != null); + // this is one of the literal parts. If it contains a { or } then we need to escape those so that + // they're treated the same way in string.Format. + stringBuilder.Append(escapeInterpolatedStringLiteral(part.ConstantValue.StringValue)); + } } format = _factory.StringLiteral(formatString.ToStringAndFree()); + return; + + static string escapeInterpolatedStringLiteral(string value) + { + var builder = PooledStringBuilder.GetInstance(); + var stringBuilder = builder.Builder; + foreach (var c in value) + { + stringBuilder.Append(c); + if (c is '{' or '}') + { + stringBuilder.Append(c); + } + } + + // Avoid unnecessary allocation in the common case of nothing to escape. + var result = builder.Length == value.Length + ? value + : builder.Builder.ToString(); + builder.Free(); + + return result; + } } public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) @@ -287,8 +310,8 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) else { // this is one of the literal parts - Debug.Assert(part is BoundLiteral && part.ConstantValue is { StringValue: { } }); - part = _factory.StringLiteral(ConstantValueUtils.UnescapeInterpolatedStringLiteral(part.ConstantValue.StringValue)); + Debug.Assert(part is BoundLiteral && part.ConstantValue?.StringValue is not null); + part = _factory.StringLiteral(part.ConstantValue.StringValue); } result = result == null ? @@ -299,7 +322,7 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) // We need to ensure that the result of the interpolated string is not null. If the single part has a non-null constant value // or is itself an interpolated string (which by proxy cannot be null), then there's nothing else that needs to be done. Otherwise, // we need to test for null and ensure "" if it is. - if (length == 1 && result is not ({ Kind: BoundKind.InterpolatedString } or { ConstantValue: { IsString: true } })) + if (length == 1 && result is not ({ Kind: BoundKind.InterpolatedString } or { ConstantValue.IsString: true })) { Debug.Assert(result is not null); Debug.Assert(result.Type is not null); diff --git a/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs b/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs index 9e5a0b229be8e..a1a0a5fc65166 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConstantValueUtils.cs @@ -73,23 +73,6 @@ private static BoundFieldEqualsValue BindFieldOrEnumInitializer( return result; } - internal static string UnescapeInterpolatedStringLiteral(string s) - { - var builder = PooledStringBuilder.GetInstance(); - var stringBuilder = builder.Builder; - int formatLength = s.Length; - for (int i = 0; i < formatLength; i++) - { - char c = s[i]; - stringBuilder.Append(c); - if ((c == '{' || c == '}') && (i + 1) < formatLength && s[i + 1] == c) - { - i++; - } - } - return builder.ToStringAndFree(); - } - internal static ConstantValue GetAndValidateConstantValue( BoundExpression boundValue, Symbol thisSymbol, diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs index 99901da29b550..31e30c68f7d00 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs @@ -4724,7 +4724,7 @@ public void InterpolationEscapeConstantValue_WithDefaultHandler() } [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] - public void InterpolationEscapeConstantValue_WithoutDefaultHandler() + public void InterpolationEscapeConstantValue_WithoutDefaultHandler1() { var code = @" int i = 1; @@ -4739,7 +4739,7 @@ public void InterpolationEscapeConstantValue_WithoutDefaultHandler() Parts(3): IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: '{{ ') Text: - ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{{ "", IsImplicit) (Syntax: '{{ ') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{ "", IsImplicit) (Syntax: '{{ ') IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i}') Expression: ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') @@ -4749,10 +4749,87 @@ public void InterpolationEscapeConstantValue_WithoutDefaultHandler() null IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: ' }}') Text: - ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "" }}"", IsImplicit) (Syntax: ' }}') + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "" }"", IsImplicit) (Syntax: ' }}') "; - VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void RawInterpolationEscapeConstantValue_WithoutDefaultHandler1() + { + var code = @" +int i = 1; +System.Console.WriteLine(/**/$$""""""{{i}}""""""/**/);"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$$""""""{{i}}""""""') + Parts(1): + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{{i}}') + Expression: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + null + FormatString: + null"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void RawInterpolationEscapeConstantValue_WithoutDefaultHandler2() + { + var code = @" +System.Console.WriteLine(/**/$$$""""""{{i}}""""""/**/);"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: ""{{i}}"") (Syntax: '$$$""""""{{i}}""""""') + Parts(1): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: '{{i}}') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{{i}}"", IsImplicit) (Syntax: '{{i}}')"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void RawInterpolationEscapeConstantValue_WithoutDefaultHandler3() + { + var code = @" +System.Console.WriteLine(/**/$$$$""""""{{i}}""""""/**/);"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: ""{{i}}"") (Syntax: '$$$$""""""{{i}}""""""') + Parts(1): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: '{{i}}') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{{i}}"", IsImplicit) (Syntax: '{{i}}')"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void RawInterpolationEscapeConstantValue_WithoutDefaultHandler4() + { + var code = @" +System.Console.WriteLine(/**/$$$$""""""{{{i}}}""""""/**/);"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, Constant: ""{{{i}}}"") (Syntax: '$$$$""""""{{{i}}}""""""') + Parts(1): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: '{{{i}}}') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{{{i}}}"", IsImplicit) (Syntax: '{{{i}}}')"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index e8f04ab535ec6..ea0a93ad55fbc 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -4,14 +4,14 @@ #nullable disable +using System.Collections.Immutable; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; -using System.Collections.Immutable; -using System.Linq; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics @@ -1132,8 +1132,6 @@ .maxstack 2 }"); } - - [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] #if NETCOREAPP [InlineData(TargetFramework.Net60)] @@ -1144,7 +1142,7 @@ .maxstack 2 [InlineData(TargetFramework.Mscorlib461)] [InlineData(TargetFramework.Mscorlib40)] [Theory] - public void InterpolatedStringWithCurlyBracesFollowerAfterFormatSpecifierTest(TargetFramework framework) + public void InterpolatedStringWithCurlyBracesAndFormatSpecifier(TargetFramework framework) { var text = @"using System; @@ -1242,6 +1240,218 @@ .locals init (string V_0, //str } } + [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] +#if NETCOREAPP + [InlineData(TargetFramework.Net60)] + [InlineData(TargetFramework.Net50)] +#endif + [InlineData(TargetFramework.NetFramework)] + [InlineData(TargetFramework.NetStandard20)] + [InlineData(TargetFramework.Mscorlib461)] + [InlineData(TargetFramework.Mscorlib40)] + [Theory] + public void RawInterpolatedStringWithCurlyBracesAndFormatSpecifier(TargetFramework framework) + { + var text = +@"using System; + +class App{ + public static void Main(){ + var str = $$""""""Before {{{12:X}}} After""""""; + Console.WriteLine(str); + } +}"; + var parseOptions = TestOptions.RegularNext; + var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); + + //string.Format was fixed in dotnet core 3 + var expectedOutput = +#if NETCOREAPP3_0_OR_GREATER + "Before {C} After" +#else + "Before {X} After" +#endif + ; + + + var comp = CreateCompilation(text, targetFramework: framework, + parseOptions: parseOptions, options: compOptions); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + + switch (framework) + { + case TargetFramework.Net60: + checkNet60IL(verifier); + break; + default: + checkNet50IL(verifier); + break; + } + + static void checkNet50IL(CompilationVerifier verifier) + { + verifier.VerifyIL("App.Main", @"{ + // Code size 27 (0x1b) + .maxstack 2 + .locals init (string V_0) //str + IL_0000: nop + IL_0001: ldstr ""Before {{{0:X}}} After"" + IL_0006: ldc.i4.s 12 + IL_0008: box ""int"" + IL_000d: call ""string string.Format(string, object)"" + IL_0012: stloc.0 + IL_0013: ldloc.0 + IL_0014: call ""void System.Console.WriteLine(string)"" + IL_0019: nop + IL_001a: ret +}"); + } + + static void checkNet60IL(CompilationVerifier verifier) + { + verifier.VerifyIL("App.Main", @"{ + // Code size 68 (0x44) + .maxstack 3 + .locals init (string V_0, //str + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: nop + IL_0001: ldloca.s V_1 + IL_0003: ldc.i4.s 15 + IL_0005: ldc.i4.1 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldstr ""Before {"" + IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0017: nop + IL_0018: ldloca.s V_1 + IL_001a: ldc.i4.s 12 + IL_001c: ldstr ""X"" + IL_0021: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)"" + IL_0026: nop + IL_0027: ldloca.s V_1 + IL_0029: ldstr ""} After"" + IL_002e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0033: nop + IL_0034: ldloca.s V_1 + IL_0036: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_003b: stloc.0 + IL_003c: ldloc.0 + IL_003d: call ""void System.Console.WriteLine(string)"" + IL_0042: nop + IL_0043: ret +}"); + } + } + + [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] +#if NETCOREAPP + [InlineData(TargetFramework.Net60)] + [InlineData(TargetFramework.Net50)] +#endif + [InlineData(TargetFramework.NetFramework)] + [InlineData(TargetFramework.NetStandard20)] + [InlineData(TargetFramework.Mscorlib461)] + [InlineData(TargetFramework.Mscorlib40)] + [Theory] + public void InterpolatedStringWithCurlyBracesAndAllStringValues(TargetFramework framework) + { + var text = +@"using System; + +class App{ + public static void Main(){ + string a = ""a""; + var str = $""Before {{{a}}} After""; + Console.WriteLine(str); + } +}"; + var parseOptions = new CSharpParseOptions( + languageVersion: LanguageVersion.CSharp10, + documentationMode: DocumentationMode.Parse, + kind: SourceCodeKind.Regular + ); + var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); + + var expectedOutput = "Before {a} After"; + + var comp = CreateCompilation(text, targetFramework: framework, + parseOptions: parseOptions, options: compOptions); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + + verifier.VerifyIL("App.Main", @"{ + // Code size 32 (0x20) + .maxstack 3 + .locals init (string V_0, //a + string V_1) //str + IL_0000: nop + IL_0001: ldstr ""a"" + IL_0006: stloc.0 + IL_0007: ldstr ""Before {"" + IL_000c: ldloc.0 + IL_000d: ldstr ""} After"" + IL_0012: call ""string string.Concat(string, string, string)"" + IL_0017: stloc.1 + IL_0018: ldloc.1 + IL_0019: call ""void System.Console.WriteLine(string)"" + IL_001e: nop + IL_001f: ret +}"); + } + + [WorkItem(57750, "https://github.com/dotnet/roslyn/issues/57750")] +#if NETCOREAPP + [InlineData(TargetFramework.Net60)] + [InlineData(TargetFramework.Net50)] +#endif + [InlineData(TargetFramework.NetFramework)] + [InlineData(TargetFramework.NetStandard20)] + [InlineData(TargetFramework.Mscorlib461)] + [InlineData(TargetFramework.Mscorlib40)] + [Theory] + public void RawInterpolatedStringWithCurlyBracesAndAllStringValues(TargetFramework framework) + { + var text = +@"using System; + +class App{ + public static void Main(){ + string a = ""a""; + var str = $$""""""Before {{{a}}} After""""""; + Console.WriteLine(str); + } +}"; + var parseOptions = TestOptions.RegularNext; + var compOptions = new CSharpCompilationOptions(OutputKind.ConsoleApplication); + + var expectedOutput = "Before {a} After"; + + var comp = CreateCompilation(text, targetFramework: framework, + parseOptions: parseOptions, options: compOptions); + comp.VerifyDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: expectedOutput); + + verifier.VerifyIL("App.Main", @"{ + // Code size 32 (0x20) + .maxstack 3 + .locals init (string V_0, //a + string V_1) //str + IL_0000: nop + IL_0001: ldstr ""a"" + IL_0006: stloc.0 + IL_0007: ldstr ""Before {"" + IL_000c: ldloc.0 + IL_000d: ldstr ""} After"" + IL_0012: call ""string string.Concat(string, string, string)"" + IL_0017: stloc.1 + IL_0018: ldloc.1 + IL_0019: call ""void System.Console.WriteLine(string)"" + IL_001e: nop + IL_001f: ret +}"); + } + [WorkItem(1097386, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097386")] [Fact] public void Syntax01() diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs index 5d738b9255dc7..0ff0b6b5959d0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RawInterpolationTests_Handler.cs @@ -11386,6 +11386,120 @@ .locals init (int V_0, //i "); } + [Theory, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + [InlineData(@"$$$""""""{{1 + 2}}""""""")] + public void BracesNotEscaped1(string expression) + { + var code = @" +int i = 1; +CustomHandler c = " + expression + @"; +System.Console.WriteLine(c.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +literal:{{1 + 2}}"); + + verifier.VerifyIL("", @" +{ + // Code size 43 (0x2b) + .maxstack 3 + .locals init (CustomHandler V_0, //c + CustomHandler V_1) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.s 9 + IL_0004: ldc.i4.0 + IL_0005: call ""CustomHandler..ctor(int, int)"" + IL_000a: ldloca.s V_1 + IL_000c: ldstr ""{{1 + 2}}"" + IL_0011: call ""void CustomHandler.AppendLiteral(string)"" + IL_0016: ldloc.1 + IL_0017: stloc.0 + IL_0018: ldloca.s V_0 + IL_001a: constrained. ""CustomHandler"" + IL_0020: callvirt ""string object.ToString()"" + IL_0025: call ""void System.Console.WriteLine(string)"" + IL_002a: ret +} +"); + } + + [Theory, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + [InlineData(@"$$$$""""""{{1 + 2}}""""""")] + public void BracesNotEscaped2(string expression) + { + var code = @" +int i = 1; +CustomHandler c = " + expression + @"; +System.Console.WriteLine(c.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +literal:{{1 + 2}}"); + + verifier.VerifyIL("", @" +{ + // Code size 43 (0x2b) + .maxstack 3 + .locals init (CustomHandler V_0, //c + CustomHandler V_1) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.s 9 + IL_0004: ldc.i4.0 + IL_0005: call ""CustomHandler..ctor(int, int)"" + IL_000a: ldloca.s V_1 + IL_000c: ldstr ""{{1 + 2}}"" + IL_0011: call ""void CustomHandler.AppendLiteral(string)"" + IL_0016: ldloc.1 + IL_0017: stloc.0 + IL_0018: ldloca.s V_0 + IL_001a: constrained. ""CustomHandler"" + IL_0020: callvirt ""string object.ToString()"" + IL_0025: call ""void System.Console.WriteLine(string)"" + IL_002a: ret +} +"); + } + + [Theory, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + [InlineData(@"$$$$""""""{{{1 + 2}}}""""""")] + public void BracesNotEscaped3(string expression) + { + var code = @" +int i = 1; +CustomHandler c = " + expression + @"; +System.Console.WriteLine(c.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +literal:{{{1 + 2}}}"); + + verifier.VerifyIL("", @" +{ + // Code size 43 (0x2b) + .maxstack 3 + .locals init (CustomHandler V_0, //c + CustomHandler V_1) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.s 11 + IL_0004: ldc.i4.0 + IL_0005: call ""CustomHandler..ctor(int, int)"" + IL_000a: ldloca.s V_1 + IL_000c: ldstr ""{{{1 + 2}}}"" + IL_0011: call ""void CustomHandler.AppendLiteral(string)"" + IL_0016: ldloc.1 + IL_0017: stloc.0 + IL_0018: ldloca.s V_0 + IL_001a: constrained. ""CustomHandler"" + IL_0020: callvirt ""string object.ToString()"" + IL_0025: call ""void System.Console.WriteLine(string)"" + IL_002a: ret +} +"); + } + [Fact] public void InterpolatedStringsAddedUnderObjectAddition() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RawInterpolatedStringLiteralCompilingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RawInterpolatedStringLiteralCompilingTests.cs index 38c8f54ab2149..239d113222d0c 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RawInterpolatedStringLiteralCompilingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RawInterpolatedStringLiteralCompilingTests.cs @@ -4,6 +4,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Parsing; @@ -1850,7 +1851,7 @@ public void TestWhitespaceMismatch4() Diagnostic(ErrorCode.ERR_LineContainsDifferentWhitespace, " ").WithArguments(@"\t", @"\u0020").WithLocation(4, 1)); } - [Fact] + [Fact, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] public void TestWhitespaceMismatch5() { RenderAndVerify( @@ -1858,4 +1859,76 @@ public void TestWhitespaceMismatch5() // (4,1): error CS9003: Line contains different whitespace than the closing line of the raw string literal: '\f' versus '\v' Diagnostic(ErrorCode.ERR_LineContainsDifferentWhitespace, " ").WithArguments(@"\f", @"\v").WithLocation(4, 1)); } + + [Fact, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + public void TestThreeDollarTwoCurly_SingleLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$""""""{{1 + 2}}"""""");", expectedOutput: "{{1 + 2}}"); + } + + [Fact] + public void TestThreeDollarTwoCurly_MultiLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$"""""" + {{1 + 2}} + """""");", expectedOutput: "{{1 + 2}}"); + } + + [Fact, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + public void TestFourDollarTwoCurly_SingleLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$$""""""{{1 + 2}}"""""");", expectedOutput: "{{1 + 2}}"); + } + + [Fact] + public void TestFourDollarTwoCurly_MultiLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$"""""" + {{1 + 2}} + """""");", expectedOutput: "{{1 + 2}}"); + } + + [Fact, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + public void TestThreeDollarThreeCurly_SingleLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$""""""{{{1 + 2}}}"""""");", expectedOutput: "3"); + } + + [Fact] + public void TestThreeDollarThreeCurly_MultiLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$"""""" + {{{1 + 2}}} + """""");", expectedOutput: "3"); + } + + [Fact, WorkItem(59603, "https://github.com/dotnet/roslyn/issues/59603")] + public void TestFourDollarThreeCurly_SingleLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$$""""""{{{1 + 2}}}"""""");", expectedOutput: "{{{1 + 2}}}"); + } + + [Fact] + public void TestFourDollarThreeCurly_MultiLine() + { + RenderAndVerify(@" +System.Console.Write( + $$$$"""""" + {{{1 + 2}}} + """""");", expectedOutput: "{{{1 + 2}}}"); + } } From 1e091e693d892a331222922b6ad5d26f40808f48 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Sat, 19 Feb 2022 14:51:30 +0000 Subject: [PATCH 115/187] Update dependencies from https://github.com/dotnet/arcade build 20220217.2 (#59662) [main] Update dependencies from dotnet/arcade --- eng/Version.Details.xml | 8 ++-- eng/common/retain-build.ps1 | 47 +++++++++++++++++++++ eng/common/templates/jobs/jobs.yml | 9 ---- eng/common/templates/steps/retain-build.yml | 28 ++++++++++++ global.json | 4 +- 5 files changed, 81 insertions(+), 15 deletions(-) create mode 100644 eng/common/retain-build.ps1 create mode 100644 eng/common/templates/steps/retain-build.yml diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 08e6a33b2ac04..395135b239a34 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,18 +13,18 @@ - + https://github.com/dotnet/arcade - 6d977266bcc193caedb60a27ae44d14d9a11a040 + 49750c02e63d0ad3a77d035bba7498a0b1acd218 https://github.com/dotnet/roslyn 592501cbb9c9394072a245c15b3458ff88155d85 - + https://github.com/dotnet/arcade - 6d977266bcc193caedb60a27ae44d14d9a11a040 + 49750c02e63d0ad3a77d035bba7498a0b1acd218 diff --git a/eng/common/retain-build.ps1 b/eng/common/retain-build.ps1 new file mode 100644 index 0000000000000..e08fc227b2103 --- /dev/null +++ b/eng/common/retain-build.ps1 @@ -0,0 +1,47 @@ + +Param( +[Parameter(Mandatory=$true)][int] $buildId, +[Parameter(Mandatory=$true)][string] $azdoOrgUri, +[Parameter(Mandatory=$true)][string] $azdoProject, +[Parameter(Mandatory=$true)][string] $token +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +. $PSScriptRoot\tools.ps1 + + +function Get-AzDOHeaders( + [string] $token) +{ + $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":${token}")) + $headers = @{"Authorization"="Basic $base64AuthInfo"} + return $headers +} + +function Update-BuildRetention( + [string] $azdoOrgUri, + [string] $azdoProject, + [int] $buildId, + [string] $token) +{ + $headers = Get-AzDOHeaders -token $token + $requestBody = "{ + `"keepForever`": `"true`" + }" + + $requestUri = "${azdoOrgUri}/${azdoProject}/_apis/build/builds/${buildId}?api-version=6.0" + write-Host "Attempting to retain build using the following URI: ${requestUri} ..." + + try { + Invoke-RestMethod -Uri $requestUri -Method Patch -Body $requestBody -Header $headers -contentType "application/json" + Write-Host "Updated retention settings for build ${buildId}." + } + catch { + Write-PipelineTelemetryError -Category "Build" -Message "Failed to update retention settings for build: $_.Exception.Response.StatusDescription" + ExitWithExitCode 1 + } +} + +Update-BuildRetention -azdoOrgUri $azdoOrgUri -azdoProject $azdoProject -buildId $buildId -token $token +ExitWithExitCode 0 diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml index 6976330862c20..554e71cfc436d 100644 --- a/eng/common/templates/jobs/jobs.yml +++ b/eng/common/templates/jobs/jobs.yml @@ -8,10 +8,6 @@ parameters: # Optional: Enable publishing using release pipelines enablePublishUsingPipelines: false - # Optional: Disable component governance detection. In general, component governance - # should be on for all jobs. Use only in the event of issues. - disableComponentGovernance: false - # Optional: Enable running the source-build jobs to build repo from source enableSourceBuild: false @@ -41,11 +37,6 @@ parameters: # 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. -# Sbom related params - enableSbom: true - PackageVersion: 7.0.0 - BuildDropPath: '$(Build.SourcesDirectory)/artifacts' - jobs: - ${{ each job in parameters.jobs }}: - template: ../job/job.yml diff --git a/eng/common/templates/steps/retain-build.yml b/eng/common/templates/steps/retain-build.yml new file mode 100644 index 0000000000000..83d97a26a01ff --- /dev/null +++ b/eng/common/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/global.json b/global.json index 4d865d78f5387..ee7e263ef19f1 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22114.7", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22114.7" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22117.2", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22117.2" } } From f92de36e0e5f02a8b57c1d65a7b4db7df974432e Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Sat, 19 Feb 2022 12:44:35 -0800 Subject: [PATCH 116/187] Add build step to generate SBOM --- azure-pipelines-official.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index e43cb5206ac23..0fa14ae3690ea 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -180,6 +180,8 @@ stages: /p:DotnetPublishUsingPipelines=true /p:IgnoreIbcMergeErrors=true condition: succeeded() + + - template: eng\common\templates\steps\generate-sbom.yml - task: PowerShell@2 displayName: Publish Assets From c85df2bab34a96052642097129b4029328f8ef7f Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sat, 19 Feb 2022 20:42:43 -0800 Subject: [PATCH 117/187] Additional tests for lambda attributes, explicit return type, and inferred delegate type (#59634) --- .../Semantic/Semantics/DelegateTypeTests.cs | 674 +++++++++++++++++- .../Test/Semantic/Semantics/LambdaTests.cs | 153 +++- .../Semantics/OverloadResolutionPerfTests.cs | 42 +- .../Parsing/LambdaAttributeParsingTests.cs | 97 +++ .../Parsing/LambdaReturnTypeParsingTests.cs | 45 ++ .../Syntax/Syntax/SyntaxNormalizerTests.cs | 29 + 6 files changed, 1029 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index e9ad802ad31d1..84c8031cf61f3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -1413,6 +1414,49 @@ static class B Assert.Null(symbolInfo.Symbol); } + [Fact] + public void Discard() + { + var source = +@"class Program +{ + static void F() { } + static void F(object o) { } + static void Main() + { + _ = Main; + _ = F; + _ = () => { }; + _ = x => x; + } +}"; + + var expectedDiagnostics = new[] + { + // (7,9): error CS8183: Cannot infer the type of implicitly-typed discard. + // _ = Main; + Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(7, 9), + // (8,9): error CS8183: Cannot infer the type of implicitly-typed discard. + // _ = F; + Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(8, 9), + // (9,9): error CS8183: Cannot infer the type of implicitly-typed discard. + // _ = () => { }; + Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(9, 9), + // (10,9): error CS8183: Cannot infer the type of implicitly-typed discard. + // _ = x => x; + Diagnostic(ErrorCode.ERR_DiscardTypeInferenceFailed, "_").WithLocation(10, 9) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); + } + [WorkItem(55923, "https://github.com/dotnet/roslyn/issues/55923")] [Fact] public void ConvertMethodGroupToObject_01() @@ -1986,7 +2030,7 @@ static void Main() } static Delegate F1() { - return (T t, ref int p) => { }; + return (T t, ref int i) => { }; } static Delegate F2() { @@ -2799,6 +2843,39 @@ static unsafe void Main() Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "(int* p) => p").WithArguments("System.IntPtr").WithLocation(6, 13)); } + [Fact] + public void SystemDelegate_Missing() + { + var sourceA = +@"namespace System +{ + public class Object { } + public abstract class ValueType { } + public class String { } + public class Type { } + public struct Void { } + public struct Boolean { } + public struct Int32 { } + public struct IntPtr { } +}"; + var sourceB = +@"class Program +{ + static void F(ref object o) { } + static void Main() + { + var d1 = F; + var d2 = (ref int i) => i; + } +}"; + var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }); + comp.VerifyEmitDiagnostics( + // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. + Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), + // error CS0518: Predefined type 'System.MulticastDelegate' is not defined or imported + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.MulticastDelegate").WithLocation(1, 1)); + } + [Fact] public void SystemMulticastDelegate_Missing() { @@ -5067,7 +5144,7 @@ static void Main() } [Fact] - public void ConditionalOperator_01() + public void NullCoalescingOperator_01() { var source = @"class Program @@ -5881,6 +5958,307 @@ static void Main() Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2(T, in T)").WithLocation(11, 16)); } + [Fact] + public void TypeInference_Variance_01() + { + var source = +@"using System; +class Program +{ + static T F(T x, T y) => y; + static void Main() + { + Report(F(() => string.Empty, () => new object())); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(() => string.Empty, () => new object())); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"System.Func`1[System.Object] +"); + } + + [Fact] + public void TypeInference_Variance_02() + { + var source = +@"using System; +class Program +{ + static T F(T x, T y) => y; + static void Main() + { + Report(F((string s) => { }, (object o) => { })); + Report(F(string () => default, object () => default)); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((string s) => { }, (object o) => { })); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16), + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(string () => default, object () => default)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16), + // (8,18): error CS8773: Feature 'lambda return type' is not available in C# 9.0. Please use language version 10.0 or greater. + // Report(F(string () => default, object () => default)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "string").WithArguments("lambda return type", "10.0").WithLocation(8, 18), + // (8,40): error CS8773: Feature 'lambda return type' is not available in C# 9.0. Please use language version 10.0 or greater. + // Report(F(string () => default, object () => default)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "object").WithArguments("lambda return type", "10.0").WithLocation(8, 40)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,37): error CS1661: Cannot convert lambda expression to type 'Action' because the parameter types do not match the delegate parameter types + // Report(F((string s) => { }, (object o) => { })); + Diagnostic(ErrorCode.ERR_CantConvAnonMethParams, "(object o) => { }").WithArguments("lambda expression", "System.Action").WithLocation(7, 37), + // (7,45): error CS1678: Parameter 1 is declared as type 'object' but should be 'string' + // Report(F((string s) => { }, (object o) => { })); + Diagnostic(ErrorCode.ERR_BadParamType, "o").WithArguments("1", "", "object", "", "string").WithLocation(7, 45), + // (8,18): error CS8934: Cannot convert lambda expression to type 'Func' because the return type does not match the delegate return type + // Report(F(string () => default, object () => default)); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturnType, "string () => default").WithArguments("lambda expression", "System.Func").WithLocation(8, 18)); + } + + [Fact] + public void TypeInference_Variance_03() + { + var source = +@"using System; +class Program +{ + static void F1(T t) { } + static T F2() => default; + static T F(T x, T y) => y; + static void Main() + { + Report(F(F1, F1)); + Report(F(F2, F2)); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (9,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(9, 16), + // (10,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F2, F2)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(10, 16)); + + CompileAndVerify(source, options: TestOptions.ReleaseExe, expectedOutput: +@"System.Action`1[System.String] +System.Func`1[System.Object] +"); + } + + [Fact] + [WorkItem(55909, "https://github.com/dotnet/roslyn/issues/55909")] + public void TypeInference_Variance_04() + { + var source = +@"using System; +class Program +{ + static T F(T x, T y) => y; + static void Main() + { + Report(F((out string s) => { s = default; }, (out object o) => { o = default; })); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((out string s) => { s = default; }, (out object o) => { o = default; }); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((out string s) => { s = default; }, (out object o) => { o = default; }); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16)); + } + + [Fact] + [WorkItem(55909, "https://github.com/dotnet/roslyn/issues/55909")] + public void TypeInference_Variance_05() + { + var source = +@"using System; +class Program +{ + static void F1(out T t) { t = default; } + static T F(T x, T y) => y; + static void Main() + { + Report(F(F1, F1)); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16)); + + // Compile and execute after fixing https://github.com/dotnet/roslyn/issues/55909. + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16)); + } + + [Fact] + public void TypeInference_Variance_06() + { + var source = +@"using System; +class Program +{ + static T F(T x, T y) => y; + static void Main() + { + Report(F((ref string x) => { }, (ref object y) => { })); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((out string s) => { s = default; }, (out object o) => { o = default; }); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((out string s) => { s = default; }, (out object o) => { o = default; }); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16)); + } + + [Fact] + public void TypeInference_Variance_07() + { + var source = +@"using System; +class Program +{ + static void F1(ref T t) { } + static T F(T x, T y) => y; + static void Main() + { + Report(F(F1, F1)); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16)); + } + + [Fact] + [WorkItem(55909, "https://github.com/dotnet/roslyn/issues/55909")] + public void TypeInference_Variance_08() + { + var source = +@"using System; +class Program +{ + static T F(T x, T y) => y; + static void Main() + { + Report(F((ref int i, string s) => { }, (ref int i, object o) => { })); + Report(F(string (ref int i) => default, object (ref int i) => default)); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((ref int i, string s) => { }, (ref int i, object o) => { })); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16), + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(string (ref int i) => default, object (ref int i) => default)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16), + // (8,18): error CS8773: Feature 'lambda return type' is not available in C# 9.0. Please use language version 10.0 or greater. + // Report(F(string (ref int i) => default, object (ref int i) => default)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "string").WithArguments("lambda return type", "10.0").WithLocation(8, 18), + // (8,49): error CS8773: Feature 'lambda return type' is not available in C# 9.0. Please use language version 10.0 or greater. + // Report(F(string (ref int i) => default, object (ref int i) => default)); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "object").WithArguments("lambda return type", "10.0").WithLocation(8, 49)); + + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F((ref int i, string s) => { }, (ref int i, object o) => { })); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(7, 16), + // (8,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(string (ref int i) => default, object (ref int i) => default)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(8, 16)); + } + + [Fact] + [WorkItem(55909, "https://github.com/dotnet/roslyn/issues/55909")] + public void TypeInference_Variance_09() + { + var source = +@"using System; +class Program +{ + static void F1(ref int i, T t) { } + static T F2(ref int i) => default; + static T F(T x, T y) => y; + static void Main() + { + Report(F(F1, F1)); + Report(F(F2, F2)); + } + static void Report(object obj) => Console.WriteLine(obj.GetType()); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (9,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(9, 16), + // (10,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F2, F2)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(10, 16)); + + // Compile and execute after fixing https://github.com/dotnet/roslyn/issues/55909. + comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (9,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F1, F1)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(9, 16), + // (10,16): error CS0411: The type arguments for method 'Program.F(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // Report(F(F2, F2)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F(T, T)").WithLocation(10, 16)); + } + [Fact] public void TypeInference_Nested_01() { @@ -7394,6 +7772,47 @@ static void Main() Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(6, 19)); } + [Fact] + public void ImplicitlyTypedVariables_15() + { + var source = +@"class Program +{ + static string F1() => string.Empty; + static void F2(object o) { } + static void M(bool b) + { + var d1 = b ? () => string.Empty : () => string.Empty; + var d2 = b ? F1 : () => string.Empty; + var d3 = b ? (object o) => { } : F2; + var d4 = b ? F2 : F2; + } +}"; + + var expectedDiagnostics = new[] + { + // (7,18): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'lambda expression' + // var d1 = b ? () => string.Empty : () => string.Empty; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? () => string.Empty : () => string.Empty").WithArguments("lambda expression", "lambda expression").WithLocation(7, 18), + // (8,18): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'lambda expression' + // var d2 = b ? F1 : () => string.Empty; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? F1 : () => string.Empty").WithArguments("method group", "lambda expression").WithLocation(8, 18), + // (9,18): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'lambda expression' and 'method group' + // var d3 = b ? (object o) => { } : F2; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? (object o) => { } : F2").WithArguments("lambda expression", "method group").WithLocation(9, 18), + // (10,18): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'method group' + // var d4 = b ? F2 : F2; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? F2 : F2").WithArguments("method group", "method group").WithLocation(10, 18) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics(expectedDiagnostics); + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics(expectedDiagnostics); + comp = CreateCompilation(source); + comp.VerifyDiagnostics(expectedDiagnostics); + } + [Fact] public void ImplicitlyTypedVariables_UseSiteErrors() { @@ -9742,6 +10161,257 @@ static void Main() Diagnostic(ErrorCode.ERR_CannotClone, "d2").WithArguments("").WithLocation(8, 14)); } + [Fact] + public void Extension_GetAwaiter_01() + { + var source = +@"using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +class Program +{ + static async Task Main() + { + Type type; + type = await Main; + Console.WriteLine(type); + type = await ((ref int x) => x); + Console.WriteLine(type); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +} +class Awaiter : INotifyCompletion +{ + private Delegate _d; + public Awaiter(Delegate d) { _d = d; } + public void OnCompleted(Action a) { } + public Type GetResult() => _d.GetType(); + public bool IsCompleted => true; +} +static class Extensions +{ + public static Awaiter GetAwaiter(this Delegate d) => new Awaiter(d); +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (9,16): error CS4001: Cannot await 'method group' + // type = await Main; + Diagnostic(ErrorCode.ERR_BadAwaitArgIntrinsic, "await Main").WithArguments("method group").WithLocation(9, 16), + // (11,16): error CS4001: Cannot await 'lambda expression' + // type = await ((ref int x) => x); + Diagnostic(ErrorCode.ERR_BadAwaitArgIntrinsic, "await ((ref int x) => x)").WithArguments("lambda expression").WithLocation(11, 16)); + } + + [Fact] + public void Extension_GetAwaiter_02() + { + var source = +@"using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +class Program +{ + static async Task Main() + { + Type type; + type = await (Delegate)Main; + Console.WriteLine(type); + type = await (Delegate)((ref int x) => x); + Console.WriteLine(type); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +} +class Awaiter : INotifyCompletion +{ + private Delegate _d; + public Awaiter(Delegate d) { _d = d; } + public void OnCompleted(Action a) { } + public Type GetResult() => _d.GetType(); + public bool IsCompleted => true; +} +static class Extensions +{ + public static Awaiter GetAwaiter(this Delegate d) => new Awaiter(d); +}"; + CompileAndVerify(source, expectedOutput: +@"System.Func`1[System.Threading.Tasks.Task] +<>F{00000001}`2[System.Int32,System.Int32] +"); + } + + [Fact] + public void Extension_GetEnumerator_01() + { + var source = +@"using System; +using System.Collections.Generic; +class Program +{ + static void Main() + { + foreach(var d in Main) Report(d); + foreach(var d in (ref int x) => x) Report(d); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +} +static class Extensions +{ + public static IEnumerator GetEnumerator(this Delegate d) + { + yield return d; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,26): error CS0446: Foreach cannot operate on a 'method group'. Did you intend to invoke the 'method group'? + // foreach(var d in Main) Report(d); + Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "Main").WithArguments("method group").WithLocation(7, 26), + // (8,26): error CS0446: Foreach cannot operate on a 'lambda expression'. Did you intend to invoke the 'lambda expression'? + // foreach(var d in (ref int x) => x) Report(d); + Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "(ref int x) => x").WithArguments("lambda expression").WithLocation(8, 26)); + } + + [Fact] + public void Extension_GetEnumerator_02() + { + var source = +@"using System; +using System.Collections.Generic; +class Program +{ + static void Main() + { + foreach (var d in (Delegate)Main) Report(d); + foreach (var d in (Delegate)((ref int x) => x)) Report(d); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +} +static class Extensions +{ + public static IEnumerator GetEnumerator(this Delegate d) + { + yield return d; + } +}"; + CompileAndVerify(source, expectedOutput: +@"System.Action +<>F{00000001}`2[System.Int32,System.Int32] +"); + } + + [Fact] + public void Extension_Deconstruct_01() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Type type; + string name; + (type, name) = Main; + Console.WriteLine(type); + (type, name) = (ref int x) => x; + Console.WriteLine(type); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +} +static class Extensions +{ + public static void Deconstruct(this Delegate d, out Type type, out string name) + { + type = d.GetType(); + name = d.Method.Name; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,24): error CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. + // (type, name) = Main; + Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "Main").WithLocation(8, 24), + // (10,24): error CS8131: Deconstruct assignment requires an expression with a type on the right-hand-side. + // (type, name) = (ref int x) => x; + Diagnostic(ErrorCode.ERR_DeconstructRequiresExpression, "(ref int x) => x").WithLocation(10, 24)); + } + + [Fact] + public void Extension_Deconstruct_02() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Type type; + string name; + (type, name) = (Delegate)Main; + Console.WriteLine(type); + (type, name) = (Delegate)((ref int x) => x); + Console.WriteLine(type); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +} +static class Extensions +{ + public static void Deconstruct(this Delegate d, out Type type, out string name) + { + type = d.GetType(); + name = d.Method.Name; + } +}"; + CompileAndVerify(source, expectedOutput: +@"System.Action +<>F{00000001}`2[System.Int32,System.Int32] +"); + } + + [Fact] + public void IOperation() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Delegate d = (int x) => x.ToString(); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var syntax = tree.GetRoot().DescendantNodes().OfType().Single(); + var operation = (IVariableDeclaratorOperation)model.GetOperation(syntax)!; + + var actualText = OperationTreeVerifier.GetOperationTree(comp, operation); + OperationTreeVerifier.Verify( +@"IVariableDeclaratorOperation (Symbol: System.Delegate d) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'd = (int x) ... .ToString()') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= (int x) = ... .ToString()') + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Delegate, IsImplicit) (Syntax: '(int x) => x.ToString()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Func, IsImplicit) (Syntax: '(int x) => x.ToString()') + Target: + IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null) (Syntax: '(int x) => x.ToString()') + IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'x.ToString()') + IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'x.ToString()') + ReturnedValue: + IInvocationOperation (virtual System.String System.Int32.ToString()) (OperationKind.Invocation, Type: System.String) (Syntax: 'x.ToString()') + Instance Receiver: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') + Arguments(0) +", + actualText); + + var value = ((IConversionOperation)operation.Initializer!.Value).Operand; + Assert.Equal("System.Func", value.Type.ToTestDisplayString()); + } + [Fact] public void ClassifyConversionFromExpression() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index 17855adb842ae..7fe132336e9b2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -3858,10 +3858,23 @@ static void Main() var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); - var attributeSyntaxes = tree.GetRoot().DescendantNodes().OfType(); - var actualAttributes = attributeSyntaxes.Select(a => model.GetSymbolInfo(a).Symbol.GetSymbol()).ToImmutableArray(); - var expectedAttributes = new[] { "AAttribute", "BAttribute", "CAttribute", "DAttribute" }.Select(a => comp.GetTypeByMetadataName(a).InstanceConstructors.Single()).ToImmutableArray(); - AssertEx.Equal(expectedAttributes, actualAttributes); + var attributeSyntaxes = tree.GetRoot().DescendantNodes().OfType().ToImmutableArray(); + Assert.Equal(4, attributeSyntaxes.Length); + verify(attributeSyntaxes[0], "AAttribute"); + verify(attributeSyntaxes[1], "BAttribute"); + verify(attributeSyntaxes[2], "CAttribute"); + verify(attributeSyntaxes[3], "DAttribute"); + + void verify(AttributeSyntax attributeSyntax, string expectedAttributeName) + { + var expectedAttributeConstructor = comp.GetTypeByMetadataName(expectedAttributeName).InstanceConstructors.Single().GetPublicSymbol(); + var expectedAttributeType = expectedAttributeConstructor.ContainingType; + var typeInfo = model.GetTypeInfo(attributeSyntax); + Assert.Equal(expectedAttributeType, typeInfo.Type); + Assert.Equal(expectedAttributeType, typeInfo.ConvertedType); + var symbol = model.GetSymbolInfo(attributeSyntax).Symbol; + Assert.Equal(expectedAttributeConstructor, symbol); + } } [Theory] @@ -5380,6 +5393,138 @@ static void F(string? x, string y) Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "string? () =>").WithArguments("lambda expression", "System.Func").WithLocation(8, 27)); } + [Fact] + public void LambdaReturnType_18() + { + var source = +@"using System; +class Program +{ + static void F(T t, U u, V v) where U : T + { + Func f1 = T () => u; + Func f2 = T () => v; + Func f3 = U () => t; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,30): error CS0029: Cannot implicitly convert type 'V' to 'T' + // Func f2 = T () => v; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "v").WithArguments("V", "T").WithLocation(7, 30), + // (7,30): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // Func f2 = T () => v; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "v").WithArguments("lambda expression").WithLocation(7, 30), + // (8,30): error CS0266: Cannot implicitly convert type 'T' to 'U'. An explicit conversion exists (are you missing a cast?) + // Func f3 = U () => t; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "t").WithArguments("T", "U").WithLocation(8, 30), + // (8,30): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // Func f3 = U () => t; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "t").WithArguments("lambda expression").WithLocation(8, 30)); + } + + [Fact] + public void LambdaReturnType_19() + { + var source = +@"using System; +class Program +{ + static void F(T t, U u, V v) where U : T + { + Delegate d; + d = T () => u; + d = T () => v; + d = U () => t; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,21): error CS0029: Cannot implicitly convert type 'V' to 'T' + // d = T () => v; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "v").WithArguments("V", "T").WithLocation(8, 21), + // (8,21): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // d = T () => v; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "v").WithArguments("lambda expression").WithLocation(8, 21), + // (9,21): error CS0266: Cannot implicitly convert type 'T' to 'U'. An explicit conversion exists (are you missing a cast?) + // d = U () => t; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "t").WithArguments("T", "U").WithLocation(9, 21), + // (9,21): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // d = U () => t; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "t").WithArguments("lambda expression").WithLocation(9, 21)); + } + + [Fact] + public void LambdaReturnType_20() + { + var source = +@"using System; +class Program +{ + static void F(T t, U u, V v) where U : T + { + Func f1 = T () => { if (t is null) return t; return u; }; + Func f2 = U () => { if (t is null) return t; return u; }; + Func f3 = T () => { if (t is null) return t; return v; }; + Func f4 = V () => { if (t is null) return t; return v; }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (7,54): error CS0266: Cannot implicitly convert type 'T' to 'U'. An explicit conversion exists (are you missing a cast?) + // Func f2 = U () => { if (t is null) return t; return u; }; + Diagnostic(ErrorCode.ERR_NoImplicitConvCast, "t").WithArguments("T", "U").WithLocation(7, 54), + // (7,54): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // Func f2 = U () => { if (t is null) return t; return u; }; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "t").WithArguments("lambda expression").WithLocation(7, 54), + // (8,64): error CS0029: Cannot implicitly convert type 'V' to 'T' + // Func f3 = T () => { if (t is null) return t; return v; }; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "v").WithArguments("V", "T").WithLocation(8, 64), + // (8,64): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // Func f3 = T () => { if (t is null) return t; return v; }; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "v").WithArguments("lambda expression").WithLocation(8, 64), + // (9,54): error CS0029: Cannot implicitly convert type 'T' to 'V' + // Func f4 = V () => { if (t is null) return t; return v; }; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "t").WithArguments("T", "V").WithLocation(9, 54), + // (9,54): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // Func f4 = V () => { if (t is null) return t; return v; }; + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "t").WithArguments("lambda expression").WithLocation(9, 54)); + } + + [Fact] + public void LambdaReturnType_SemanticModel() + { + var source = +@"class Program +{ + static void F() + { + var x = T () => default; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var lambdaSyntax = tree.GetRoot().DescendantNodes().OfType().Single(); + + var expectedType = comp.GetMember("Program.F").TypeParameters.Single().GetPublicSymbol(); + Assert.Equal(TypeKind.TypeParameter, expectedType.TypeKind); + Assert.Equal("T", expectedType.ToTestDisplayString()); + + var method = (IMethodSymbol)model.GetSymbolInfo(lambdaSyntax).Symbol; + Assert.Equal(MethodKind.LambdaMethod, method.MethodKind); + + var returnTypeSyntax = lambdaSyntax.ReturnType; + var typeInfo = model.GetTypeInfo(returnTypeSyntax); + Assert.Equal(expectedType, typeInfo.Type); + Assert.Equal(expectedType, typeInfo.ConvertedType); + + var symbolInfo = model.GetSymbolInfo(returnTypeSyntax); + Assert.Equal(expectedType, symbolInfo.Symbol); + } + [Fact] public void LambdaReturnType_CustomModifiers_01() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs index 32ef2edbcaf87..32e6578244253 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OverloadResolutionPerfTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests public class OverloadResolutionPerfTests : CSharpTestBase { [WorkItem(13685, "https://github.com/dotnet/roslyn/issues/13685")] - [ConditionalFactAttribute(typeof(IsRelease), typeof(NoIOperationValidation))] + [ConditionalFact(typeof(IsRelease), typeof(NoIOperationValidation))] public void Overloads() { const int n = 3000; @@ -44,7 +44,7 @@ public void Overloads() } [WorkItem(13685, "https://github.com/dotnet/roslyn/issues/13685")] - [ConditionalFactAttribute(typeof(IsRelease), typeof(NoIOperationValidation))] + [ConditionalFact(typeof(IsRelease), typeof(NoIOperationValidation))] public void BinaryOperatorOverloads() { const int n = 3000; @@ -158,7 +158,7 @@ public void ExtensionMethodsWithLambdaAndParams() comp.VerifyDiagnostics(); } - [ConditionalFactAttribute(typeof(IsRelease), typeof(NoIOperationValidation))] + [ConditionalFact(typeof(IsRelease), typeof(NoIOperationValidation))] public void ExtensionMethodsWithLambdaAndErrors() { const int n = 200; @@ -287,7 +287,7 @@ static class Ext comp.VerifyDiagnostics(); } - [ConditionalFactAttribute(typeof(IsRelease))] + [ConditionalFact(typeof(IsRelease))] [WorkItem(40495, "https://github.com/dotnet/roslyn/issues/40495")] public void NestedLambdas_01() { @@ -311,9 +311,41 @@ static void Main() comp.VerifyDiagnostics(); } + /// + /// A variation of but with + /// explicit parameter types and return type for the lambdas. + /// + [ConditionalFact(typeof(IsRelease))] + public void NestedLambdas_WithParameterAndReturnTypes() + { + var source = +@"#nullable enable +using System.Linq; +class Program +{ + static void Main() + { + Enumerable.Range(0, 1).Sum(int (int a) => + Enumerable.Range(0, 1).Sum(int (int b) => + Enumerable.Range(0, 1).Sum(int (int c) => + Enumerable.Range(0, 1).Sum(int (int d) => + Enumerable.Range(0, 1).Sum(int (int e) => + Enumerable.Range(0, 1).Sum(int (int f) => + Enumerable.Range(0, 1).Sum(int (int g) => + Enumerable.Range(0, 1).Sum(int (int h) => + Enumerable.Range(0, 1).Sum(int (int i) => + Enumerable.Range(0, 1).Sum(int (int j) => + Enumerable.Range(0, 1).Sum(int (int k) => + Enumerable.Range(0, 1).Count(l => true)))))))))))); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + // Test should complete in several seconds if UnboundLambda.ReallyBind // uses results from _returnInferenceCache. - [ConditionalFactAttribute(typeof(IsRelease))] + [ConditionalFact(typeof(IsRelease))] [WorkItem(1083969, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1083969")] public void NestedLambdas_02() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs index ff4abdd5ca3a9..005d2a46705eb 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaAttributeParsingTests.cs @@ -2773,6 +2773,103 @@ public void CollectionInitializer_03() EOF(); } + [Fact] + public void ObjectInitializer_01() + { + string source = "new() { P = [A] x => x, Q = [B] () => { } }"; + UsingExpression(source, TestOptions.Regular9); + verify(); + + UsingExpression(source, TestOptions.Regular10); + verify(); + + void verify() + { + N(SyntaxKind.ImplicitObjectCreationExpression); + { + N(SyntaxKind.NewKeyword); + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ObjectInitializerExpression); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "P"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleLambdaExpression); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Q"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + } + [Fact] public void AnonymousType_01() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs index 4350d251a9d81..b0609099684ab 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/LambdaReturnTypeParsingTests.cs @@ -5003,6 +5003,51 @@ public void Async_06() EOF(); } + [Fact] + public void Async_TopLevelStatement() + { + string source = "async MyMethod() => null;"; + UsingTree(source, TestOptions.Regular9); + verify(); + + UsingTree(source, TestOptions.Regular10); + verify(); + + void verify() + { + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "async"); + } + N(SyntaxKind.IdentifierToken, "MyMethod"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } + [Fact] public void Dynamic_01() { diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs index d72a43893e72a..1e50707c56596 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs @@ -399,6 +399,35 @@ public void TestNormalizeStatement1() TestNormalizeStatement("Func f = blah;", "Func f = blah;"); } + [Theory] + [InlineData("[ return:A ]void Local( [ B ]object o){}", "[return: A]\r\nvoid Local([B] object o)\r\n{\r\n}")] + [InlineData("[A,B][C]T Local()=>default;", "[A, B]\r\n[C]\r\nT Local() => default;")] + public void TestLocalFunctionAttributes(string text, string expected) + { + TestNormalizeStatement(text, expected); + } + + [Theory] + [InlineData("( [ A ]x)=>x", "([A] x) => x")] + [InlineData("[return:A]([B]object o)=>{}", "[return: A]\r\n([B] object o) =>\r\n{\r\n}")] + [InlineData("[ A ,B ] [C]()=>x", "[A, B]\r\n[C]\r\n() => x")] + [InlineData("[A]B()=>{ }", "[A]\r\nB() =>\r\n{\r\n}")] + [WorkItem(59653, "https://github.com/dotnet/roslyn/issues/59653")] + public void TestLambdaAttributes(string text, string expected) + { + TestNormalizeExpression(text, expected); + } + + [Theory] + [InlineData("int( x )=>x", "int (x) => x")] + [InlineData("A( B b )=>{}", "A(B b) =>\r\n{\r\n}")] + [InlineData("static\r\nasync\r\nA()=>x", "static async A() => x")] + [WorkItem(59653, "https://github.com/dotnet/roslyn/issues/59653")] + public void TestLambdaReturnType(string text, string expected) + { + TestNormalizeExpression(text, expected); + } + [Theory] [InlineData("int*p;", "int* p;")] [InlineData("int *p;", "int* p;")] From 7e835c8c324e294b467ba69648a2049e3acbcc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Sat, 19 Feb 2022 22:02:52 -0800 Subject: [PATCH 118/187] SyntaxWrappingOptions (#59604) --- .../Wrapping/AbstractWrappingTests.cs | 20 ++++++---- .../CodeActions/AbstractCodeActionTest.cs | 16 +------- .../AbstractParameterWrappingTests.vb | 19 ++++++--- .../Wrapping/CSharpSyntaxWrappingOptions.cs | 39 +++++++++++++++++++ .../CSharpWrappingCodeRefactoringProvider.cs | 5 +++ .../CSharpArgumentWrapper.cs | 3 +- .../CSharpInitializerExpressionWrapper.cs | 7 ++-- .../CSharpParameterWrapper.cs | 5 +-- .../Wrapping/AbstractCodeActionComputer.cs | 22 ++++------- .../Core/Portable/Wrapping/AbstractWrapper.cs | 8 ++-- ...AbstractWrappingCodeRefactoringProvider.cs | 9 ++++- .../AbstractBinaryExpressionWrapper.cs | 3 +- .../BinaryExpressionCodeActionComputer.cs | 8 ++-- .../AbstractChainedExpressionWrapper.cs | 3 +- .../ChainedExpressionCodeActionComputer.cs | 8 ++-- .../Core/Portable/Wrapping/ISyntaxWrapper.cs | 2 +- .../AbstractSeparatedSyntaxListWrapper.cs | 5 +-- .../SeparatedSyntaxListCodeActionComputer.cs | 6 +-- .../Wrapping/SyntaxWrappingOptions.cs | 34 ++++++++++++++++ ...ctVisualBasicSeparatedSyntaxListWrapper.vb | 4 +- .../VisualBasicArgumentWrapper.vb | 1 - .../VisualBasicSyntaxWrappingOptions.vb | 35 +++++++++++++++++ ...ualBasicWrappingCodeRefactoringProvider.vb | 6 +++ .../Portable/Workspace/Solution/Document.cs | 7 ++++ .../Core/Formatting/FormattingOptions2.cs | 13 ------- .../Core/CodeFixes/CodeActionOptions.cs | 15 ++++++- 26 files changed, 210 insertions(+), 93 deletions(-) create mode 100644 src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs create mode 100644 src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs create mode 100644 src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb diff --git a/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs b/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs index 99210d479517f..55b01386e6e7f 100644 --- a/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs +++ b/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs @@ -9,7 +9,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.SymbolSearch; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Wrapping { @@ -18,17 +18,23 @@ public abstract class AbstractWrappingTests : AbstractCSharpCodeActionTest protected sealed override ImmutableArray MassageActions(ImmutableArray actions) => FlattenActions(actions); - private protected OptionsCollection GetIndentionColumn(int column) - => new OptionsCollection(GetLanguage()) - { - { FormattingOptions2.PreferredWrappingColumn, column } - }; + private protected static CodeActionOptions GetIndentionColumn(int column) + => new(SymbolSearchOptions.Default, WrappingColumn: column); protected Task TestAllWrappingCasesAsync( string input, params string[] outputs) { - return TestAllWrappingCasesAsync(input, options: null, outputs); + return TestAllWrappingCasesAsync(input, options: CodeActionOptions.Default, outputs); + } + + private protected Task TestAllWrappingCasesAsync( + string input, + CodeActionOptions options, + params string[] outputs) + { + var parameters = new TestParameters(codeActionOptions: options); + return TestAllInRegularAndScriptAsync(input, parameters, outputs); } private protected Task TestAllWrappingCasesAsync( diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs index 31ac98f267b19..b00a65ec45540 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionTest.cs @@ -48,29 +48,15 @@ protected override Task> GetDiagnosticsWorkerAsync(Te internal async Task GetCodeRefactoringAsync( TestWorkspace workspace, TestParameters parameters) - { - return (await GetCodeRefactoringsAsync(workspace, parameters)).FirstOrDefault(); - } - - private async Task> GetCodeRefactoringsAsync( - TestWorkspace workspace, TestParameters parameters) { var provider = CreateCodeRefactoringProvider(workspace, parameters); - return SpecializedCollections.SingletonEnumerable( - await GetCodeRefactoringAsync(provider, workspace)); - } - private static async Task GetCodeRefactoringAsync( - CodeRefactoringProvider provider, - TestWorkspace workspace) - { var documentsWithSelections = workspace.Documents.Where(d => !d.IsLinkFile && d.SelectedSpans.Count == 1); Debug.Assert(documentsWithSelections.Count() == 1, "One document must have a single span annotation"); var span = documentsWithSelections.Single().SelectedSpans.Single(); var actions = ArrayBuilder<(CodeAction, TextSpan?)>.GetInstance(); var document = workspace.CurrentSolution.GetDocument(documentsWithSelections.Single().Id); - var options = CodeActionOptions.Default; - var context = new CodeRefactoringContext(document, span, (a, t) => actions.Add((a, t)), options, CancellationToken.None); + var context = new CodeRefactoringContext(document, span, (a, t) => actions.Add((a, t)), parameters.codeActionOptions, CancellationToken.None); await provider.ComputeRefactoringsAsync(context); var result = actions.Count > 0 ? new CodeRefactoring(provider, actions.ToImmutable()) : null; diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb b/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb index 71869c016604f..01c9887ad6705 100644 --- a/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb @@ -6,7 +6,7 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings -Imports Microsoft.CodeAnalysis.Formatting +Imports Microsoft.CodeAnalysis.SymbolSearch Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Wrapping Public MustInherit Class AbstractWrappingTests @@ -16,17 +16,24 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Wrapping Return FlattenActions(actions) End Function - Private Protected Function GetIndentionColumn(column As Integer) As OptionsCollection - Return New OptionsCollection(GetLanguage()) From { - {FormattingOptions2.PreferredWrappingColumn, column} - } + Private Protected Shared Function GetIndentionColumn(column As Integer) As CodeActionOptions + Return New CodeActionOptions(SymbolSearchOptions.Default, WrappingColumn:=column) End Function Protected Function TestAllWrappingCasesAsync( input As String, ParamArray outputs As String()) As Task - Return TestAllWrappingCasesAsync(input, options:=Nothing, outputs) + Return TestAllWrappingCasesAsync(input, options:=CodeActionOptions.Default, outputs) + End Function + + Private Protected Function TestAllWrappingCasesAsync( + input As String, + options As CodeActionOptions, + ParamArray outputs As String()) As Task + + Dim parameters = New TestParameters(codeActionOptions:=options) + Return TestAllInRegularAndScriptAsync(input, parameters, outputs) End Function Private Protected Function TestAllWrappingCasesAsync( diff --git a/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs b/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs new file mode 100644 index 0000000000000..467df8966f241 --- /dev/null +++ b/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Wrapping; + +namespace Microsoft.CodeAnalysis.CSharp.Wrapping +{ + internal sealed class CSharpSyntaxWrappingOptions : SyntaxWrappingOptions + { + public readonly bool NewLinesForBracesInObjectCollectionArrayInitializers; + + public CSharpSyntaxWrappingOptions( + bool useTabs, + int tabSize, + string newLine, + int wrappingColumn, + OperatorPlacementWhenWrappingPreference operatorPlacement, + bool newLinesForBracesInObjectCollectionArrayInitializers) + : base(useTabs, tabSize, newLine, wrappingColumn, operatorPlacement) + { + NewLinesForBracesInObjectCollectionArrayInitializers = newLinesForBracesInObjectCollectionArrayInitializers; + } + + public static CSharpSyntaxWrappingOptions Create(AnalyzerConfigOptions options, CodeActionOptions ideOptions) + => new( + useTabs: options.GetOption(FormattingOptions2.UseTabs), + tabSize: options.GetOption(FormattingOptions2.TabSize), + newLine: options.GetOption(FormattingOptions2.NewLine), + operatorPlacement: options.GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping), + wrappingColumn: ideOptions.WrappingColumn, + newLinesForBracesInObjectCollectionArrayInitializers: options.GetOption(CSharpFormattingOptions2.NewLinesForBracesInObjectCollectionArrayInitializers)); + } +} diff --git a/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs index 30bf98435b21d..31cb4b66cacb4 100644 --- a/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs @@ -5,10 +5,12 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Wrapping.BinaryExpression; using Microsoft.CodeAnalysis.CSharp.Wrapping.ChainedExpression; using Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Wrapping; namespace Microsoft.CodeAnalysis.CSharp.Wrapping @@ -30,5 +32,8 @@ public CSharpWrappingCodeRefactoringProvider() : base(s_wrappers) { } + + protected override SyntaxWrappingOptions GetWrappingOptions(AnalyzerConfigOptions options, CodeActionOptions ideOptions) + => CSharpSyntaxWrappingOptions.Create(options, ideOptions); } } diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs index fcbcafc1fd4a9..bd3dd28d03636 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpArgumentWrapper.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Wrapping; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList @@ -28,7 +29,7 @@ internal partial class CSharpArgumentWrapper public override bool Supports_WrapEveryGroup_UnwrapFirst => true; public override bool Supports_WrapLongGroup_UnwrapFirst => true; - protected override bool ShouldMoveOpenBraceToNewLine(OptionSet options) + protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options) => false; protected override bool ShouldMoveCloseBraceToNewLine diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs index 88cb6940decf4..63d15c6a16377 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.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 Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Wrapping; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList @@ -28,8 +27,8 @@ internal sealed partial class CSharpInitializerExpressionWrapper protected override string Indent_wrapped_items => throw ExceptionUtilities.Unreachable; protected override string Unwrap_and_indent_all_items => throw ExceptionUtilities.Unreachable; - protected override bool ShouldMoveOpenBraceToNewLine(OptionSet options) - => options.GetOption(CSharpFormattingOptions.NewLinesForBracesInObjectCollectionArrayInitializers); + protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options) + => ((CSharpSyntaxWrappingOptions)options).NewLinesForBracesInObjectCollectionArrayInitializers; protected override bool ShouldMoveCloseBraceToNewLine => true; diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs index 21d9c591c45d3..862939dd857c7 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpParameterWrapper.cs @@ -5,11 +5,10 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Utilities; +using Microsoft.CodeAnalysis.Wrapping; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList @@ -30,7 +29,7 @@ internal partial class CSharpParameterWrapper public override bool Supports_WrapEveryGroup_UnwrapFirst => true; public override bool Supports_WrapLongGroup_UnwrapFirst => true; - protected override bool ShouldMoveOpenBraceToNewLine(OptionSet options) + protected override bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options) => false; protected override bool ShouldMoveCloseBraceToNewLine diff --git a/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs index bf337820804ce..09f3e7c4a5fe3 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs @@ -50,11 +50,7 @@ protected abstract class AbstractCodeActionComputer : ICodeActionCompu protected readonly Document OriginalDocument; protected readonly SourceText OriginalSourceText; protected readonly CancellationToken CancellationToken; - - protected readonly bool UseTabs; - protected readonly int TabSize; - protected readonly string NewLine; - protected readonly int WrappingColumn; + protected readonly SyntaxWrappingOptions Options; protected readonly SyntaxTriviaList NewLineTrivia; protected readonly SyntaxTriviaList SingleWhitespaceTrivia; @@ -70,22 +66,18 @@ public AbstractCodeActionComputer( TWrapper service, Document document, SourceText originalSourceText, - DocumentOptionSet options, + SyntaxWrappingOptions options, CancellationToken cancellationToken) { Wrapper = service; OriginalDocument = document; OriginalSourceText = originalSourceText; CancellationToken = cancellationToken; - - UseTabs = options.GetOption(FormattingOptions2.UseTabs); - TabSize = options.GetOption(FormattingOptions2.TabSize); - NewLine = options.GetOption(FormattingOptions2.NewLine); - WrappingColumn = options.GetOption(FormattingOptions2.PreferredWrappingColumn); + Options = options; var generator = SyntaxGenerator.GetGenerator(document); var generatorInternal = document.GetRequiredLanguageService(); - NewLineTrivia = new SyntaxTriviaList(generatorInternal.EndOfLine(NewLine)); + NewLineTrivia = new SyntaxTriviaList(generatorInternal.EndOfLine(options.NewLine)); SingleWhitespaceTrivia = new SyntaxTriviaList(generator.Whitespace(" ")); } @@ -96,9 +88,9 @@ protected string GetSmartIndentationAfter(SyntaxNodeOrToken nodeOrToken) protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOptions.IndentStyle indentStyle) { - var newSourceText = OriginalSourceText.WithChanges(new TextChange(new TextSpan(nodeOrToken.Span.End, 0), NewLine)); + var newSourceText = OriginalSourceText.WithChanges(new TextChange(new TextSpan(nodeOrToken.Span.End, 0), Options.NewLine)); newSourceText = newSourceText.WithChanges( - new TextChange(TextSpan.FromBounds(nodeOrToken.Span.End + NewLine.Length, newSourceText.Length), "")); + new TextChange(TextSpan.FromBounds(nodeOrToken.Span.End + Options.NewLine.Length, newSourceText.Length), "")); var newDocument = OriginalDocument.WithText(newSourceText); var indentationService = Wrapper.IndentationService; @@ -108,7 +100,7 @@ protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOp indentStyle, CancellationToken); - return desiredIndentation.GetIndentationString(newSourceText, UseTabs, TabSize); + return desiredIndentation.GetIndentationString(newSourceText, Options.UseTabs, Options.TabSize); } /// diff --git a/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs b/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs index d3ab4b123d15f..600295aee2041 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractWrapper.cs @@ -2,6 +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.Linq; +using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.Text; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -9,9 +12,6 @@ namespace Microsoft.CodeAnalysis.Wrapping { - using System.Linq; - using Microsoft.CodeAnalysis.Indentation; - using Microsoft.CodeAnalysis.Text; /// /// Common implementation of all . This type takes care of a lot of common logic for @@ -33,7 +33,7 @@ protected AbstractSyntaxWrapper(IIndentationService indentationService) => IndentationService = indentationService; public abstract Task TryCreateComputerAsync( - Document document, int position, SyntaxNode node, bool containsSyntaxError, CancellationToken cancellationToken); + Document document, int position, SyntaxNode node, SyntaxWrappingOptions options, bool containsSyntaxError, CancellationToken cancellationToken); protected static async Task ContainsUnformattableContentAsync( Document document, IEnumerable nodesAndTokens, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs index e8154b7ec6117..00498c2fa0bcd 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs @@ -5,7 +5,9 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Wrapping @@ -29,6 +31,8 @@ protected AbstractWrappingCodeRefactoringProvider( _wrappers = wrappers; } + protected abstract SyntaxWrappingOptions GetWrappingOptions(AnalyzerConfigOptions options, CodeActionOptions ideOptions); + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var (document, span, cancellationToken) = context; @@ -39,6 +43,9 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(position); + var configOptions = await document.GetAnalyzerConfigOptionsAsync(cancellationToken).ConfigureAwait(false); + var options = GetWrappingOptions(configOptions, context.Options); + foreach (var node in token.GetRequiredParent().AncestorsAndSelf()) { var containsSyntaxError = node.GetDiagnostics().Any(d => d.Severity == DiagnosticSeverity.Error); @@ -50,7 +57,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte cancellationToken.ThrowIfCancellationRequested(); var computer = await wrapper.TryCreateComputerAsync( - document, position, node, containsSyntaxError, cancellationToken).ConfigureAwait(false); + document, position, node, options, containsSyntaxError, cancellationToken).ConfigureAwait(false); if (computer == null) continue; diff --git a/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs b/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs index fd7c82f566459..45bb422126ba5 100644 --- a/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs @@ -40,7 +40,7 @@ protected AbstractBinaryExpressionWrapper( protected abstract SyntaxTriviaList GetNewLineBeforeOperatorTrivia(SyntaxTriviaList newLine); public sealed override async Task TryCreateComputerAsync( - Document document, int position, SyntaxNode node, bool containsSyntaxError, CancellationToken cancellationToken) + Document document, int position, SyntaxNode node, SyntaxWrappingOptions options, bool containsSyntaxError, CancellationToken cancellationToken) { if (containsSyntaxError) return null; @@ -87,7 +87,6 @@ protected AbstractBinaryExpressionWrapper( return null; var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); return new BinaryExpressionCodeActionComputer( this, document, sourceText, options, binaryExpr, exprsAndOperators, cancellationToken); diff --git a/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs index a692d94ba2991..ed160c6a93512 100644 --- a/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs @@ -23,7 +23,6 @@ private class BinaryExpressionCodeActionComputer : AbstractCodeActionComputer> { private readonly ImmutableArray _exprsAndOperators; - private readonly OperatorPlacementWhenWrappingPreference _preference; /// /// trivia to place at the end of a node prior to a chunk that is wrapped. @@ -48,14 +47,13 @@ public BinaryExpressionCodeActionComputer( AbstractBinaryExpressionWrapper service, Document document, SourceText originalSourceText, - DocumentOptionSet options, + SyntaxWrappingOptions options, TBinaryExpressionSyntax binaryExpression, ImmutableArray exprsAndOperators, CancellationToken cancellationToken) : base(service, document, originalSourceText, options, cancellationToken) { _exprsAndOperators = exprsAndOperators; - _preference = options.GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping); var generator = SyntaxGenerator.GetGenerator(document); @@ -63,7 +61,7 @@ public BinaryExpressionCodeActionComputer( _indentAndAlignTrivia = new SyntaxTriviaList(generator.Whitespace( OriginalSourceText.GetOffset(binaryExpression.Span.Start) - .CreateIndentationString(UseTabs, TabSize))); + .CreateIndentationString(options.UseTabs, options.TabSize))); _smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace( GetSmartIndentationAfter(_exprsAndOperators[1]))); @@ -94,7 +92,7 @@ private ImmutableArray GetWrapEdits(bool align) var opToken = _exprsAndOperators[i].AsToken(); var right = _exprsAndOperators[i + 1].AsNode(); - if (_preference == OperatorPlacementWhenWrappingPreference.BeginningOfLine) + if (Options.OperatorPlacement == OperatorPlacementWhenWrappingPreference.BeginningOfLine) { // convert: // (a == b) && (c == d) to diff --git a/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs b/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs index 70476fe96690b..1305d5462075a 100644 --- a/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs @@ -70,7 +70,7 @@ protected AbstractChainedExpressionWrapper( protected abstract SyntaxTriviaList GetNewLineBeforeOperatorTrivia(SyntaxTriviaList newLine); public sealed override async Task TryCreateComputerAsync( - Document document, int position, SyntaxNode node, bool containsSyntaxError, CancellationToken cancellationToken) + Document document, int position, SyntaxNode node, SyntaxWrappingOptions options, bool containsSyntaxError, CancellationToken cancellationToken) { if (containsSyntaxError) return null; @@ -108,7 +108,6 @@ protected AbstractChainedExpressionWrapper( // Looks good. Create the action computer which will actually determine // the set of wrapping options to provide. var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); return new CallExpressionCodeActionComputer( this, document, sourceText, options, chunks, cancellationToken); } diff --git a/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs index fbe707c82dc73..de9f4dfbf9433 100644 --- a/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs @@ -69,7 +69,7 @@ public CallExpressionCodeActionComputer( AbstractChainedExpressionWrapper service, Document document, SourceText originalSourceText, - DocumentOptionSet options, + SyntaxWrappingOptions options, ImmutableArray> chunks, CancellationToken cancellationToken) : base(service, document, originalSourceText, options, cancellationToken) @@ -84,7 +84,7 @@ public CallExpressionCodeActionComputer( var firstPeriod = chunks[0][0]; _firstPeriodIndentationTrivia = new SyntaxTriviaList(generator.Whitespace( - OriginalSourceText.GetOffset(firstPeriod.SpanStart).CreateIndentationString(UseTabs, TabSize))); + OriginalSourceText.GetOffset(firstPeriod.SpanStart).CreateIndentationString(options.UseTabs, options.TabSize))); _smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace( GetSmartIndentationAfter(firstPeriod))); @@ -116,8 +116,8 @@ private async Task AddUnwrapCodeActionAsync(ArrayBuilder action private async Task AddWrapLongCodeActionAsync(ArrayBuilder actions) { - actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(WrappingColumn, align: false), FeaturesResources.Wrapping, FeaturesResources.Wrap_long_call_chain).ConfigureAwait(false)); - actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(WrappingColumn, align: true), FeaturesResources.Wrapping, FeaturesResources.Wrap_and_align_long_call_chain).ConfigureAwait(false)); + actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(Options.WrappingColumn, align: false), FeaturesResources.Wrapping, FeaturesResources.Wrap_long_call_chain).ConfigureAwait(false)); + actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(Options.WrappingColumn, align: true), FeaturesResources.Wrapping, FeaturesResources.Wrap_and_align_long_call_chain).ConfigureAwait(false)); } private ImmutableArray GetWrapEdits(int wrappingColumn, bool align) diff --git a/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs b/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs index 106c9df08c757..95f1879510e79 100644 --- a/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs @@ -26,6 +26,6 @@ internal interface ISyntaxWrapper /// node passed in. Returns if this Wrapper cannot wrap this node. /// Task TryCreateComputerAsync( - Document document, int position, SyntaxNode node, bool containsSyntaxError, CancellationToken cancellationToken); + Document document, int position, SyntaxNode node, SyntaxWrappingOptions options, bool containsSyntaxError, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs index 7697b676f62ba..cdc655e517918 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs @@ -41,7 +41,7 @@ protected AbstractSeparatedSyntaxListWrapper(IIndentationService indentationServ } protected abstract bool ShouldMoveCloseBraceToNewLine { get; } - protected abstract bool ShouldMoveOpenBraceToNewLine(OptionSet options); + protected abstract bool ShouldMoveOpenBraceToNewLine(SyntaxWrappingOptions options); protected abstract TListSyntax? TryGetApplicableList(SyntaxNode node); protected abstract SeparatedSyntaxList GetListItems(TListSyntax listSyntax); @@ -49,7 +49,7 @@ protected abstract bool PositionIsApplicable( SyntaxNode root, int position, SyntaxNode declaration, bool containsSyntaxError, TListSyntax listSyntax); public override async Task TryCreateComputerAsync( - Document document, int position, SyntaxNode declaration, bool containsSyntaxError, CancellationToken cancellationToken) + Document document, int position, SyntaxNode declaration, SyntaxWrappingOptions options, bool containsSyntaxError, CancellationToken cancellationToken) { var listSyntax = TryGetApplicableList(declaration); if (listSyntax == null) @@ -74,7 +74,6 @@ protected abstract bool PositionIsApplicable( if (containsUnformattableContent) return null; - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return new SeparatedSyntaxListCodeActionComputer( this, document, sourceText, options, listSyntax, listItems, cancellationToken); diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs index 5ac92ee692845..25bd17606fe15 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs @@ -75,7 +75,7 @@ public SeparatedSyntaxListCodeActionComputer( AbstractSeparatedSyntaxListWrapper service, Document document, SourceText sourceText, - DocumentOptionSet options, + SyntaxWrappingOptions options, TListSyntax listSyntax, SeparatedSyntaxList listItems, CancellationToken cancellationToken) @@ -107,7 +107,7 @@ private string GetAfterOpenTokenIdentation() var openToken = _listSyntax.GetFirstToken(); var afterOpenTokenOffset = OriginalSourceText.GetOffset(openToken.Span.End); - var indentString = afterOpenTokenOffset.CreateIndentationString(UseTabs, TabSize); + var indentString = afterOpenTokenOffset.CreateIndentationString(Options.UseTabs, Options.TabSize); return indentString; } @@ -289,7 +289,7 @@ private ImmutableArray GetWrapLongLinesEdits( if (i > 0) { - if (currentOffset < WrappingColumn) + if (currentOffset < Options.WrappingColumn) { // this item would not make us go pass our preferred wrapping column. So // keep it on this line, making sure there's a space between the previous diff --git a/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs b/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs new file mode 100644 index 0000000000000..a7a2883923fd4 --- /dev/null +++ b/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Formatting; + +namespace Microsoft.CodeAnalysis.Wrapping +{ + internal abstract class SyntaxWrappingOptions + { + public readonly bool UseTabs; + public readonly int TabSize; + public readonly string NewLine; + public readonly int WrappingColumn; + public readonly OperatorPlacementWhenWrappingPreference OperatorPlacement; + + protected SyntaxWrappingOptions( + bool useTabs, + int tabSize, + string newLine, + int wrappingColumn, + OperatorPlacementWhenWrappingPreference operatorPlacement) + { + UseTabs = useTabs; + TabSize = tabSize; + NewLine = newLine; + WrappingColumn = wrappingColumn; + OperatorPlacement = operatorPlacement; + } + } +} diff --git a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/AbstractVisualBasicSeparatedSyntaxListWrapper.vb b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/AbstractVisualBasicSeparatedSyntaxListWrapper.vb index cd52bce2ffd0c..8f19cc1c2736c 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/AbstractVisualBasicSeparatedSyntaxListWrapper.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/AbstractVisualBasicSeparatedSyntaxListWrapper.vb @@ -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. -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Indentation +Imports Microsoft.CodeAnalysis.Wrapping Imports Microsoft.CodeAnalysis.Wrapping.SeparatedSyntaxList Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping.SeparatedSyntaxList @@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping.SeparatedSyntaxList ' The visual basic language always requires the open brace to be on the same line as the collection ' being initialized. - Protected NotOverridable Overrides Function ShouldMoveOpenBraceToNewLine(options As OptionSet) As Boolean + Protected NotOverridable Overrides Function ShouldMoveOpenBraceToNewLine(options As SyntaxWrappingOptions) As Boolean Return False End Function End Class diff --git a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb index 2295b38683159..b2dac3164d143 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/SeparatedSyntaxList/VisualBasicArgumentWrapper.vb @@ -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. -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices Imports Microsoft.CodeAnalysis.VisualBasic.Syntax diff --git a/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb b/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb new file mode 100644 index 0000000000000..5d843197b09b7 --- /dev/null +++ b/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb @@ -0,0 +1,35 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.CodeActions +Imports Microsoft.CodeAnalysis.CodeStyle +Imports Microsoft.CodeAnalysis.Wrapping +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Formatting + +Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping + + Friend NotInheritable Class VisualBasicSyntaxWrappingOptions + Inherits SyntaxWrappingOptions + + Public Sub New( + useTabs As Boolean, + tabSize As Integer, + newLine As String, + wrappingColumn As Integer, + operatorPlacement As OperatorPlacementWhenWrappingPreference) + + MyBase.New(useTabs, tabSize, newLine, wrappingColumn, operatorPlacement) + End Sub + + Public Shared Function Create(options As AnalyzerConfigOptions, ideOptions As CodeActionOptions) As VisualBasicSyntaxWrappingOptions + Return New VisualBasicSyntaxWrappingOptions( + useTabs:=options.GetOption(FormattingOptions2.UseTabs), + tabSize:=options.GetOption(FormattingOptions2.TabSize), + newLine:=options.GetOption(FormattingOptions2.NewLine), + operatorPlacement:=options.GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping), + wrappingColumn:=ideOptions.WrappingColumn) + End Function + End Class +End Namespace diff --git a/src/Features/VisualBasic/Portable/Wrapping/VisualBasicWrappingCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/Wrapping/VisualBasicWrappingCodeRefactoringProvider.vb index 782a3e06b0fa5..c0d2488dd6488 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/VisualBasicWrappingCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/VisualBasicWrappingCodeRefactoringProvider.vb @@ -5,7 +5,9 @@ Imports System.Collections.Immutable Imports System.Composition Imports System.Diagnostics.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.Wrapping.BinaryExpression Imports Microsoft.CodeAnalysis.VisualBasic.Wrapping.ChainedExpression Imports Microsoft.CodeAnalysis.VisualBasic.Wrapping.SeparatedSyntaxList @@ -29,5 +31,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping Public Sub New() MyBase.New(s_wrappers) End Sub + + Protected Overrides Function GetWrappingOptions(options As AnalyzerConfigOptions, ideOptions As CodeActionOptions) As SyntaxWrappingOptions + Return VisualBasicSyntaxWrappingOptions.Create(options, ideOptions) + End Function End Class End Namespace diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index a9c751dd05d65..c64163a5a1277 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -494,6 +494,13 @@ internal Task GetOptionsAsync(OptionSet solutionOptions, Canc return _cachedOptions.GetValueAsync(cancellationToken); } + internal async ValueTask GetAnalyzerConfigOptionsAsync(CancellationToken cancellationToken) + { + var optionService = Project.Solution.Workspace.Services.GetRequiredService(); + var documentOptions = await GetOptionsAsync(cancellationToken).ConfigureAwait(false); + return documentOptions.AsAnalyzerConfigOptions(optionService, Project.Language); + } + private void InitializeCachedOptions(OptionSet solutionOptions) { var newAsyncLazy = new AsyncLazy(async c => diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs index f38ab25895024..13282f2a3e29b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs @@ -76,19 +76,6 @@ public Provider() new(FeatureName, FormattingOptionGroups.NewLine, nameof(InsertFinalNewLine), defaultValue: false, storageLocation: EditorConfigStorageLocation.ForBoolOption("insert_final_newline")); - /// - /// Default value of 120 was picked based on the amount of code in a github.com diff at 1080p. - /// That resolution is the most common value as per the last DevDiv survey as well as the latest - /// Steam hardware survey. This also seems to a reasonable length default in that shorter - /// lengths can often feel too cramped for .NET languages, which are often starting with a - /// default indentation of at least 16 (for namespace, class, member, plus the final construct - /// indentation). - /// - /// TODO: Currently the option has no storage and always has its default value. See https://github.com/dotnet/roslyn/pull/30422#issuecomment-436118696. - /// - internal static Option2 PreferredWrappingColumn { get; } = - new(FeatureName, FormattingOptionGroups.NewLine, nameof(PreferredWrappingColumn), defaultValue: 120); - #if !CODE_STYLE internal static readonly ImmutableArray Options = ImmutableArray.Create( UseTabs, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs index 7a27888f664b8..e140fd6eb22fd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CodeActionOptions.cs @@ -22,8 +22,21 @@ internal readonly struct CodeActionOptions internal readonly record struct CodeActionOptions( [property: DataMember(Order = 0)] SymbolSearchOptions SearchOptions, [property: DataMember(Order = 1)] bool HideAdvancedMembers = false, - [property: DataMember(Order = 2)] bool IsBlocking = false) + [property: DataMember(Order = 2)] bool IsBlocking = false, + [property: DataMember(Order = 3)] int WrappingColumn = CodeActionOptions.DefaultWrappingColumn) { + /// + /// Default value of 120 was picked based on the amount of code in a github.com diff at 1080p. + /// That resolution is the most common value as per the last DevDiv survey as well as the latest + /// Steam hardware survey. This also seems to a reasonable length default in that shorter + /// lengths can often feel too cramped for .NET languages, which are often starting with a + /// default indentation of at least 16 (for namespace, class, member, plus the final construct + /// indentation). + /// + /// TODO: Currently the option has no storage and always has its default value. See https://github.com/dotnet/roslyn/pull/30422#issuecomment-436118696. + /// + public const int DefaultWrappingColumn = 120; + public CodeActionOptions() : this(SearchOptions: SymbolSearchOptions.Default) { From 5089eacff07bd6c9741371ab1cef25ba39b3fe08 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Sun, 20 Feb 2022 21:31:00 +0300 Subject: [PATCH 119/187] Fixed odd naming suggestions --- .../DeclarationNameCompletionProviderTests.cs | 87 +++++++++++++++++-- .../DeclarationNameCompletionProvider.cs | 8 +- .../Core/Extensions/ITypeSymbolExtensions.cs | 9 +- 3 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index b4d8e61d2437f..1264e9ac1fae5 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -8,7 +8,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Completion.Providers; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; @@ -2171,6 +2170,7 @@ public void Method() } [WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")] + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TypeIsNullableStructInLocalWithNullableTypeName() { @@ -2187,10 +2187,11 @@ public void Method() } } "; - await VerifyItemExistsAsync(markup, "vs"); + await VerifyItemExistsAsync(markup, "ints"); } [WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")] + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TypeIsNullableStructInLocalWithQuestionMark() { @@ -2207,10 +2208,11 @@ public void Method() } } "; - await VerifyItemExistsAsync(markup, "vs"); + await VerifyItemExistsAsync(markup, "ints"); } [WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")] + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TypeIsNullableReferenceInLocal() { @@ -2227,10 +2229,11 @@ public void Method() } } "; - await VerifyItemExistsAsync(markup, "vs"); + await VerifyItemExistsAsync(markup, "ints"); } [WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")] + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TypeIsNullableStructInParameterWithNullableTypeName() { @@ -2246,10 +2249,11 @@ public void Method(Nullable> $$) } } "; - await VerifyItemExistsAsync(markup, "vs"); + await VerifyItemExistsAsync(markup, "ints"); } [WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")] + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TypeIsNullableStructInParameterWithQuestionMark() { @@ -2263,10 +2267,11 @@ public void Method(ImmutableArray? $$) } } "; - await VerifyItemExistsAsync(markup, "vs"); + await VerifyItemExistsAsync(markup, "ints"); } [WorkItem(1220195, "https://developercommunity2.visualstudio.com/t/Regression-from-1675-Suggested-varia/1220195")] + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task TypeIsNullableReferenceInParameter() { @@ -2282,7 +2287,75 @@ public void Method(IEnumerable? $$) } } "; - await VerifyItemExistsAsync(markup, "vs"); + await VerifyItemExistsAsync(markup, "ints"); + } + + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableParameterOfUnmanagedType() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "ints"); + } + + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableParameterOfObject() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "objects"); + } + + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableParameterOfString() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "strings"); + } + + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableGenericParameter() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "generics"); } [Fact, Trait(Traits.Feature, Traits.Features.Completion)] diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs index 590db74622cdf..cca2a44d93c32 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -21,7 +20,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Naming; @@ -188,6 +186,12 @@ private static Glyph GetGlyph(SymbolKind kind, Accessibility? declaredAccessibil return (type, wasPlural); } + // The main purpose of this is to prevent converting "string" to "chars", but it also simplifies logic for other basic types (int, double, object etc.) + if (type.SpecialType != SpecialType.None) + { + return (type, wasPlural); + } + seenTypes.AddRange(type.GetBaseTypesAndThis()); if (type is IArrayTypeSymbol arrayType) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index ebfd0891ff13a..70d3516da22bf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -9,10 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; -using System.Threading; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -21,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions internal static partial class ITypeSymbolExtensions { public const string DefaultParameterName = "p"; - private const string DefaultBuiltInParameterName = "v"; + private const string GenericParameterName = "generic"; public static bool IsIntegralType([NotNullWhen(returnValue: true)] this ITypeSymbol? type) => type?.SpecialType.IsIntegralType() == true; @@ -368,9 +365,9 @@ private static string GetParameterName(ITypeSymbol? type) return DefaultParameterName; } - if (type.IsSpecialType() || type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) + if (type.Kind == SymbolKind.TypeParameter) { - return DefaultBuiltInParameterName; + return GenericParameterName; } var shortName = type.GetShortName(); From ce2f6c68d9454102a84a9d38d83b887cca4e6012 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Sun, 20 Feb 2022 14:48:33 -0800 Subject: [PATCH 120/187] Do not try to check for escapes in an interpolated raw string literal --- .../Classification/SemanticClassifierTests.cs | 62 +++++++++++++++++++ .../FallbackSyntaxClassifier.cs | 5 ++ .../VirtualChars/CSharpVirtualCharService.cs | 37 +++++++---- 3 files changed, 91 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs index 2e7e57b3eaf49..88cef8127f556 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs @@ -4086,6 +4086,68 @@ public async Task TestStringEscape9(TestHost testHost) Escape(@"}}")); } + [Theory] + [CombinatorialData] + public async Task TestNotStringEscapeInRawLiteral1(TestHost testHost) + { + await TestInMethodAsync(@"var goo = """"""goo\r\nbar"""""";", + testHost, + Keyword("var")); + } + + [Theory] + [CombinatorialData] + public async Task TestNotStringEscapeInRawLiteral2(TestHost testHost) + { + await TestInMethodAsync(@"var goo = """""" + goo\r\nbar + """""";", + testHost, + Keyword("var")); + } + + [Theory] + [CombinatorialData] + public async Task TestNotStringEscapeInRawLiteral3(TestHost testHost) + { + await TestInMethodAsync(@"var goo = $"""""" + goo\r\nbar + """""";", + testHost, + Keyword("var")); + } + + [Theory] + [CombinatorialData] + public async Task TestNotStringEscapeInRawLiteral4(TestHost testHost) + { + await TestInMethodAsync(@"var goo = """"""\"""""";", + testHost, + Keyword("var")); + } + + [Theory] + [CombinatorialData] + public async Task TestNotStringEscapeInRawLiteral5(TestHost testHost) + { + await TestInMethodAsync(@"var goo = """""" + \ + """""";", + testHost, + Keyword("var")); + } + + [Theory] + [CombinatorialData] + public async Task TestNotStringEscapeInRawLiteral6(TestHost testHost) + { + await TestInMethodAsync(@"var goo = $"""""" + \ + """""";", + testHost, + Keyword("var")); + } + [WorkItem(31200, "https://github.com/dotnet/roslyn/issues/31200")] [Theory] [CombinatorialData] diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs index 10cb42c5c7ba9..76bb4f5cd772a 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/LanguageServices/FallbackSyntaxClassifier.cs @@ -36,6 +36,11 @@ public override void AddClassifications( if (virtualChars.IsDefaultOrEmpty) return; + // Can avoid any work if we got the same number of virtual characters back as characters in the string. In + // that case, there are clearly no escaped characters. + if (virtualChars.Length == token.Text.Length) + return; + foreach (var vc in virtualChars) { if (vc.Span.Length > 1) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs index 769ea9ab8bfce..bbf3e04d0a3b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs @@ -54,7 +54,7 @@ protected override VirtualCharSequence TryConvertToVirtualCharsWorker(SyntaxToke return TryConvertStringToVirtualChars(token, "'", "'", escapeBraces: false); if (token.Kind() is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) - return TryConvertRawStringToVirtualChars(token); + return TryConvertRawStringToVirtualChars(token, skipDelimiterQuotes: true); if (token.Kind() == SyntaxKind.InterpolatedStringTextToken) { @@ -64,9 +64,16 @@ protected override VirtualCharSequence TryConvertToVirtualCharsWorker(SyntaxToke if (parent.Parent is InterpolatedStringExpressionSyntax interpolatedString) { - return interpolatedString.StringStartToken.Kind() == SyntaxKind.InterpolatedVerbatimStringStartToken - ? TryConvertVerbatimStringToVirtualChars(token, "", "", escapeBraces: true) - : TryConvertStringToVirtualChars(token, "", "", escapeBraces: true); + return interpolatedString.StringStartToken.Kind() switch + { + SyntaxKind.InterpolatedStringStartToken + => TryConvertStringToVirtualChars(token, "", "", escapeBraces: true), + SyntaxKind.InterpolatedVerbatimStringStartToken + => TryConvertVerbatimStringToVirtualChars(token, "", "", escapeBraces: true), + SyntaxKind.InterpolatedSingleLineRawStringStartToken or SyntaxKind.InterpolatedMultiLineRawStringStartToken + => TryConvertRawStringToVirtualChars(token, skipDelimiterQuotes: false), + _ => default, + }; } } @@ -89,25 +96,29 @@ private static bool IsInDirective(SyntaxNode? node) private static VirtualCharSequence TryConvertVerbatimStringToVirtualChars(SyntaxToken token, string startDelimiter, string endDelimiter, bool escapeBraces) => TryConvertSimpleDoubleQuoteString(token, startDelimiter, endDelimiter, escapeBraces); - private static VirtualCharSequence TryConvertRawStringToVirtualChars(SyntaxToken token) + private static VirtualCharSequence TryConvertRawStringToVirtualChars( + SyntaxToken token, bool skipDelimiterQuotes) { var tokenText = token.Text; var offset = token.SpanStart; - Contract.ThrowIfFalse(tokenText[0] == '"'); - Contract.ThrowIfFalse(tokenText[^1] == '"'); - using var _ = ArrayBuilder.GetInstance(out var result); var startIndexInclusive = 0; var endIndexExclusive = tokenText.Length; - while (tokenText[startIndexInclusive] == '"') + if (skipDelimiterQuotes) { - // All quotes should be paired at the end - Contract.ThrowIfFalse(tokenText[endIndexExclusive - 1] == '"'); - startIndexInclusive++; - endIndexExclusive--; + Contract.ThrowIfFalse(tokenText[0] == '"'); + Contract.ThrowIfFalse(tokenText[^1] == '"'); + + while (tokenText[startIndexInclusive] == '"') + { + // All quotes should be paired at the end + Contract.ThrowIfFalse(tokenText[endIndexExclusive - 1] == '"'); + startIndexInclusive++; + endIndexExclusive--; + } } for (var index = startIndexInclusive; index < endIndexExclusive;) From f7e9c81ec5c82c5520fc201940fcb107a6c70c2f Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Mon, 21 Feb 2022 21:29:02 +0300 Subject: [PATCH 121/187] PR feedvack, fixed wrong tests --- .../AddAnonymousTypeMemberNameTests.cs | 6 +- .../AddParameter/AddParameterTests.cs | 151 +++++++++--------- .../DeclarationNameCompletionProviderTests.cs | 21 ++- ...ionNameCompletionProvider.NameGenerator.cs | 17 +- .../DeclarationNameCompletionProvider.cs | 2 +- .../Core/Extensions/ITypeSymbolExtensions.cs | 20 +-- 6 files changed, 115 insertions(+), 102 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs b/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs index 84578536cf1f6..6e430c9bb5cfc 100644 --- a/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs +++ b/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs @@ -86,7 +86,7 @@ class C { void M() { - var v = new { P = new { Type = this.GetType(), V = this.ToString() } }; + var v = new { Value = new { Type = this.GetType(), String = this.ToString() } }; } }"); } @@ -108,7 +108,7 @@ class C { void M() { - var v = new { P = new { Type = this.GetType(), V = this.ToString() } }; + var v = new { Value = new { Type = this.GetType(), String = this.ToString() } }; } }"); } @@ -130,7 +130,7 @@ class C { void M() { - var v = new { P = new { Type = this.GetType(), Type1 = this.GetType() } }; + var v = new { Value = new { Type = this.GetType(), Type1 = this.GetType() } }; } }"); } diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs index d3961df775dbc..f6642bfcebb94 100644 --- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs @@ -72,7 +72,7 @@ void M() @" class C { - public C(int v) { } + public C(int @int) { } } class D @@ -155,7 +155,7 @@ void M() @" class C { - public C(bool v, int i) { } + public C(bool @bool, int i) { } } class D @@ -187,7 +187,7 @@ void M() @" class C { - public C(int i, bool v) { } + public C(int i, bool @bool) { } } class D @@ -219,7 +219,7 @@ void M() @" class C { - public C(bool v, params int[] i) { } + public C(bool @bool, params int[] i) { } } class D @@ -272,7 +272,7 @@ private void Goo() @" class C { - public C(bool v, + public C(bool @bool, int i, /* goo */ int j) { @@ -309,7 +309,7 @@ private void Goo() class C { public C(int i, - bool v, + bool @bool, /* goo */ int j) { @@ -346,7 +346,7 @@ class C { public C(int i, /* goo */ int j, - bool v) + bool @bool) { } @@ -382,7 +382,7 @@ private void Goo() class C { public C( - bool v, + bool @bool, int i, /* goo */ int j) { @@ -421,7 +421,7 @@ class C { public C( int i, - bool v, + bool @bool, /* goo */ int j) { @@ -460,7 +460,7 @@ class C public C( int i, /* goo */ int j, - bool v) + bool @bool) { } @@ -493,7 +493,7 @@ void M() @" class C { - public C(object p, int i) { } + public C(object value, int i) { } } class D @@ -526,7 +526,7 @@ void M() @" class C { - public C(string s, int v) { } + public C(string s, int @int) { } } class D @@ -559,7 +559,7 @@ void M() @" class C { - public C(int i, int v) { } + public C(int i, int @int) { } } class D @@ -592,7 +592,7 @@ void M() @" class C { - public C(string s, int v) { } + public C(string s, int @int) { } } class D @@ -709,7 +709,7 @@ class C1 { void M1() { - int Local(int v) => 1; + int Local(int @int) => 1; Local(2); } }"); @@ -753,7 +753,7 @@ void M2() @" class C1 { - static void M1(int v) + static void M1(int @int) { } void M2() @@ -788,7 +788,7 @@ void M1() namespace N { static class Extensions { - public static void ExtensionM1(this object o, int v) + public static void ExtensionM1(this object o, int @int) { } } @@ -828,7 +828,7 @@ void M1() namespace N { static class Extensions { - public static void ExtensionM1(this object o, int v) + public static void ExtensionM1(this object o, int @int) { } } @@ -866,7 +866,7 @@ protected virtual void M1() { } } class C1 : Base { - protected override void M1(int v) { } + protected override void M1(int @int) { } void M2() { M1(1); @@ -875,11 +875,11 @@ void M2() var fix_All = @" class Base { - protected virtual void M1(int v) { } + protected virtual void M1(int @int) { } } class C1 : Base { - protected override void M1(int v) { } + protected override void M1(int @int) { } void M2() { M1(1); @@ -909,7 +909,7 @@ void M2() var fix_DeclarationOnly = @" interface I1 { - void M1(int v); + void M1(int @int); } class C1 : I1 { @@ -922,11 +922,11 @@ void M2() var fix_All = @" interface I1 { - void M1(int v); + void M1(int @int); } class C1 : I1 { - void I1.M1(int v) { } + void I1.M1(int @int) { } void M2() { ((I1)this).M1(1); @@ -961,7 +961,7 @@ interface I1 } class C1 : I1 { - public void M1(int v) { } + public void M1(int @int) { } void M2() { M1(1); @@ -970,11 +970,11 @@ void M2() var fix_All = @" interface I1 { - void M1(int v); + void M1(int @int); } class C1 : I1 { - public void M1(int v) { } + public void M1(int @int) { } void M2() { M1(1); @@ -1017,7 +1017,7 @@ interface I2 } class C1 : I1, I2 { - public void M1(int v) { } + public void M1(int @int) { } void M2() { M1(1); @@ -1026,15 +1026,15 @@ void M2() var fix_All = @" interface I1 { - void M1(int v); + void M1(int @int); } interface I2 { - void M1(int v); + void M1(int @int); } class C1 : I1, I2 { - public void M1(int v) { } + public void M1(int @int) { } void M2() { M1(1); @@ -1062,13 +1062,12 @@ void M2() @" class C1 { - void M1(T arg, int v) { } + void M1(T arg, int @int) { } void M2() { M1(1, 2); } }"); - //Should fix to: void M1(T arg, T v) { } } [WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")] @@ -1087,7 +1086,7 @@ void M1() @" class C1 { - void M1(int v) + void M1(int @int) { M1(1); } @@ -1114,7 +1113,7 @@ void M2() class C1 { void M1(string s) { } - void M1(int i, int v) { } + void M1(int i, int @int) { } void M2() { M1(1, 2); @@ -1124,7 +1123,7 @@ void M2() @" class C1 { - void M1(int v, string s) { } + void M1(int @int, string s) { } void M1(int i) { } void M2() { @@ -1157,7 +1156,7 @@ class C1 { void M1(string s1, string s2) { } void M1(string s) { } - void M1(int i, int v) { } + void M1(int i, int @int) { } void M2() { M1(1, 2); @@ -1168,7 +1167,7 @@ void M2() class C1 { void M1(string s1, string s2) { } - void M1(int v, string s) { } + void M1(int @int, string s) { } void M1(int i) { } void M2() { @@ -1199,7 +1198,7 @@ void M2() @" class C1 { - void M1((int, int) t1, (int, string) p) + void M1((int, int) t1, (int, string) value) { } void M2() @@ -1357,7 +1356,7 @@ void M2() class C1 { void M1(int i1, int i2) { } - void M1(double d, int v) { } + void M1(double d, int @int) { } void M2() { M1(1.0, 1); @@ -1389,7 +1388,7 @@ void M2() class C1 { void M1(int i1, int i2) { } - void M1(System.Action a, int v) { } + void M1(System.Action a, int @int) { } void M2() { M1(()=> { }, 1); @@ -1475,7 +1474,7 @@ public async Task TestInvocation_ExisitingTypeArgumentIsNotGeneralized() @" class C1 { - void M1(T v) { } + void M1(T @bool) { } void M2() { [|M1|](true, true); @@ -1486,7 +1485,7 @@ void M2() @" class C1 { - void M1(T v, bool v1) { } + void M1(T @bool, bool bool1) { } void M2() { M1(true, true); @@ -1516,7 +1515,7 @@ static void M2() @" class C1 { - static void M1(bool v, params int[] nums) { } + static void M1(bool @bool, params int[] nums) { } static void M2() { M1(true, 4); @@ -1553,7 +1552,7 @@ void M2() @" class BaseClass { - protected virtual void M1(int v) { } + protected virtual void M1(int @int) { } } class Derived1: BaseClass { @@ -1571,11 +1570,11 @@ void M2() @" class BaseClass { - protected virtual void M1(int v) { } + protected virtual void M1(int @int) { } } class Derived1: BaseClass { - protected override void M1(int v) { } + protected override void M1(int @int) { } } class Test: BaseClass { @@ -1630,7 +1629,7 @@ namespace N1 { partial class C1 { - partial void PartialM(int v); + partial void PartialM(int @int); } } @@ -1639,7 +1638,7 @@ namespace N1 { partial class C1 { - partial void PartialM(int v) { } + partial void PartialM(int @int) { } void M1() { PartialM(1); @@ -1692,7 +1691,7 @@ namespace N1 { partial class C1 { - public partial void PartialM(int v); + public partial void PartialM(int @int); } } @@ -1701,7 +1700,7 @@ namespace N1 { partial class C1 { - public partial void PartialM(int v) { } + public partial void PartialM(int @int) { } void M1() { PartialM(1); @@ -1741,11 +1740,11 @@ namespace N1 { partial class C1 { - partial void PartialM(int v); + partial void PartialM(int @int); } partial class C1 { - partial void PartialM(int v) { } + partial void PartialM(int @int) { } void M1() { PartialM(1); @@ -1835,7 +1834,7 @@ namespace N { public class Derived: BaseClass { - public override void M(int v) { } + public override void M(int @int) { } } public class DerivedDerived: Derived { @@ -1851,7 +1850,7 @@ namespace N { public class Derived: BaseClass { - public override void M({|Conflict:int v|}) { } + public override void M({|Conflict:int @int|}) { } } public class DerivedDerived: Derived { @@ -1933,7 +1932,7 @@ namespace N { public class BaseClass { - public virtual void M(int v) { } + public virtual void M(int @int) { } } } @@ -1945,7 +1944,7 @@ namespace N { public class Derived1: BaseClass { - public override void M(int v) { } + public override void M(int @int) { } } } @@ -1957,7 +1956,7 @@ namespace N { public class Derived2: BaseClass { - public override void M(int v) { } + public override void M(int @int) { } } } @@ -2008,7 +2007,7 @@ interface I1 } class C: I1 { - public void M1(int v) { } + public void M1(int @int) { } void MTest() { M1(1); @@ -2019,11 +2018,11 @@ void MTest() @" interface I1 { - void M1(int v); + void M1(int @int); } class C: I1 { - public void M1(int v) { } + public void M1(int @int) { } void MTest() { M1(1); @@ -2077,7 +2076,7 @@ public void Test() { Namespace N Public Class BaseClass - Public Overridable Sub M(v As Integer) + Public Overridable Sub M(int As Integer) End Sub End Class End Namespace @@ -2090,7 +2089,7 @@ namespace N { public class Derived: BaseClass { - public override void M(int v) { } + public override void M(int @int) { } } public class T { @@ -2123,7 +2122,7 @@ void Test() @" class C { - void M(int v) { } + void M(int @int) { } void Test() { M(1, 2, 3, 4); @@ -2152,7 +2151,7 @@ void Test() @" class C { - void M(int i = 1, int v = 0) { } + void M(int i = 1, int @int = 0) { } void Test() { M(1, 2); @@ -2208,7 +2207,7 @@ void Test() @" class C { - void M(string v, params int[] ints) { } + void M(string @string, params int[] ints) { } void Test() { M(""text""); @@ -2301,7 +2300,7 @@ void Test() @" class C { - void M(int v) { } + void M(int @int) { } void Test() { M(1 @@ -2319,7 +2318,7 @@ public async Task TestInvocation_InvocationStyles_Incomplete_2() @" class C { - void M(int v) { } + void M(int @string) { } void Test() { [|M|](""text"", 1 @@ -2328,7 +2327,7 @@ void Test() @" class C { - void M(string v1, int v) { } + void M(string string1, int @string) { } void Test() { M(""text"", 1 @@ -2516,7 +2515,7 @@ public C(): [|this|](1, 1) @" class C { - public C(int i, int v) { } + public C(int i, int @int) { } public C(): this(1, 1) { } @@ -2579,7 +2578,7 @@ class Rsrp public void M() { Local(""ignore this"", true); - void Local(string whatever, bool v) + void Local(string whatever, bool @bool) { } @@ -2688,7 +2687,7 @@ void M1() namespace N { static class Extensions { - public static IEnumerator GetEnumerator(this object o, int v) + public static IEnumerator GetEnumerator(this object o, int @int) { } } @@ -2732,7 +2731,7 @@ async Task M1() namespace N { static class Extensions { - public static IAsyncEnumerator GetAsyncEnumerator(this object o, int v) + public static IAsyncEnumerator GetAsyncEnumerator(this object o, int @int) { } } @@ -2761,7 +2760,7 @@ void local(int x, int y) @" [|local|](1, 2, 3); -void local(int x, int y, int v) +void local(int x, int y, int @int) { } ", parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp9)); @@ -2786,7 +2785,7 @@ void outer() { local(1, 2, 3); - void local(int x, int y, int v) + void local(int x, int y, int @int) { } } @@ -2810,7 +2809,7 @@ void M() @" class C { - C(int i, int v) { } + C(int i, int @int) { } void M() { diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index 1264e9ac1fae5..daae31a960b34 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -2343,7 +2343,7 @@ public void Method(IEnumerable $$) [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task EnumerableGenericParameter() + public async Task EnumerableGenericTParameter() { var markup = @" using System.Collections.Generic; @@ -2355,7 +2355,24 @@ public void Method(IEnumerable $$) } } "; - await VerifyItemExistsAsync(markup, "generics"); + await VerifyItemExistsAsync(markup, "values"); + } + + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableGenericTNameParameter() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "results"); } [Fact, Trait(Traits.Feature, Traits.Features.Completion)] diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs index a07679bc3b76b..6545758b14d50 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs @@ -4,11 +4,11 @@ using System.Collections.Immutable; using Humanizer; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; using Words = System.Collections.Immutable.ImmutableArray; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers @@ -19,7 +19,7 @@ internal class NameGenerator { internal static ImmutableArray GetBaseNames(ITypeSymbol type, bool pluralize) { - var baseName = TryRemoveInterfacePrefix(type); + var baseName = TryRemoveKnownPrefixes(type); using var parts = TemporaryArray.Empty; StringBreaker.AddWordParts(baseName, ref parts.AsRef()); var result = GetInterleavedPatterns(parts, baseName, pluralize); @@ -34,7 +34,7 @@ internal static ImmutableArray GetBaseNames(IAliasSymbol alias) ((INamedTypeSymbol)alias.Target).IsInterfaceType() && CanRemoveInterfacePrefix(name)) { - name = name.Substring(1); + name = name[1..]; } using var breaks = TemporaryArray.Empty; @@ -94,17 +94,24 @@ private static Words GetWords(int start, int end, in TemporaryArray br return result.ToImmutableAndClear(); } - private static string TryRemoveInterfacePrefix(ITypeSymbol type) + //Tries to remove "I" prefix from interfaces and "T" prefix from generic parameter names + private static string TryRemoveKnownPrefixes(ITypeSymbol type) { var name = type.Name; + if (type.TypeKind == TypeKind.Interface && name.Length > 1) { if (CanRemoveInterfacePrefix(name)) { - return name.Substring(1); + return name[1..]; } } + if (type.TypeKind == TypeKind.TypeParameter) + { + return name == "T" ? ITypeSymbolExtensions.DefaultParameterName : name[1..].ToCamelCase(); + } + return type.CreateParameterName(); } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs index cca2a44d93c32..7a9e26d006756 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs @@ -187,7 +187,7 @@ private static Glyph GetGlyph(SymbolKind kind, Accessibility? declaredAccessibil } // The main purpose of this is to prevent converting "string" to "chars", but it also simplifies logic for other basic types (int, double, object etc.) - if (type.SpecialType != SpecialType.None) + if (type.IsSpecialType()) { return (type, wasPlural); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index 70d3516da22bf..83c0ca7b9ffc0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions { internal static partial class ITypeSymbolExtensions { - public const string DefaultParameterName = "p"; - private const string GenericParameterName = "generic"; + public const string DefaultParameterName = "value"; public static bool IsIntegralType([NotNullWhen(returnValue: true)] this ITypeSymbol? type) => type?.SpecialType.IsIntegralType() == true; @@ -42,8 +41,7 @@ public static IList GetAllInterfacesIncludingThis(this ITypeSy var allInterfaces = type.AllInterfaces; if (type is INamedTypeSymbol namedType && namedType.TypeKind == TypeKind.Interface && !allInterfaces.Contains(namedType)) { - var result = new List(allInterfaces.Length + 1); - result.Add(namedType); + var result = new List(allInterfaces.Length + 1) { namedType }; result.AddRange(allInterfaces); return result; } @@ -365,11 +363,6 @@ private static string GetParameterName(ITypeSymbol? type) return DefaultParameterName; } - if (type.Kind == SymbolKind.TypeParameter) - { - return GenericParameterName; - } - var shortName = type.GetShortName(); return shortName.Length == 0 ? DefaultParameterName @@ -552,9 +545,8 @@ private static IEnumerable SelectAccessibleMembers(this IEnumerable SelectAccessibleMembers(this IEnumerable SelectAccessibleMembers(this IEnumerable Date: Mon, 21 Feb 2022 23:28:10 +0300 Subject: [PATCH 122/187] PR feedback v2 --- .../AddAnonymousTypeMemberNameTests.cs | 4 +- .../AddParameter/AddParameterTests.cs | 146 +++++++++--------- ...ionNameCompletionProvider.NameGenerator.cs | 6 +- .../Extensions/SemanticModelExtensions.cs | 2 +- .../Core/Extensions/ITypeSymbolExtensions.cs | 15 +- 5 files changed, 90 insertions(+), 83 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs b/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs index 6e430c9bb5cfc..868dbaea8a625 100644 --- a/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs +++ b/src/EditorFeatures/CSharpTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.cs @@ -86,7 +86,7 @@ class C { void M() { - var v = new { Value = new { Type = this.GetType(), String = this.ToString() } }; + var v = new { Value = new { Type = this.GetType(), V = this.ToString() } }; } }"); } @@ -108,7 +108,7 @@ class C { void M() { - var v = new { Value = new { Type = this.GetType(), String = this.ToString() } }; + var v = new { Value = new { Type = this.GetType(), V = this.ToString() } }; } }"); } diff --git a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs index f6642bfcebb94..958cf1812f78b 100644 --- a/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/AddParameter/AddParameterTests.cs @@ -72,7 +72,7 @@ void M() @" class C { - public C(int @int) { } + public C(int v) { } } class D @@ -155,7 +155,7 @@ void M() @" class C { - public C(bool @bool, int i) { } + public C(bool v, int i) { } } class D @@ -187,7 +187,7 @@ void M() @" class C { - public C(int i, bool @bool) { } + public C(int i, bool v) { } } class D @@ -219,7 +219,7 @@ void M() @" class C { - public C(bool @bool, params int[] i) { } + public C(bool v, params int[] i) { } } class D @@ -272,7 +272,7 @@ private void Goo() @" class C { - public C(bool @bool, + public C(bool v, int i, /* goo */ int j) { @@ -309,7 +309,7 @@ private void Goo() class C { public C(int i, - bool @bool, + bool v, /* goo */ int j) { @@ -346,7 +346,7 @@ class C { public C(int i, /* goo */ int j, - bool @bool) + bool v) { } @@ -382,7 +382,7 @@ private void Goo() class C { public C( - bool @bool, + bool v, int i, /* goo */ int j) { @@ -421,7 +421,7 @@ class C { public C( int i, - bool @bool, + bool v, /* goo */ int j) { @@ -460,7 +460,7 @@ class C public C( int i, /* goo */ int j, - bool @bool) + bool v) { } @@ -526,7 +526,7 @@ void M() @" class C { - public C(string s, int @int) { } + public C(string s, int v) { } } class D @@ -559,7 +559,7 @@ void M() @" class C { - public C(int i, int @int) { } + public C(int i, int v) { } } class D @@ -592,7 +592,7 @@ void M() @" class C { - public C(string s, int @int) { } + public C(string s, int v) { } } class D @@ -709,7 +709,7 @@ class C1 { void M1() { - int Local(int @int) => 1; + int Local(int v) => 1; Local(2); } }"); @@ -753,7 +753,7 @@ void M2() @" class C1 { - static void M1(int @int) + static void M1(int v) { } void M2() @@ -788,7 +788,7 @@ void M1() namespace N { static class Extensions { - public static void ExtensionM1(this object o, int @int) + public static void ExtensionM1(this object o, int v) { } } @@ -828,7 +828,7 @@ void M1() namespace N { static class Extensions { - public static void ExtensionM1(this object o, int @int) + public static void ExtensionM1(this object o, int v) { } } @@ -866,7 +866,7 @@ protected virtual void M1() { } } class C1 : Base { - protected override void M1(int @int) { } + protected override void M1(int v) { } void M2() { M1(1); @@ -875,11 +875,11 @@ void M2() var fix_All = @" class Base { - protected virtual void M1(int @int) { } + protected virtual void M1(int v) { } } class C1 : Base { - protected override void M1(int @int) { } + protected override void M1(int v) { } void M2() { M1(1); @@ -909,7 +909,7 @@ void M2() var fix_DeclarationOnly = @" interface I1 { - void M1(int @int); + void M1(int v); } class C1 : I1 { @@ -922,11 +922,11 @@ void M2() var fix_All = @" interface I1 { - void M1(int @int); + void M1(int v); } class C1 : I1 { - void I1.M1(int @int) { } + void I1.M1(int v) { } void M2() { ((I1)this).M1(1); @@ -961,7 +961,7 @@ interface I1 } class C1 : I1 { - public void M1(int @int) { } + public void M1(int v) { } void M2() { M1(1); @@ -970,11 +970,11 @@ void M2() var fix_All = @" interface I1 { - void M1(int @int); + void M1(int v); } class C1 : I1 { - public void M1(int @int) { } + public void M1(int v) { } void M2() { M1(1); @@ -1017,7 +1017,7 @@ interface I2 } class C1 : I1, I2 { - public void M1(int @int) { } + public void M1(int v) { } void M2() { M1(1); @@ -1026,15 +1026,15 @@ void M2() var fix_All = @" interface I1 { - void M1(int @int); + void M1(int v); } interface I2 { - void M1(int @int); + void M1(int v); } class C1 : I1, I2 { - public void M1(int @int) { } + public void M1(int v) { } void M2() { M1(1); @@ -1062,7 +1062,7 @@ void M2() @" class C1 { - void M1(T arg, int @int) { } + void M1(T arg, int v) { } void M2() { M1(1, 2); @@ -1086,7 +1086,7 @@ void M1() @" class C1 { - void M1(int @int) + void M1(int v) { M1(1); } @@ -1113,7 +1113,7 @@ void M2() class C1 { void M1(string s) { } - void M1(int i, int @int) { } + void M1(int i, int v) { } void M2() { M1(1, 2); @@ -1123,7 +1123,7 @@ void M2() @" class C1 { - void M1(int @int, string s) { } + void M1(int v, string s) { } void M1(int i) { } void M2() { @@ -1156,7 +1156,7 @@ class C1 { void M1(string s1, string s2) { } void M1(string s) { } - void M1(int i, int @int) { } + void M1(int i, int v) { } void M2() { M1(1, 2); @@ -1167,7 +1167,7 @@ void M2() class C1 { void M1(string s1, string s2) { } - void M1(int @int, string s) { } + void M1(int v, string s) { } void M1(int i) { } void M2() { @@ -1356,7 +1356,7 @@ void M2() class C1 { void M1(int i1, int i2) { } - void M1(double d, int @int) { } + void M1(double d, int v) { } void M2() { M1(1.0, 1); @@ -1388,7 +1388,7 @@ void M2() class C1 { void M1(int i1, int i2) { } - void M1(System.Action a, int @int) { } + void M1(System.Action a, int v) { } void M2() { M1(()=> { }, 1); @@ -1474,7 +1474,7 @@ public async Task TestInvocation_ExisitingTypeArgumentIsNotGeneralized() @" class C1 { - void M1(T @bool) { } + void M1(T v) { } void M2() { [|M1|](true, true); @@ -1485,7 +1485,7 @@ void M2() @" class C1 { - void M1(T @bool, bool bool1) { } + void M1(T v, bool v1) { } void M2() { M1(true, true); @@ -1515,7 +1515,7 @@ static void M2() @" class C1 { - static void M1(bool @bool, params int[] nums) { } + static void M1(bool v, params int[] nums) { } static void M2() { M1(true, 4); @@ -1552,7 +1552,7 @@ void M2() @" class BaseClass { - protected virtual void M1(int @int) { } + protected virtual void M1(int v) { } } class Derived1: BaseClass { @@ -1570,11 +1570,11 @@ void M2() @" class BaseClass { - protected virtual void M1(int @int) { } + protected virtual void M1(int v) { } } class Derived1: BaseClass { - protected override void M1(int @int) { } + protected override void M1(int v) { } } class Test: BaseClass { @@ -1629,7 +1629,7 @@ namespace N1 { partial class C1 { - partial void PartialM(int @int); + partial void PartialM(int v); } } @@ -1638,7 +1638,7 @@ namespace N1 { partial class C1 { - partial void PartialM(int @int) { } + partial void PartialM(int v) { } void M1() { PartialM(1); @@ -1691,7 +1691,7 @@ namespace N1 { partial class C1 { - public partial void PartialM(int @int); + public partial void PartialM(int v); } } @@ -1700,7 +1700,7 @@ namespace N1 { partial class C1 { - public partial void PartialM(int @int) { } + public partial void PartialM(int v) { } void M1() { PartialM(1); @@ -1740,11 +1740,11 @@ namespace N1 { partial class C1 { - partial void PartialM(int @int); + partial void PartialM(int v); } partial class C1 { - partial void PartialM(int @int) { } + partial void PartialM(int v) { } void M1() { PartialM(1); @@ -1834,7 +1834,7 @@ namespace N { public class Derived: BaseClass { - public override void M(int @int) { } + public override void M(int v) { } } public class DerivedDerived: Derived { @@ -1850,7 +1850,7 @@ namespace N { public class Derived: BaseClass { - public override void M({|Conflict:int @int|}) { } + public override void M({|Conflict:int v|}) { } } public class DerivedDerived: Derived { @@ -1932,7 +1932,7 @@ namespace N { public class BaseClass { - public virtual void M(int @int) { } + public virtual void M(int v) { } } } @@ -1944,7 +1944,7 @@ namespace N { public class Derived1: BaseClass { - public override void M(int @int) { } + public override void M(int v) { } } } @@ -1956,7 +1956,7 @@ namespace N { public class Derived2: BaseClass { - public override void M(int @int) { } + public override void M(int v) { } } } @@ -2007,7 +2007,7 @@ interface I1 } class C: I1 { - public void M1(int @int) { } + public void M1(int v) { } void MTest() { M1(1); @@ -2018,11 +2018,11 @@ void MTest() @" interface I1 { - void M1(int @int); + void M1(int v); } class C: I1 { - public void M1(int @int) { } + public void M1(int v) { } void MTest() { M1(1); @@ -2076,7 +2076,7 @@ public void Test() { Namespace N Public Class BaseClass - Public Overridable Sub M(int As Integer) + Public Overridable Sub M(v As Integer) End Sub End Class End Namespace @@ -2089,7 +2089,7 @@ namespace N { public class Derived: BaseClass { - public override void M(int @int) { } + public override void M(int v) { } } public class T { @@ -2122,7 +2122,7 @@ void Test() @" class C { - void M(int @int) { } + void M(int v) { } void Test() { M(1, 2, 3, 4); @@ -2151,7 +2151,7 @@ void Test() @" class C { - void M(int i = 1, int @int = 0) { } + void M(int i = 1, int v = 0) { } void Test() { M(1, 2); @@ -2207,7 +2207,7 @@ void Test() @" class C { - void M(string @string, params int[] ints) { } + void M(string v, params int[] ints) { } void Test() { M(""text""); @@ -2300,7 +2300,7 @@ void Test() @" class C { - void M(int @int) { } + void M(int v) { } void Test() { M(1 @@ -2318,7 +2318,7 @@ public async Task TestInvocation_InvocationStyles_Incomplete_2() @" class C { - void M(int @string) { } + void M(int v) { } void Test() { [|M|](""text"", 1 @@ -2327,7 +2327,7 @@ void Test() @" class C { - void M(string string1, int @string) { } + void M(string v1, int v) { } void Test() { M(""text"", 1 @@ -2515,7 +2515,7 @@ public C(): [|this|](1, 1) @" class C { - public C(int i, int @int) { } + public C(int i, int v) { } public C(): this(1, 1) { } @@ -2578,7 +2578,7 @@ class Rsrp public void M() { Local(""ignore this"", true); - void Local(string whatever, bool @bool) + void Local(string whatever, bool v) { } @@ -2687,7 +2687,7 @@ void M1() namespace N { static class Extensions { - public static IEnumerator GetEnumerator(this object o, int @int) + public static IEnumerator GetEnumerator(this object o, int v) { } } @@ -2731,7 +2731,7 @@ async Task M1() namespace N { static class Extensions { - public static IAsyncEnumerator GetAsyncEnumerator(this object o, int @int) + public static IAsyncEnumerator GetAsyncEnumerator(this object o, int v) { } } @@ -2760,7 +2760,7 @@ void local(int x, int y) @" [|local|](1, 2, 3); -void local(int x, int y, int @int) +void local(int x, int y, int v) { } ", parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp9)); @@ -2785,7 +2785,7 @@ void outer() { local(1, 2, 3); - void local(int x, int y, int @int) + void local(int x, int y, int v) { } } @@ -2809,7 +2809,7 @@ void M() @" class C { - C(int i, int @int) { } + C(int i, int v) { } void M() { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs index 6545758b14d50..add879117cc75 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs @@ -19,7 +19,7 @@ internal class NameGenerator { internal static ImmutableArray GetBaseNames(ITypeSymbol type, bool pluralize) { - var baseName = TryRemoveKnownPrefixes(type); + var baseName = TryRemoveKnownPrefixes(type, pluralize); using var parts = TemporaryArray.Empty; StringBreaker.AddWordParts(baseName, ref parts.AsRef()); var result = GetInterleavedPatterns(parts, baseName, pluralize); @@ -95,7 +95,7 @@ private static Words GetWords(int start, int end, in TemporaryArray br } //Tries to remove "I" prefix from interfaces and "T" prefix from generic parameter names - private static string TryRemoveKnownPrefixes(ITypeSymbol type) + private static string TryRemoveKnownPrefixes(ITypeSymbol type, bool considerPuralization) { var name = type.Name; @@ -112,7 +112,7 @@ private static string TryRemoveKnownPrefixes(ITypeSymbol type) return name == "T" ? ITypeSymbolExtensions.DefaultParameterName : name[1..].ToCamelCase(); } - return type.CreateParameterName(); + return type.CreateParameterName(considerPuralization: considerPuralization); } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 376a5a2763599..19b88cf222fc2 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -188,7 +188,7 @@ public static string GenerateNameFromType(this SemanticModel semanticModel, ITyp // If there's no type argument and we have an array type, we should pluralize, e.g. using 'frogs' for 'new Frog[]' instead of 'frog' if (type.TypeKind == TypeKind.Array && typeArguments.IsEmpty) { - return type.CreateParameterName(capitalize).Pluralize(); + return type.CreateParameterName(capitalize, true).Pluralize(); } // Otherwise assume no pluralization, e.g. using 'immutableArray', 'list', etc. instead of their diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index 83c0ca7b9ffc0..b876262a8aabd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -18,6 +18,7 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions internal static partial class ITypeSymbolExtensions { public const string DefaultParameterName = "value"; + private const string DefaultBuiltInParameterName = "v"; public static bool IsIntegralType([NotNullWhen(returnValue: true)] this ITypeSymbol? type) => type?.SpecialType.IsIntegralType() == true; @@ -335,7 +336,7 @@ private static bool ContainsAnonymousType(INamedTypeSymbol type) return false; } - public static string CreateParameterName(this ITypeSymbol type, bool capitalize = false) + public static string CreateParameterName(this ITypeSymbol type, bool capitalize = false, bool considerPuralization = false) { while (true) { @@ -352,17 +353,22 @@ public static string CreateParameterName(this ITypeSymbol type, bool capitalize break; } - var shortName = GetParameterName(type); + var shortName = GetParameterName(type, considerPuralization); return capitalize ? shortName.ToPascalCase() : shortName.ToCamelCase(); } - private static string GetParameterName(ITypeSymbol? type) + private static string GetParameterName(ITypeSymbol? type, bool considerPuralization) { if (type == null || type.IsAnonymousType() || type.IsTupleType) { return DefaultParameterName; } + if (!considerPuralization && type.IsSpecialType()) + { + return DefaultBuiltInParameterName; + } + var shortName = type.GetShortName(); return shortName.Length == 0 ? DefaultParameterName @@ -568,9 +574,10 @@ private static IEnumerable SelectAccessibleMembers(this IEnumerable Date: Tue, 22 Feb 2022 20:31:48 +0300 Subject: [PATCH 123/187] Moved logic of pluralization check, improved unexpectedly named generic parameters handling, fixed tests --- .../ConvertLinqQueryToForEachTests.cs | 20 +++---- .../IntroduceVariableTests.cs | 56 +++++++++---------- .../DeclarationNameCompletionProviderTests.cs | 34 +++++++++++ .../GenerateMethod/GenerateMethodTests.cs | 20 +++---- .../GenerateType/GenerateTypeTests.cs | 24 ++++---- .../EditAndContinue/StatementEditingTests.cs | 2 +- .../GenerateConstructorTests.cs | 24 ++++---- ...ionNameCompletionProvider.NameGenerator.cs | 26 ++++++--- .../Extensions/SemanticModelExtensions.cs | 15 ++++- .../Core/Extensions/ITypeSymbolExtensions.cs | 24 +++----- 10 files changed, 146 insertions(+), 99 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs index 4d727c363173e..6d79e2d5ae2b3 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertLinqQueryToForEachTests.cs @@ -172,13 +172,13 @@ static void Main(string[] args) { System.Collections.Generic.IEnumerable enumerable() { - var vs1 = new int[] { 1, 2 }; - var vs = new int[] { 3, 4 }; - foreach (var num in vs1) + var ints1 = new int[] { 1, 2 }; + var ints = new int[] { 3, 4 }; + foreach (var num in ints1) { foreach (var a in new int[] { 5, 6 }) { - foreach (var x1 in vs) + foreach (var x1 in ints) { if (object.Equals(num, x1)) { @@ -221,18 +221,18 @@ static void Main(string[] args) { System.Collections.Generic.IEnumerable enumerable() { - var vs2 = new int[] { 1, 2 }; - var vs1 = new int[] { 3, 4 }; - var vs = new int[] { 7, 8 }; - foreach (var num in vs2) + var ints2 = new int[] { 1, 2 }; + var ints1 = new int[] { 3, 4 }; + var ints = new int[] { 7, 8 }; + foreach (var num in ints2) { foreach (var a in new int[] { 5, 6 }) { - foreach (var x1 in vs1) + foreach (var x1 in ints1) { if (object.Equals(num, x1)) { - foreach (var x2 in vs) + foreach (var x2 in ints) { if (object.Equals(num, x2)) { diff --git a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs index 3ab4a4831464e..3ff1f79e41842 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/IntroduceVariable/IntroduceVariableTests.cs @@ -117,8 +117,8 @@ class C { void M(Action action) { - Action {|Rename:p|} = () => { var x[||] = y; }; - M(p); + Action {|Rename:value|} = () => { var x[||] = y; }; + M(value); } }"); } @@ -1450,8 +1450,8 @@ void Main() { void Main() { - var {|Rename:p|} = new { A = 0 }; - var a = p; + var {|Rename:value|} = new { A = 0 }; + var a = value; } }"); } @@ -1474,8 +1474,8 @@ static void Main(string[] args) static void Main(string[] args) { int[] a = null; - var {|Rename:vs|} = a = new[] { 1, 2, 3 }; - int[] temp = checked(vs); + var {|Rename:ints|} = a = new[] { 1, 2, 3 }; + int[] temp = checked(ints); } }", options: ImplicitTypingEverywhere()); @@ -1516,8 +1516,8 @@ void Main() { void Main() { - var {|Rename:p|} = new { X = 1 }; - WriteLine(p); + var {|Rename:value|} = new { X = 1 }; + WriteLine(value); } }"); } @@ -2567,8 +2567,8 @@ static void Main(string[] args) { Func> f = x => { - Func {|Rename:p|} = y => y + 1; - return p; + Func {|Rename:value|} = y => y + 1; + return value; }; } }"); @@ -2596,8 +2596,8 @@ static void Main(string[] args) { Func> f = x => { - Func {|Rename:p|} = y => x + 1; - return p; + Func {|Rename:value|} = y => x + 1; + return value; }; } }"); @@ -4964,8 +4964,8 @@ public async Task Tuple_TuplesDisabled() var expected = @"class C { - private static readonly (int, string) {|Rename:p|} = (1, ""hello""); - var i = p.ToString(); + private static readonly (int, string) {|Rename:value|} = (1, ""hello""); + var i = value.ToString(); }"; await TestAsync(code, expected, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp6)); @@ -5003,8 +5003,8 @@ public async Task Tuple_IntroduceConstant() var expected = @"class C { - private static readonly (int, string) {|Rename:p|} = (1, ""hello""); - var i = p.ToString(); + private static readonly (int, string) {|Rename:value|} = (1, ""hello""); + var i = value.ToString(); }"; await TestInRegularAndScriptAsync(code, expected); @@ -5022,8 +5022,8 @@ public async Task TupleWithNames_IntroduceConstant() var expected = @"class C { - private static readonly (int a, string b) {|Rename:p|} = (a: 1, b: ""hello""); - var i = p.ToString(); + private static readonly (int a, string b) {|Rename:value|} = (a: 1, b: ""hello""); + var i = value.ToString(); }"; await TestInRegularAndScriptAsync(code, expected); @@ -5041,8 +5041,8 @@ public async Task Tuple_IntroduceConstantForAllOccurrences() var expected = @"class C { - private static readonly (int, string) {|Rename:p|} = (1, ""hello""); - var i = p.ToString() + p.ToString(); + private static readonly (int, string) {|Rename:value|} = (1, ""hello""); + var i = value.ToString() + value.ToString(); }"; await TestInRegularAndScriptAsync(code, expected, index: 1); @@ -5060,8 +5060,8 @@ public async Task TupleWithNames_IntroduceConstantForAllOccurrences() var expected = @"class C { - private static readonly (int a, string b) {|Rename:p|} = (a: 1, b: ""hello""); - var i = p.ToString() + p.ToString(); + private static readonly (int a, string b) {|Rename:value|} = (a: 1, b: ""hello""); + var i = value.ToString() + value.ToString(); }"; await TestInRegularAndScriptAsync(code, expected, index: 1); @@ -5079,8 +5079,8 @@ public async Task TupleWithDifferentNames_IntroduceConstantForAllOccurrences() var expected = @"class C { - private static readonly (int a, string b) {|Rename:p|} = (a: 1, b: ""hello""); - var i = p.ToString() + (c: 1, d: ""hello"").ToString(); + private static readonly (int a, string b) {|Rename:value|} = (a: 1, b: ""hello""); + var i = value.ToString() + (c: 1, d: ""hello"").ToString(); }"; await TestInRegularAndScriptAsync(code, expected, index: 1); @@ -5098,8 +5098,8 @@ public async Task TupleWithOneName_IntroduceConstantForAllOccurrences() var expected = @"class C { - private static readonly (int a, string) {|Rename:p|} = (a: 1, ""hello""); - var i = p.ToString() + p.ToString(); + private static readonly (int a, string) {|Rename:value|} = (a: 1, ""hello""); + var i = value.ToString() + value.ToString(); }"; await TestInRegularAndScriptAsync(code, expected, index: 1); @@ -6074,8 +6074,8 @@ class C byte[] getArray() => null; void test() { - byte[] {|Rename:vs|} = getArray(); - var goo = vs[0]; + byte[] {|Rename:bytes|} = getArray(); + var goo = bytes[0]; } }"); } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index daae31a960b34..66f70c8b796df 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -2375,6 +2375,40 @@ public void Method(IEnumerable $$) await VerifyItemExistsAsync(markup, "results"); } + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableGenericUnexpectedlyNamedParameter() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "args"); + } + + [WorkItem(36364, "https://github.com/dotnet/roslyn/issues/36364")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task EnumerableGenericUnexpectedlyNamedParameterBeginsWithT() + { + var markup = @" +using System.Collections.Generic; + +public class Class1 +{ + public void Method(IEnumerable $$) + { + } +} +"; + await VerifyItemExistsAsync(markup, "types"); + } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] public async Task CustomNamingStyleInsideClass() { diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs index 1932f6c1aa04f..394798278ca77 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs @@ -6,14 +6,12 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; @@ -873,7 +871,7 @@ void Method() Goo(null); } - private void Goo(object p) + private void Goo(object value) { throw new NotImplementedException(); } @@ -4838,7 +4836,7 @@ static void Goo(List x) Bar(() => x); } - private static void Bar(Func> p) + private static void Bar(Func> value) { throw new NotImplementedException(); } @@ -5225,7 +5223,7 @@ void M() M(new { x = 1 }); } - private void M(object p) + private void M(object value) { throw new NotImplementedException(); } @@ -5685,7 +5683,7 @@ static void Main(string[] args) Main(args.Goo()); } - private static void Main(object p) + private static void Main(object value) { throw new NotImplementedException(); } @@ -5717,7 +5715,7 @@ void Baz(string[] args) Baz(() => { return true; }); } - private void Baz(Func p) + private void Baz(Func value) { throw new NotImplementedException(); } @@ -5884,7 +5882,7 @@ void TestMethod(IEnumerable c) new C().TestMethod((a,b) => c.Add) } - private void TestMethod(Func p) + private void TestMethod(Func value) { throw new NotImplementedException(); } @@ -7729,7 +7727,7 @@ void Method() (int, string) d = NewMethod((1, ""hello"")); } - private (int, string) NewMethod((int, string) p) + private (int, string) NewMethod((int, string) value) { throw new NotImplementedException(); } @@ -7756,7 +7754,7 @@ void Method() (int a, string b) d = NewMethod((c: 1, d: ""hello"")); } - private (int a, string b) NewMethod((int c, string d) p) + private (int a, string b) NewMethod((int c, string d) value) { throw new NotImplementedException(); } @@ -7783,7 +7781,7 @@ void Method() (int a, string) d = NewMethod((c: 1, ""hello"")); } - private (int a, string) NewMethod((int c, string) p) + private (int a, string) NewMethod((int c, string) value) { throw new NotImplementedException(); } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs index 56a5df86e2ee6..4cbadb49c6f86 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs @@ -566,11 +566,11 @@ await TestInRegularAndScriptAsync( private class Generated { - private (int, int) p; + private (int, int) value; - public Generated((int, int) p) + public Generated((int, int) value) { - this.p = p; + this.value = value; } } }", @@ -591,11 +591,11 @@ await TestInRegularAndScriptAsync( private class Generated { - private (int a, int b, int) p; + private (int a, int b, int) value; - public Generated((int a, int b, int) p) + public Generated((int a, int b, int) value) { - this.p = p; + this.value = value; } } }", @@ -1579,13 +1579,13 @@ void M(int i, bool b) internal class T { private bool b; - private object p; + private object value; - public T(out int i, ref bool b, object p) + public T(out int i, ref bool b, object value) { i = 0; this.b = b; - this.p = p; + this.value = value; } }", index: 1); @@ -1951,11 +1951,11 @@ string M(int i) internal class T { - private Func p; + private Func value; - public T(Func p) + public T(Func value) { - this.p = p; + this.value = value; } }", index: 1); diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 571d1e4f31a97..6402ca7270580 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -3476,7 +3476,7 @@ void F() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingLambdaParameters, "a", "lambda")); + Diagnostic(RudeEditKind.ChangingLambdaParameters, "a", CSharpFeaturesResources.lambda)); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs b/src/EditorFeatures/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs index 064307be06e10..00e087e404775 100644 --- a/src/EditorFeatures/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs +++ b/src/EditorFeatures/CSharpTest/GenerateConstructor/GenerateConstructorTests.cs @@ -2595,7 +2595,7 @@ enum A [AttributeUsage(AttributeTargets.Class)] class MyAttrAttribute : Attribute { - private int[] vs; + private int[] ints; private A a1; private bool v1; private byte v2; @@ -2607,9 +2607,9 @@ class MyAttrAttribute : Attribute private float v8; private string v9; - public MyAttrAttribute(int[] vs, A a1, bool v1, byte v2, char v3, short v4, int v5, long v6, double v7, float v8, string v9) + public MyAttrAttribute(int[] ints, A a1, bool v1, byte v2, char v3, short v4, int v5, long v6, double v7, float v8, string v9) { - this.vs = vs; + this.ints = ints; this.a1 = a1; this.v1 = v1; this.v2 = v2; @@ -2913,12 +2913,12 @@ void M() }", @"class C { - private (int, string) p; + private (int, string) value; private bool v; - public C((int, string) p, bool v) + public C((int, string) value, bool v) { - this.p = p; + this.value = value; this.v = v; } @@ -2942,11 +2942,11 @@ void M() }", @"class C { - private (int a, string b) p; + private (int a, string b) value; - public C((int a, string b) p) + public C((int a, string b) value) { - this.p = p; + this.value = value; } void M() @@ -2969,11 +2969,11 @@ void M() }", @"class C { - private (int a, string) p; + private (int a, string) value; - public C((int a, string) p) + public C((int a, string) value) { - this.p = p; + this.value = value; } void M() diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs index add879117cc75..63ac15aac3171 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.NameGenerator.cs @@ -17,9 +17,12 @@ internal partial class DeclarationNameCompletionProvider { internal class NameGenerator { + private const char DefaultInterfacePrefix = 'I'; + private const char DefaultGenericParameterPrefix = 'T'; + internal static ImmutableArray GetBaseNames(ITypeSymbol type, bool pluralize) { - var baseName = TryRemoveKnownPrefixes(type, pluralize); + var baseName = TryRemoveKnownPrefixes(type); using var parts = TemporaryArray.Empty; StringBreaker.AddWordParts(baseName, ref parts.AsRef()); var result = GetInterleavedPatterns(parts, baseName, pluralize); @@ -32,7 +35,7 @@ internal static ImmutableArray GetBaseNames(IAliasSymbol alias) var name = alias.Name; if (alias.Target.IsType && ((INamedTypeSymbol)alias.Target).IsInterfaceType() && - CanRemoveInterfacePrefix(name)) + CanRemovePrefix(name, DefaultInterfacePrefix)) { name = name[1..]; } @@ -95,13 +98,13 @@ private static Words GetWords(int start, int end, in TemporaryArray br } //Tries to remove "I" prefix from interfaces and "T" prefix from generic parameter names - private static string TryRemoveKnownPrefixes(ITypeSymbol type, bool considerPuralization) + private static string TryRemoveKnownPrefixes(ITypeSymbol type) { var name = type.Name; - if (type.TypeKind == TypeKind.Interface && name.Length > 1) + if (type.TypeKind == TypeKind.Interface) { - if (CanRemoveInterfacePrefix(name)) + if (CanRemovePrefix(name, DefaultInterfacePrefix)) { return name[1..]; } @@ -109,13 +112,20 @@ private static string TryRemoveKnownPrefixes(ITypeSymbol type, bool considerPura if (type.TypeKind == TypeKind.TypeParameter) { - return name == "T" ? ITypeSymbolExtensions.DefaultParameterName : name[1..].ToCamelCase(); + if (CanRemovePrefix(name, DefaultGenericParameterPrefix)) + { + return name[1..]; + } + else if (name.Length == 1 && name[0] == DefaultGenericParameterPrefix) + { + return ITypeSymbolExtensions.DefaultParameterName; + } } - return type.CreateParameterName(considerPuralization: considerPuralization); + return type.CreateParameterName(); } } - private static bool CanRemoveInterfacePrefix(string name) => name.Length > 1 && name[0] == 'I' && char.IsUpper(name[1]); + private static bool CanRemovePrefix(string name, char prefix) => name.Length > 1 && name[0] == prefix && char.IsUpper(name[1]); } } diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs index 19b88cf222fc2..56e52ff290d3e 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SemanticModelExtensions.cs @@ -17,6 +17,8 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions { internal static partial class SemanticModelExtensions { + private const string DefaultBuildInParameterName = "v"; + public static SemanticMap GetSemanticMap(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) => SemanticMap.From(semanticModel, node, cancellationToken); @@ -188,12 +190,21 @@ public static string GenerateNameFromType(this SemanticModel semanticModel, ITyp // If there's no type argument and we have an array type, we should pluralize, e.g. using 'frogs' for 'new Frog[]' instead of 'frog' if (type.TypeKind == TypeKind.Array && typeArguments.IsEmpty) { - return type.CreateParameterName(capitalize, true).Pluralize(); + return type.CreateParameterName(capitalize).Pluralize(); } // Otherwise assume no pluralization, e.g. using 'immutableArray', 'list', etc. instead of their // plural forms - return type.CreateParameterName(capitalize); + if (type.IsSpecialType() || + type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T || + type.TypeKind == TypeKind.Pointer) + { + return capitalize ? DefaultBuildInParameterName.ToUpper() : DefaultBuildInParameterName; + } + else + { + return type.CreateParameterName(capitalize); + } } private static bool ShouldPluralize(this SemanticModel semanticModel, ITypeSymbol type) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index b876262a8aabd..3eb88267b2464 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -18,7 +18,6 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions internal static partial class ITypeSymbolExtensions { public const string DefaultParameterName = "value"; - private const string DefaultBuiltInParameterName = "v"; public static bool IsIntegralType([NotNullWhen(returnValue: true)] this ITypeSymbol? type) => type?.SpecialType.IsIntegralType() == true; @@ -313,13 +312,13 @@ public static Accessibility DetermineMinimalAccessibility(this ITypeSymbol typeS public static bool ContainsAnonymousType([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol) { - switch (symbol) + return symbol switch { - case IArrayTypeSymbol a: return ContainsAnonymousType(a.ElementType); - case IPointerTypeSymbol p: return ContainsAnonymousType(p.PointedAtType); - case INamedTypeSymbol n: return ContainsAnonymousType(n); - default: return false; - } + IArrayTypeSymbol a => ContainsAnonymousType(a.ElementType), + IPointerTypeSymbol p => ContainsAnonymousType(p.PointedAtType), + INamedTypeSymbol n => ContainsAnonymousType(n), + _ => false, + }; } private static bool ContainsAnonymousType(INamedTypeSymbol type) @@ -336,7 +335,7 @@ private static bool ContainsAnonymousType(INamedTypeSymbol type) return false; } - public static string CreateParameterName(this ITypeSymbol type, bool capitalize = false, bool considerPuralization = false) + public static string CreateParameterName(this ITypeSymbol type, bool capitalize = false) { while (true) { @@ -353,22 +352,17 @@ public static string CreateParameterName(this ITypeSymbol type, bool capitalize break; } - var shortName = GetParameterName(type, considerPuralization); + var shortName = GetParameterName(type); return capitalize ? shortName.ToPascalCase() : shortName.ToCamelCase(); } - private static string GetParameterName(ITypeSymbol? type, bool considerPuralization) + private static string GetParameterName(ITypeSymbol? type) { if (type == null || type.IsAnonymousType() || type.IsTupleType) { return DefaultParameterName; } - if (!considerPuralization && type.IsSpecialType()) - { - return DefaultBuiltInParameterName; - } - var shortName = type.GetShortName(); return shortName.Length == 0 ? DefaultParameterName From 41513895cadaee13048e2ae4968b686023cef4fa Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Tue, 22 Feb 2022 20:38:49 +0300 Subject: [PATCH 124/187] Undo some VS refactoring --- .../Compiler/Core/Extensions/ITypeSymbolExtensions.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index 3eb88267b2464..393ffc5535d1c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -41,7 +41,8 @@ public static IList GetAllInterfacesIncludingThis(this ITypeSy var allInterfaces = type.AllInterfaces; if (type is INamedTypeSymbol namedType && namedType.TypeKind == TypeKind.Interface && !allInterfaces.Contains(namedType)) { - var result = new List(allInterfaces.Length + 1) { namedType }; + var result = new List(allInterfaces.Length + 1); + result.Add(namedType); result.AddRange(allInterfaces); return result; } @@ -557,8 +558,9 @@ private static IEnumerable SelectAccessibleMembers(this IEnumerable SelectAccessibleMembers(this IEnumerable Date: Fri, 18 Feb 2022 14:17:49 -0800 Subject: [PATCH 125/187] Correctly apply anchor indentation adjustments in the presence of trivia Fixes #57465 --- ...tSwitchStatementToExpressionFixAllTests.cs | 12 ++--- .../ConvertForEachToLinqQueryTests.cs | 2 +- .../ReplaceMethodWithPropertyTests.cs | 2 +- .../Formatting/FormattingEngineTests.cs | 54 +++++++++++++++++-- .../Formatting/CoreFormatterTestsBase.cs | 10 ++-- .../ReplacePropertyWithMethodsTests.vb | 4 +- .../CSharpTest/Formatting/FormattingTests.cs | 4 +- .../Engine/Trivia/CSharpTriviaFormatter.cs | 5 +- .../Context/FormattingContext.AnchorData.cs | 7 +-- .../Formatting/Context/FormattingContext.cs | 5 +- .../Operations/AnchorIndentationOperation.cs | 6 +-- .../Rules/Operations/FormattingOperations.cs | 10 ++-- .../Formatting/FormattingTests.vb | 2 +- 13 files changed, 88 insertions(+), 35 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionFixAllTests.cs b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionFixAllTests.cs index be82952deb667..2adc585610f97 100644 --- a/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionFixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionFixAllTests.cs @@ -194,12 +194,12 @@ System.Func M(int i, int j) // 1 // 2 _ => () => - { - return j switch - { - _ => 3, - }; - } + { + return j switch + { + _ => 3, + }; + } , }; diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs index a86fa1a4029e1..ab5c7aee6e1ea 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ConvertLinq/ConvertForEachToLinqQueryTests.cs @@ -4324,7 +4324,7 @@ void M(IEnumerable nums) /* 8*/// 9 /* 10 */ where/* 11 *//* 12 */n1 /* 13 */ > /* 14 */ 0/* 15 */// 16 - select n1/* 4 *//* 21 */// 22 + select n1/* 4 *//* 21 */// 22 /*23*//*24*/ ) { diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs index 91eb93138bfea..c716feadef23c 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ReplaceMethodWithProperty/ReplaceMethodWithPropertyTests.cs @@ -1937,7 +1937,7 @@ int Goo get { throw e + - e; + e; } } }", options: new OptionsCollection(GetLanguage()) diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index 1db1098022187..f021e8c203279 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -8,11 +8,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.BraceCompletion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.Implementation.Formatting; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; @@ -1585,6 +1583,56 @@ public void FormatArbitraryNodeParenthesizedLambdaExpression() AssertFormatOnArbitraryNode(node, expected); } + [WpfFact] + [WorkItem(57465, "https://github.com/dotnet/roslyn/issues/57465")] + public async Task FormatLambdaWithDirective() + { + var code = @"namespace N +{ + public class C + { + protected void Render() + { + if (outer) + { + M(() => + { +#nullable enable + if (inner) + { + } + } + ); + } + } + } +} +"; + var expected = @"namespace N +{ + public class C + { + protected void Render() + { + if (outer) + { + M(() => + { +#nullable enable + if (inner) + { + } + } + ); + } + } + } +} +"; + + await AssertFormatAsync(expected, code, spans: null); + } + [WorkItem(30787, "https://github.com/dotnet/roslyn/issues/30787")] [WpfFact] public void DoSmartIndentOpenBraceEvenWithFormatWhileTypingOff1() @@ -2246,7 +2294,7 @@ private static void AssertFormatAfterTypeChar(string code, string expected, Dict var newSnapshot = subjectDocument.GetTextBuffer().CurrentSnapshot; - Assert.Equal(expected, newSnapshot.GetText()); + AssertEx.EqualOrDiff(expected, newSnapshot.GetText()); } } } diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index b905addc182d9..7a7eb4a5d95fb 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -5,6 +5,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,6 +19,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; @@ -176,7 +178,7 @@ protected async Task AssertFormatAsync(string expected, string code, IEnumerable { var factory = (TestFormattingRuleFactoryServiceFactory.Factory)formattingRuleProvider; factory.BaseIndentation = baseIndentation.Value; - factory.TextSpan = spans.First(); + factory.TextSpan = spans?.First() ?? syntaxTree.GetRoot(CancellationToken.None).FullSpan; } var optionSet = workspace.Options; @@ -220,7 +222,7 @@ internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattin if (actual != expected) { _output.WriteLine(actual); - Assert.Equal(expected, actual); + AssertEx.EqualOrDiff(expected, actual); } } @@ -264,12 +266,12 @@ protected async Task AssertFormatWithBaseIndentAsync( string expected, string markupCode, int baseIndentation, Dictionary options = null) { - MarkupTestFile.GetSpan(markupCode, out var code, out var span); + TestFileMarkupParser.GetSpans(markupCode, out var code, out ImmutableArray spans); await AssertFormatAsync( expected, code, - new List { span }, + spans, changedOptionSet: options, baseIndentation: baseIndentation); } diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb index 985f3fb7a7908..117e73e1a6016 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb @@ -45,7 +45,7 @@ end class", "class C Public Function GetProp() As Integer return _ -0 + 0 End Function end class") End Function @@ -64,7 +64,7 @@ end class", "class C Public Function GetProp() As Integer return _ ' Test -0 + 0 End Function end class") End Function diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index 55e783f57cd84..39245c983caeb 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -2986,8 +2986,8 @@ await AssertFormatAsync(@"class C int Method() { var q = from c in Get(1 + - 2 + - 3) + 2 + + 3) from b in Get(1 + 2 + 3) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs index 50507e48f85ac..d2f26ecb7760b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs @@ -78,7 +78,10 @@ protected override LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, if (insertNewLine) { - return LineColumnRule.PreserveLinesWithDefaultIndentation(lines: 0); + if (existingWhitespaceBetween.Spaces != this.Spaces) + return LineColumnRule.PreserveWithGivenSpaces(spaces: this.Spaces); + else + return LineColumnRule.PreserveLinesWithDefaultIndentation(lines: 0); } if (existingWhitespaceBetween.Lines > 0 && existingWhitespaceBetween.Spaces != this.Spaces) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs index a89fb23292cfd..01de97733daff 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.AnchorData.cs @@ -17,20 +17,21 @@ private class AnchorData { private readonly AnchorIndentationOperation _operation; - public AnchorData(AnchorIndentationOperation operation, int originalColumn) + public AnchorData(AnchorIndentationOperation operation, SyntaxToken anchorToken, int originalColumn) { _operation = operation; + this.AnchorToken = anchorToken; this.OriginalColumn = originalColumn; } public TextSpan TextSpan => _operation.TextSpan; - public SyntaxToken AnchorToken => _operation.AnchorToken; - public SyntaxToken StartToken => _operation.StartToken; public SyntaxToken EndToken => _operation.EndToken; + public SyntaxToken AnchorToken { get; } + public int OriginalColumn { get; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs index b3546b76d42e2..6eb4e32ea9963 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs @@ -403,8 +403,9 @@ public void AddAnchorIndentationOperation(AnchorIndentationOperation operation) return; } - var originalSpace = _tokenStream.GetOriginalColumn(operation.StartToken); - var data = new AnchorData(operation, originalSpace); + var anchorToken = _tokenStream.FirstTokenOfBaseTokenLine(operation.AnchorToken); + var originalSpace = _tokenStream.GetOriginalColumn(anchorToken); + var data = new AnchorData(operation, anchorToken, originalSpace); _anchorTree.AddIntervalInPlace(data); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs index adcfb981e18f6..8175b0dad2b0e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs @@ -13,25 +13,23 @@ namespace Microsoft.CodeAnalysis.Formatting.Rules /// internal sealed class AnchorIndentationOperation { - internal AnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken startToken, SyntaxToken endToken, TextSpan textSpan) + internal AnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken endToken, TextSpan textSpan) { Contract.ThrowIfTrue(anchorToken.RawKind == 0); Contract.ThrowIfTrue(textSpan.Start < 0 || textSpan.Length < 0); - Contract.ThrowIfTrue(startToken.RawKind == 0); Contract.ThrowIfTrue(endToken.RawKind == 0); this.AnchorToken = anchorToken; this.TextSpan = textSpan; - this.StartToken = startToken; this.EndToken = endToken; } public SyntaxToken AnchorToken { get; } public TextSpan TextSpan { get; } - public SyntaxToken StartToken { get; } + public SyntaxToken StartToken => AnchorToken; public SyntaxToken EndToken { get; } } } 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 c3976ca379f8b..bc025f6b723b8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs @@ -25,16 +25,16 @@ internal static class FormattingOperations /// /// create anchor indentation region around start and end token - /// start token will act as anchor token and right after anchor token to end of end token will become anchor region + /// right after anchor token to end of end token will become anchor region /// - public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken startToken, SyntaxToken endToken) - => CreateAnchorIndentationOperation(startToken, startToken, endToken, TextSpan.FromBounds(startToken.Span.End, endToken.Span.End)); + public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken endToken) + => CreateAnchorIndentationOperation(anchorToken, endToken, TextSpan.FromBounds(anchorToken.Span.End, endToken.Span.End)); /// /// create anchor indentation region more explicitly by providing all necessary information. /// - public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken startToken, SyntaxToken endToken, TextSpan textSpan) - => new(anchorToken, startToken, endToken, textSpan); + public static AnchorIndentationOperation CreateAnchorIndentationOperation(SyntaxToken anchorToken, SyntaxToken endToken, TextSpan textSpan) + => new(anchorToken, endToken, textSpan); /// /// create suppress region around start and end token diff --git a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb index b6125179e2998..d90d6b0171bf2 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb @@ -491,7 +491,7 @@ End Class Dim expected = Class C Sub Method() Dim a = From q In - {1, 3, 5} + {1, 3, 5} Where q > 10 Select q End Sub From e87c20bffafee1c464dca29ec0d661fb4daf7610 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 22 Feb 2022 11:47:51 -0800 Subject: [PATCH 126/187] Add coverage for additional anchor scenarios --- .../Formatting/FormattingTestBase.cs | 44 ++++++++++++++-- .../Formatting/FormattingTests.vb | 50 ++++++++++++++++++- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs index 8d7d718b351a7..c19aca7599943 100644 --- a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs +++ b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs @@ -3,12 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; @@ -67,7 +67,7 @@ private protected async Task AssertFormatAsync( var root = await syntaxTree.GetRootAsync(); var options = SyntaxFormattingOptions.Create(optionSet, workspace.Services, root.Language); - AssertFormat(workspace.Services, expected, root, spans, options, await document.GetTextAsync()); + await AssertFormatAsync(workspace.Services, expected, root, spans.AsImmutable(), options, await document.GetTextAsync()); // format with node and transform AssertFormatWithTransformation(workspace.Services, expected, root, spans, options, treeCompare, parseOptions); @@ -93,10 +93,48 @@ internal void AssertFormatWithTransformation( } } - internal static void AssertFormat(HostWorkspaceServices services, string expected, SyntaxNode root, IEnumerable spans, SyntaxFormattingOptions options, SourceText sourceText) + private static async Task AssertFormatAsync(HostWorkspaceServices services, string expected, SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, SourceText sourceText) { + // Verify formatting the input code produces the expected result var result = Formatter.GetFormattedTextChanges(root, spans, services, options); AssertResult(expected, sourceText, result); + + // Verify formatting the output code produces itself (formatting is idempotent) + var resultText = sourceText.WithChanges(result); + if (TryAdjustSpans(sourceText, result, resultText, spans, out var adjustedSpans)) + { + var resultRoot = await root.SyntaxTree.WithChangedText(resultText).GetRootAsync(); + var idempotentResult = Formatter.GetFormattedTextChanges(resultRoot, adjustedSpans, services, options); + AssertResult(expected, resultText, idempotentResult); + } + } + + private static bool TryAdjustSpans(SourceText inputText, IList changes, SourceText outputText, ImmutableArray inputSpans, out ImmutableArray outputSpans) + { + if (changes.Count == 0) + { + outputSpans = inputSpans; + return true; + } + + var outputBuilder = ImmutableArray.CreateBuilder(inputSpans.Length); + for (var i = 0; i < inputSpans.Length; i++) + { + var span = inputSpans[i]; + if (span.Start == 0 && span.End == inputText.Length) + { + // The input span is the full document + outputBuilder.Add(TextSpan.FromBounds(0, outputText.Length)); + continue; + } + + // The input span cannot be automatically adjusted + outputSpans = default; + return false; + } + + outputSpans = outputBuilder.MoveToImmutable(); + return true; } protected static void AssertResult(string expected, SourceText sourceText, IList result) diff --git a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb index d90d6b0171bf2..a92283e2be319 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb @@ -478,7 +478,7 @@ Imports System. End Function - Public Async Function AnchorQueryStatement() As Task + Public Async Function AnchorQueryStatement1() As Task Dim code = Class C Sub Method() Dim a = From q In @@ -500,6 +500,54 @@ End Class Await AssertFormatLf2CrLfAsync(code.Value, expected.Value) End Function + + Public Async Function AnchorQueryStatement2() As Task + Dim code = Class C + Sub Method() + Dim a = From q In + {1, 3, 5} + Where q > 10 + Select q + End Sub +End Class + + Dim expected = Class C + Sub Method() + Dim a = From q In + {1, 3, 5} + Where q > 10 + Select q + End Sub +End Class + + Await AssertFormatLf2CrLfAsync(code.Value, expected.Value) + End Function + + + Public Async Function AnchorQueryStatement3() As Task + Dim code = Class C + Sub Method() + Dim a = + From q In + {1, 3, 5} + Where q > 10 + Select q + End Sub +End Class + + Dim expected = Class C + Sub Method() + Dim a = + From q In + {1, 3, 5} + Where q > 10 + Select q + End Sub +End Class + + Await AssertFormatLf2CrLfAsync(code.Value, expected.Value) + End Function + Public Async Function AlignQueryStatement() As Task Dim code = Class C From 24095a58adff0244cb4f45d27cb9f5ca27ce2d77 Mon Sep 17 00:00:00 2001 From: DoctorKrolic Date: Tue, 22 Feb 2022 22:58:15 +0300 Subject: [PATCH 127/187] Fixed VB tests --- .../AddAnonymousTypeMemberNameTests.vb | 6 ++-- .../AddParameter/AddParameterTests.vb | 2 +- .../IntroduceVariableTests.vb | 24 ++++++------- .../GenerateMethod/GenerateMethodTests.vb | 34 +++++++++---------- .../GenerateType/GenerateTypeTests.vb | 28 +++++++-------- .../GenerateConstructorTests.vb | 24 ++++++------- 6 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.vb b/src/EditorFeatures/VisualBasicTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.vb index af53d3aed9f1f..5487d34766623 100644 --- a/src/EditorFeatures/VisualBasicTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.vb +++ b/src/EditorFeatures/VisualBasicTest/AddAnonymousTypeMemberName/AddAnonymousTypeMemberNameTests.vb @@ -78,7 +78,7 @@ end class", " class C sub M() - dim v = new with {.P = new with {.V = me.Equals(1), .V1 = me.ToString() + 1}} + dim v = new with {.Value = new with {.V = me.Equals(1), .V1 = me.ToString() + 1}} end sub end class") End Function @@ -99,7 +99,7 @@ class C { sub M() { - dim v = new with {.P = new with {.V = me.Equals(1), .V1 = me.ToString() + 1}} + dim v = new with {.Value = new with {.V = me.Equals(1), .V1 = me.ToString() + 1}} } end class") End Function @@ -116,7 +116,7 @@ end class", " class C sub M() - dim v = new with {.P = new with {.V = me.Equals(1), .V1 = me.Equals(2)}} + dim v = new with {.Value = new with {.V = me.Equals(1), .V1 = me.Equals(2)}} end sub end class") End Function diff --git a/src/EditorFeatures/VisualBasicTest/AddParameter/AddParameterTests.vb b/src/EditorFeatures/VisualBasicTest/AddParameter/AddParameterTests.vb index 00fa3a27046fd..a92a80bfc7226 100644 --- a/src/EditorFeatures/VisualBasicTest/AddParameter/AddParameterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/AddParameter/AddParameterTests.vb @@ -715,7 +715,7 @@ End Class" Dim fix = " Friend Class C1 - Private Sub M1(t1 As (Integer, Integer), p As (Integer, String)) + Private Sub M1(t1 As (Integer, Integer), value As (Integer, String)) Me.M1((1,1), (1,""1"")) End Sub End Class" diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb index 697bd8ec5bc68..1ee3d6664342a 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/IntroduceVariable/IntroduceVariableTests.vb @@ -1028,10 +1028,10 @@ Module Program Dim q As Object If True Then - Dim {|Rename:p|} As Object = Sub() - End Sub + Dim {|Rename:value|} As Object = Sub() + End Sub - q = p + q = value End If End Sub End Module") @@ -1634,8 +1634,8 @@ End Class") End Module", "Module Program Sub Main() - Dim {|Rename:vs|} As Integer() = New Integer() {} - Return vs + Dim {|Rename:integers|} As Integer() = New Integer() {} + Return integers End Sub End Module") End Function @@ -1953,8 +1953,8 @@ End Class", Class C Shared Sub Main() - Dim {|Rename:vs|} As Integer() = New C().Goo() - Dim x = vs(0) + Dim {|Rename:integers|} As Integer() = New C().Goo() + Dim x = integers(0) End Sub Function Goo() As Integer() End Function @@ -2158,10 +2158,10 @@ End Module " Module Program Sub Main() - Dim {|Rename:p|} = Sub() - End Sub + Dim {|Rename:value|} = Sub() + End Sub - Dim x = Function() p + Dim x = Function() value End Sub End Module " @@ -2709,8 +2709,8 @@ End Class " Imports System Class C - Private Shared ReadOnly {|Rename:p|} As Integer() = {90, 73} - Property Grades As Integer() = p + Private Shared ReadOnly {|Rename:value|} As Integer() = {90, 73} + Property Grades As Integer() = value End Class " Await TestInRegularAndScriptAsync(code, expected) diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb index eea20a4c03ae9..f9ccea29d2e64 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb @@ -1903,7 +1903,7 @@ Module Program goo(,,) End Sub - Private Sub goo(Optional p1 As Object = Nothing, Optional p2 As Object = Nothing, Optional p3 As Object = Nothing) + Private Sub goo(Optional value1 As Object = Nothing, Optional value2 As Object = Nothing, Optional value3 As Object = Nothing) Throw New NotImplementedException() End Sub End Module") @@ -1925,7 +1925,7 @@ Module Program goo(1,,) End Sub - Private Sub goo(v As Integer, Optional p1 As Object = Nothing, Optional p2 As Object = Nothing) + Private Sub goo(v As Integer, Optional value1 As Object = Nothing, Optional value2 As Object = Nothing) Throw New NotImplementedException() End Sub End Module") @@ -1947,7 +1947,7 @@ Module Program goo(, 1,) End Sub - Private Sub goo(Optional p1 As Object = Nothing, Optional v As Integer = Nothing, Optional p2 As Object = Nothing) + Private Sub goo(Optional value1 As Object = Nothing, Optional v As Integer = Nothing, Optional value2 As Object = Nothing) Throw New NotImplementedException() End Sub End Module") @@ -1969,7 +1969,7 @@ Module Program goo(,, 1) End Sub - Private Sub goo(Optional p1 As Object = Nothing, Optional p2 As Object = Nothing, Optional v As Integer = Nothing) + Private Sub goo(Optional value1 As Object = Nothing, Optional value2 As Object = Nothing, Optional v As Integer = Nothing) Throw New NotImplementedException() End Sub End Module") @@ -1991,7 +1991,7 @@ Module Program goo(1,, 1) End Sub - Private Sub goo(v1 As Integer, Optional p As Object = Nothing, Optional v2 As Integer = Nothing) + Private Sub goo(v1 As Integer, Optional value As Object = Nothing, Optional v2 As Integer = Nothing) Throw New NotImplementedException() End Sub End Module") @@ -2013,7 +2013,7 @@ Module Program goo(1, 1, ) End Sub - Private Sub goo(v1 As Integer, v2 As Integer, Optional p As Object = Nothing) + Private Sub goo(v1 As Integer, v2 As Integer, Optional value As Object = Nothing) Throw New NotImplementedException() End Sub End Module") @@ -2078,7 +2078,7 @@ Module M Bar(x, Function() y) ' Generate Bar End Sub - Private Sub Bar(Of T, S)(x As List(Of T), p As Func(Of List(Of S))) + Private Sub Bar(Of T, S)(x As List(Of T), value As Func(Of List(Of S))) Throw New NotImplementedException() End Sub End Module") @@ -2151,7 +2151,7 @@ Module Program Bar(1, {1}) End Sub - Private Sub Bar(v As Integer, p() As Integer) + Private Sub Bar(v As Integer, value() As Integer) Throw New NotImplementedException() End Sub End Module") @@ -2173,7 +2173,7 @@ Module M Goo({{1}}) End Sub - Private Sub Goo(p(,) As Integer) + Private Sub Goo(value(,) As Integer) Throw New NotImplementedException() End Sub End Module") @@ -2257,7 +2257,7 @@ Module Program End Function) End Sub - Private Sub Baz(p As Func(Of String)) + Private Sub Baz(value As Func(Of String)) Throw New NotImplementedException() End Sub @@ -2295,7 +2295,7 @@ Module Program End Function) End Sub - Private Sub Baz(p As Func(Of String)) + Private Sub Baz(value As Func(Of String)) Throw New NotImplementedException() End Sub @@ -2333,7 +2333,7 @@ Module Program End Function) End Sub - Private Sub Baz(p As Func(Of String)) + Private Sub Baz(value As Func(Of String)) Throw New NotImplementedException() End Sub @@ -2635,7 +2635,7 @@ Class M1 sub1(Of Integer, String)(New Integer() {1, 2, 3}, New String() {"a", "b"}) End Sub - Private Sub sub1(Of T1, T2)(vs1() As T1, vs2() As T2) + Private Sub sub1(Of T1, T2)(integers() As T1, strings() As T2) Throw New NotImplementedException() End Sub End Class @@ -3249,7 +3249,7 @@ Class C M2(Function() NameOf(M)) End Sub - Private Sub M2(p As Func(Of String)) + Private Sub M2(value As Func(Of String)) Throw New NotImplementedException() End Sub End Class @@ -4109,7 +4109,7 @@ Class Program Dim d As (Integer, String) = NewMethod((1, ""hello"")) End Sub - Private Shared Function NewMethod(p As (Integer, String)) As (Integer, String) + Private Shared Function NewMethod(value As (Integer, String)) As (Integer, String) Throw New NotImplementedException() End Function End Class") @@ -4186,7 +4186,7 @@ Class Program Dim d As (a As Integer, b As String) = NewMethod((c:=1, d:=""hello"")) End Sub - Private Shared Function NewMethod(p As (c As Integer, d As String)) As (a As Integer, b As String) + Private Shared Function NewMethod(value As (c As Integer, d As String)) As (a As Integer, b As String) Throw New NotImplementedException() End Function End Class") @@ -4207,7 +4207,7 @@ Class Program Dim d As (a As Integer, String) = NewMethod((c:=1, ""hello"")) End Sub - Private Shared Function NewMethod(p As (c As Integer, String)) As (a As Integer, String) + Private Shared Function NewMethod(value As (c As Integer, String)) As (a As Integer, String) Throw New NotImplementedException() End Function End Class") diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb index a06531070b2bc..75234dd27cf34 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb @@ -155,10 +155,10 @@ End Class", Dim f = New Generated((1, 2)) Private Class Generated - Private p As (Integer, Integer) + Private value As (Integer, Integer) - Public Sub New(p As (Integer, Integer)) - Me.p = p + Public Sub New(value As (Integer, Integer)) + Me.value = value End Sub End Class End Class", @@ -175,10 +175,10 @@ End Class", Dim f = New Generated((a:=1, b:=2, 3)) Private Class Generated - Private p As (a As Integer, b As Integer, Integer) + Private value As (a As Integer, b As Integer, Integer) - Public Sub New(p As (a As Integer, b As Integer, Integer)) - Me.p = p + Public Sub New(value As (a As Integer, b As Integer, Integer)) + Me.value = value End Sub End Class End Class", @@ -1317,10 +1317,10 @@ Module Program End Module Friend Class C - Private p As Func(Of Object) + Private value As Func(Of Object) - Public Sub New(p As Func(Of Object)) - Me.p = p + Public Sub New(value As Func(Of Object)) + Me.value = value End Sub End Class ", @@ -1345,12 +1345,12 @@ Module Program End Module Friend Class C - Private p1 As Object - Private p2 As Object + Private value1 As Object + Private value2 As Object - Public Sub New(p1 As Object, p2 As Object) - Me.p1 = p1 - Me.p2 = p2 + Public Sub New(value1 As Object, value2 As Object) + Me.value1 = value1 + Me.value2 = value2 End Sub End Class ", diff --git a/src/EditorFeatures/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb b/src/EditorFeatures/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb index ed67ca1646282..18b3b73900180 100644 --- a/src/EditorFeatures/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb +++ b/src/EditorFeatures/VisualBasicTest/GenerateConstructor/GenerateConstructorTests.vb @@ -799,10 +799,10 @@ End Class", End Sub End Class Class A - Private p As Object + Private value As Object - Public Sub New(p As Object) - Me.p = p + Public Sub New(value As Object) + Me.value = value End Sub End Class") End Function @@ -1434,7 +1434,7 @@ End Enum Public Class MyAttribute Inherits System.Attribute - Private vs As Short() + Private shorts As Short() Private a1 As A Private v1 As Boolean Private v2 As Integer @@ -1446,8 +1446,8 @@ Public Class MyAttribute Private v8 As Single Private v9 As String - Public Sub New(vs() As Short, a1 As A, v1 As Boolean, v2 As Integer, v3 As Char, v4 As Short, v5 As Integer, v6 As Long, v7 As Double, v8 As Single, v9 As String) - Me.vs = vs + Public Sub New(shorts() As Short, a1 As A, v1 As Boolean, v2 As Integer, v3 As Char, v4 As Short, v5 As Integer, v6 As Long, v7 As Double, v8 As Single, v9 As String) + Me.shorts = shorts Me.a1 = a1 Me.v1 = v1 Me.v2 = v2 @@ -2283,15 +2283,15 @@ End Class ", "Class C Private _a As Integer - Private p As Object + Private value As Object Public Sub New(Optional a As Integer = 1) Me._a = a End Sub - Public Sub New(Optional a As Integer = 1, Optional p As Object = Nothing) + Public Sub New(Optional a As Integer = 1, Optional value As Object = Nothing) Me.New(a) - Me.p = p + Me.value = value End Sub Public Function M() As C @@ -2320,15 +2320,15 @@ End Class ", "Class C Private _a As Integer - Private p As Object + Private value As Object Public Sub New(Optional a As Integer = 1) Me._a = a End Sub - Public Sub New(Optional a As Integer = 1, Optional p As Object = Nothing) + Public Sub New(Optional a As Integer = 1, Optional value As Object = Nothing) Me.New(a) - Me.p = p + Me.value = value End Sub Public Function M() As C From e22033533ffa8259a927e36aeff52f236b285cd4 Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Tue, 22 Feb 2022 22:16:21 +0200 Subject: [PATCH 128/187] Enable CA2009: Do not call ToImmutableCollection on an ImmutableCollection value (#53141) --- eng/config/globalconfigs/Common.globalconfig | 1 + .../QuickInfo/IntellisenseQuickInfoBuilder.cs | 2 +- .../Test2/NavigationBar/TestHelpers.vb | 2 +- .../OnAutoInsert/OnAutoInsertHandler.cs | 2 +- .../OnAutoInsert/OnAutoInsertTests.cs | 39 ++++++++++++++++++- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/eng/config/globalconfigs/Common.globalconfig b/eng/config/globalconfigs/Common.globalconfig index 7dc808dcb31fd..b3339efbab8ce 100644 --- a/eng/config/globalconfigs/Common.globalconfig +++ b/eng/config/globalconfigs/Common.globalconfig @@ -4,6 +4,7 @@ dotnet_diagnostic.CA1067.severity = warning dotnet_diagnostic.CA1068.severity = warning dotnet_diagnostic.CA1200.severity = warning dotnet_diagnostic.CA1821.severity = warning +dotnet_diagnostic.CA2009.severity = warning # CA2012: Use ValueTasks correctly dotnet_diagnostic.CA2012.severity = warning diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs index 6a4ae8f5c86ed..831f02921d1fb 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/QuickInfo/IntellisenseQuickInfoBuilder.cs @@ -111,7 +111,7 @@ private static async Task BuildInteractiveContentAsync( var tabSize = document.Project.Solution.Options.GetOption(FormattingOptions.TabSize, document.Project.Language); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var spans = IndentationHelper.GetSpansWithAlignedIndentation(text, classifiedSpans.ToImmutableArray(), tabSize); + var spans = IndentationHelper.GetSpansWithAlignedIndentation(text, classifiedSpans, tabSize); var textRunsOfSpan = spans.Select(s => new ClassifiedTextRun(s.ClassificationType, text.GetSubText(s.TextSpan).ToString(), ClassifiedTextRunStyle.UseClassificationFont)).ToList(); if (textRunsOfSpan.Count > 0) { diff --git a/src/EditorFeatures/Test2/NavigationBar/TestHelpers.vb b/src/EditorFeatures/Test2/NavigationBar/TestHelpers.vb index a0be1eae9843f..635123add3a7d 100644 --- a/src/EditorFeatures/Test2/NavigationBar/TestHelpers.vb +++ b/src/EditorFeatures/Test2/NavigationBar/TestHelpers.vb @@ -59,7 +59,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.NavigationBar Dim items = Await service.GetItemsAsync(document, snapshot.Version, Nothing) Dim hostDocument = workspace.Documents.Single(Function(d) d.CursorPosition.HasValue) - Dim model As New NavigationBarModel(service, items.ToImmutableArray()) + Dim model As New NavigationBarModel(service, items) Dim selectedItems = NavigationBarController.ComputeSelectedTypeAndMember(model, New SnapshotPoint(hostDocument.GetTextBuffer().CurrentSnapshot, hostDocument.CursorPosition.Value), Nothing) Dim isCaseSensitive = document.GetLanguageService(Of ISyntaxFactsService)().IsCaseSensitive diff --git a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 78655e0c7274c..3a9b8d4488c5a 100644 --- a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -41,7 +41,7 @@ public OnAutoInsertHandler( [ImportMany(LanguageNames.VisualBasic)] IEnumerable visualBasicBraceCompletionServices) { _csharpBraceCompletionServices = csharpBraceCompletionServices.ToImmutableArray(); - _visualBasicBraceCompletionServices = _visualBasicBraceCompletionServices.ToImmutableArray(); + _visualBasicBraceCompletionServices = visualBasicBraceCompletionServices.ToImmutableArray(); } public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.VSInternalDocumentOnAutoInsertParams request) => request.TextDocument; diff --git a/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs index b1e29203a2d43..d8ef4fb7bdf84 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.LanguageServer.Protocol; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; @@ -39,6 +40,26 @@ void M() await VerifyMarkupAndExpected("/", markup, expected); } + [Fact] + public async Task OnAutoInsert_CommentCharacter_VB() + { + var markup = +@"Class A + '''{|type:|} + Sub M() + End Sub +End Class"; + var expected = +@"Class A + ''' + ''' $0 + ''' + Sub M() + End Sub +End Class"; + await VerifyMarkupAndExpected("'", markup, expected, languageName: LanguageNames.VisualBasic); + } + [Fact] public async Task OnAutoInsert_ParametersAndReturns() { @@ -304,9 +325,23 @@ void M() await VerifyNoResult("\n", markup); } - private async Task VerifyMarkupAndExpected(string characterTyped, string markup, string expected, bool insertSpaces = true, int tabSize = 4) + private async Task VerifyMarkupAndExpected(string characterTyped, string markup, string expected, bool insertSpaces = true, int tabSize = 4, string languageName = LanguageNames.CSharp) { - using var testLspServer = await CreateTestLspServerAsync(markup); + Task testLspServerTask; + if (languageName == LanguageNames.CSharp) + { + testLspServerTask = CreateTestLspServerAsync(markup); + } + else if (languageName == LanguageNames.VisualBasic) + { + testLspServerTask = CreateVisualBasicTestLspServerAsync(markup); + } + else + { + throw ExceptionUtilities.UnexpectedValue(languageName); + } + + using var testLspServer = await testLspServerTask; var locationTyped = testLspServer.GetLocations("type").Single(); var document = testLspServer.GetCurrentSolution().GetDocuments(locationTyped.Uri).Single(); From 6bc46bb9d868e54b8ab0b68bd887f17953961237 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 22 Feb 2022 14:23:58 -0800 Subject: [PATCH 129/187] Document anchor token application --- .../Core/Formatting/Context/FormattingContext.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs index 6eb4e32ea9963..ea4d4eaba2d9d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs @@ -403,6 +403,18 @@ public void AddAnchorIndentationOperation(AnchorIndentationOperation operation) return; } + // If the indentation changes on a line which other code is anchored to, adjust those other lines to reflect + // the same change in indentation. Note that we anchor to the first token on a line to account for common + // cases like the following code, where the `{` token is anchored to the `(` token of `()`: + // + // ↓ this space can be removed, which moves `(` one character to the left + // var x = Method( () => + // { + // ↑ this `{` anchors to `var` instead of `(`, which prevents it from moving when `(` is moved + // }); + // + // The calculation of true anchor token (which is always the first token on a line) is delayed to account + // for cases where the original anchor token is moved to a new line during a formatting operation. var anchorToken = _tokenStream.FirstTokenOfBaseTokenLine(operation.AnchorToken); var originalSpace = _tokenStream.GetOriginalColumn(anchorToken); var data = new AnchorData(operation, anchorToken, originalSpace); From f0d6642d275f62f7aa91c5afc048c1d07aa567f7 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Tue, 22 Feb 2022 14:53:45 -0800 Subject: [PATCH 130/187] Add telemetry at async completion layer --- .../AsyncCompletion/AsyncCompletionLogger.cs | 40 +++ .../AsyncCompletion/CompletionSource.cs | 294 +++++++++--------- .../AsyncCompletion/ItemManager.cs | 92 +++--- 3 files changed, 246 insertions(+), 180 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/AsyncCompletionLogger.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/AsyncCompletionLogger.cs index ad7f46be609f4..995dc06fb4039 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/AsyncCompletionLogger.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/AsyncCompletionLogger.cs @@ -46,6 +46,14 @@ private enum ActionInfo CommitItemWithTargetTypeFilter, GetDefaultsMatchTicks, + + SourceInitializationTicks, + SourceGetContextCompletedTicks, + SourceGetContextCanceledTicks, + + ItemManagerSortTicks, + ItemManagerUpdateCompletedTicks, + ItemManagerUpdateCanceledTicks, } internal static void LogImportCompletionGetContext(bool isBlocking, bool delayed) @@ -89,6 +97,38 @@ internal static void LogSessionHasTargetTypeFilterEnabled() => internal static void LogGetDefaultsMatchTicksDataPoint(int count) => s_statisticLogAggregator.AddDataPoint((int)ActionInfo.GetDefaultsMatchTicks, count); + internal static void LogSourceInitializationTicksDataPoint(int count) + { + s_statisticLogAggregator.AddDataPoint((int)ActionInfo.SourceInitializationTicks, count); + s_histogramLogAggregator.IncreaseCount((int)ActionInfo.SourceInitializationTicks, count); + } + + internal static void LogSourceGetContextTicksDataPoint(int count, bool isCanceled) + { + var key = isCanceled + ? ActionInfo.SourceGetContextCanceledTicks + : ActionInfo.SourceGetContextCompletedTicks; + + s_statisticLogAggregator.AddDataPoint((int)key, count); + s_histogramLogAggregator.IncreaseCount((int)key, count); + } + + internal static void LogItemManagerSortTicksDataPoint(int count) + { + s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ItemManagerSortTicks, count); + s_histogramLogAggregator.IncreaseCount((int)ActionInfo.ItemManagerSortTicks, count); + } + + internal static void LogItemManagerUpdateDataPoint(int count, bool isCanceled) + { + var key = isCanceled + ? ActionInfo.ItemManagerUpdateCanceledTicks + : ActionInfo.ItemManagerUpdateCompletedTicks; + + s_statisticLogAggregator.AddDataPoint((int)key, count); + s_histogramLogAggregator.IncreaseCount((int)key, count); + } + internal static void ReportTelemetry() { Logger.Log(FunctionId.Intellisense_AsyncCompletion_Data, KeyValueLogMessage.Create(m => diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 5603b0d71f2be..ef428d7ab89cd 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -88,54 +88,62 @@ public AsyncCompletionData.CompletionStartData InitializeCompletion( SnapshotPoint triggerLocation, CancellationToken cancellationToken) { - // We take sourceText from document to get a snapshot span. - // We would like to be sure that nobody changes buffers at the same time. - AssertIsForeground(); - - if (_textView.Selection.Mode == TextSelectionMode.Box) + var stopwatch = SharedStopwatch.StartNew(); + try { - // No completion with multiple selection - return AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; - } + // We take sourceText from document to get a snapshot span. + // We would like to be sure that nobody changes buffers at the same time. + AssertIsForeground(); - var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - { - return AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; - } + if (_textView.Selection.Mode == TextSelectionMode.Box) + { + // No completion with multiple selection + return AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; + } - var service = document.GetLanguageService(); - if (service == null) - { - return AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; - } + var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) + { + return AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; + } + + var service = document.GetLanguageService(); + if (service == null) + { + return AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; + } - var options = _globalOptions.GetCompletionOptions(document.Project.Language); + var options = _globalOptions.GetCompletionOptions(document.Project.Language); - // The Editor supports the option per textView. - // There could be mixed desired behavior per textView and even per same completion session. - // The right fix would be to send this information as a result of the method. - // Then, the Editor would choose the right behavior for mixed cases. - _textView.Options.GlobalOptions.SetOptionValue(s_nonBlockingCompletionEditorOption, !_globalOptions.GetOption(CompletionViewOptions.BlockForCompletionItems, service.Language)); - _responsiveCompletionEnabled = _textView.Options.GetOptionValue(DefaultOptions.ResponsiveCompletionOptionId); + // The Editor supports the option per textView. + // There could be mixed desired behavior per textView and even per same completion session. + // The right fix would be to send this information as a result of the method. + // Then, the Editor would choose the right behavior for mixed cases. + _textView.Options.GlobalOptions.SetOptionValue(s_nonBlockingCompletionEditorOption, !_globalOptions.GetOption(CompletionViewOptions.BlockForCompletionItems, service.Language)); + _responsiveCompletionEnabled = _textView.Options.GetOptionValue(DefaultOptions.ResponsiveCompletionOptionId); - // In case of calls with multiple completion services for the same view (e.g. TypeScript and C#), those completion services must not be called simultaneously for the same session. - // Therefore, in each completion session we use a list of commit character for a specific completion service and a specific content type. - _textView.Properties[PotentialCommitCharacters] = service.GetRules(options).DefaultCommitCharacters; + // In case of calls with multiple completion services for the same view (e.g. TypeScript and C#), those completion services must not be called simultaneously for the same session. + // Therefore, in each completion session we use a list of commit character for a specific completion service and a specific content type. + _textView.Properties[PotentialCommitCharacters] = service.GetRules(options).DefaultCommitCharacters; - // Reset a flag which means a snippet triggered by ? + Tab. - // Set it later if met the condition. - _snippetCompletionTriggeredIndirectly = false; + // Reset a flag which means a snippet triggered by ? + Tab. + // Set it later if met the condition. + _snippetCompletionTriggeredIndirectly = false; - var sourceText = document.GetTextSynchronously(cancellationToken); + var sourceText = document.GetTextSynchronously(cancellationToken); - return ShouldTriggerCompletion(trigger, triggerLocation, sourceText, document, service, options) - ? new AsyncCompletionData.CompletionStartData( - participation: AsyncCompletionData.CompletionParticipation.ProvidesItems, - applicableToSpan: new SnapshotSpan( - triggerLocation.Snapshot, - service.GetDefaultCompletionListSpan(sourceText, triggerLocation.Position).ToSpan())) - : AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; + return ShouldTriggerCompletion(trigger, triggerLocation, sourceText, document, service, options) + ? new AsyncCompletionData.CompletionStartData( + participation: AsyncCompletionData.CompletionParticipation.ProvidesItems, + applicableToSpan: new SnapshotSpan( + triggerLocation.Snapshot, + service.GetDefaultCompletionListSpan(sourceText, triggerLocation.Position).ToSpan())) + : AsyncCompletionData.CompletionStartData.DoesNotParticipateInCompletion; + } + finally + { + AsyncCompletionLogger.LogSourceInitializationTicksDataPoint((int)stopwatch.Elapsed.TotalMilliseconds); + } } private bool ShouldTriggerCompletion( @@ -215,119 +223,127 @@ public async Task GetCompletionContextAsync( SnapshotSpan applicableToSpan, CancellationToken cancellationToken) { - if (session is null) - throw new ArgumentNullException(nameof(session)); - - var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - return VSCompletionContext.Empty; - - // The computation of completion items is divided into two tasks: - // - // 1. "Core" items (i.e. non-expanded) which should be included in the list regardless of the selection of expander. - // Right now this includes all items except those from unimported namespaces. - // - // 2. Expanded items which only show in the completion list when expander is selected, or by default if the corresponding - // features are enabled. Right now only items from unimported namespaces are associated with expander. - // - // #1 is the essence of completion so we'd always wait until its task is completed and return the results. However, because we have - // a really tight perf budget in completion, and computing those items in #2 could be expensive especially in a large solution - // (e.g. requires syntax/symbol indices and/or runs in OOP,) we decide to kick off the computation in parallel when completion is - // triggered, but only include its results if it's completed by the time task #1 is completed, otherwise we don't wait on it and - // just return items from #1 immediately. Task #2 will still be running in the background (until session is dismissed/committed,) - // and we'd check back to see if it's completed whenever we have a chance to update the completion list, i.e. when user typed another - // character, a filter was selected, etc. If so, those items will be added as part of the refresh. - // - // The reason of adopting this approach is we want to minimize typing delays. There are two ways user might perceive a delay in typing. - // First, they could see a delay between typing a character and completion list being displayed if they want to examine the items available. - // Second, they might be typing continuously w/o paying attention to completion list, and simply expect the completion to do the "right thing" - // when a commit char is typed (e.g. commit "cancellationToken" when typing 'can$TAB$'). However, the commit could be delayed if completion is - // still waiting on the computation of all available items, which manifests as UI delays and in worst case timeouts in commit which results in - // unexpected behavior (e.g. typing 'can$TAB$' results in a 250ms UI freeze and still ends up with "can" instead of "cancellationToken".) - // - // This approach would ensure the computation of #2 will not be the cause of such delays, with the obvious trade off of potentially not providing - // expanded items until later (or never) in a completion session even if the feature is enabled. Note that in most cases we'd expect task #2 to finish - // in time and complete result would be available from the start of the session. However, even in the case only partial result is returned at the start, - // we still believe this is acceptable given how critical perf is in typing scenario. - // Additionally, expanded items are usually considered complementary. The need for them only rise occasionally (it's rare when users need to add imports,) - // and when they are needed, our hypothesis is because of their more intrusive nature (adding an import to the document) users would more likely to - // contemplate such action thus typing slower before commit and/or spending more time examining the list, which give us some opportunities - // to still provide those items later before they are truly required. - - var options = _globalOptions.GetCompletionOptions(document.Project.Language); - var sessionData = CompletionSessionData.GetOrCreateSessionData(session); - - // For telemetry reporting purpose - sessionData.TargetTypeFilterExperimentEnabled = options.TargetTypedCompletionFilter; - - if (!options.ShouldShowItemsFromUnimportNamspaces()) + var totalStopWatch = SharedStopwatch.StartNew(); + try { - // No need to trigger expanded providers at all if the feature is disabled, just trigger core providers and return; - var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, - options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - - UpdateSessionData(session, sessionData, list, triggerLocation); - return context; - } - else if (!_responsiveCompletionEnabled) - { - // We tie the behavior of delaying expand items to editor's "responsive completion" option. - // i.e. "responsive completion" disabled == always wait for all items to be calculated. - var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, - options with { ExpandedCompletionBehavior = ExpandedCompletionMode.AllItems }, cancellationToken).ConfigureAwait(false); - - UpdateSessionData(session, sessionData, list, triggerLocation); - AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: true, delayed: false); - return context; - } - else - { - // OK, expand item is enabled but we shouldn't block completion on its results. - // Kick off expand item calculation first in background. - Stopwatch stopwatch = new(); - var expandedItemsTask = Task.Run(async () => + if (session is null) + throw new ArgumentNullException(nameof(session)); + + var document = triggerLocation.Snapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) + return VSCompletionContext.Empty; + + // The computation of completion items is divided into two tasks: + // + // 1. "Core" items (i.e. non-expanded) which should be included in the list regardless of the selection of expander. + // Right now this includes all items except those from unimported namespaces. + // + // 2. Expanded items which only show in the completion list when expander is selected, or by default if the corresponding + // features are enabled. Right now only items from unimported namespaces are associated with expander. + // + // #1 is the essence of completion so we'd always wait until its task is completed and return the results. However, because we have + // a really tight perf budget in completion, and computing those items in #2 could be expensive especially in a large solution + // (e.g. requires syntax/symbol indices and/or runs in OOP,) we decide to kick off the computation in parallel when completion is + // triggered, but only include its results if it's completed by the time task #1 is completed, otherwise we don't wait on it and + // just return items from #1 immediately. Task #2 will still be running in the background (until session is dismissed/committed,) + // and we'd check back to see if it's completed whenever we have a chance to update the completion list, i.e. when user typed another + // character, a filter was selected, etc. If so, those items will be added as part of the refresh. + // + // The reason of adopting this approach is we want to minimize typing delays. There are two ways user might perceive a delay in typing. + // First, they could see a delay between typing a character and completion list being displayed if they want to examine the items available. + // Second, they might be typing continuously w/o paying attention to completion list, and simply expect the completion to do the "right thing" + // when a commit char is typed (e.g. commit "cancellationToken" when typing 'can$TAB$'). However, the commit could be delayed if completion is + // still waiting on the computation of all available items, which manifests as UI delays and in worst case timeouts in commit which results in + // unexpected behavior (e.g. typing 'can$TAB$' results in a 250ms UI freeze and still ends up with "can" instead of "cancellationToken".) + // + // This approach would ensure the computation of #2 will not be the cause of such delays, with the obvious trade off of potentially not providing + // expanded items until later (or never) in a completion session even if the feature is enabled. Note that in most cases we'd expect task #2 to finish + // in time and complete result would be available from the start of the session. However, even in the case only partial result is returned at the start, + // we still believe this is acceptable given how critical perf is in typing scenario. + // Additionally, expanded items are usually considered complementary. The need for them only rise occasionally (it's rare when users need to add imports,) + // and when they are needed, our hypothesis is because of their more intrusive nature (adding an import to the document) users would more likely to + // contemplate such action thus typing slower before commit and/or spending more time examining the list, which give us some opportunities + // to still provide those items later before they are truly required. + + var options = _globalOptions.GetCompletionOptions(document.Project.Language); + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + + // For telemetry reporting purpose + sessionData.TargetTypeFilterExperimentEnabled = options.TargetTypedCompletionFilter; + + if (!options.ShouldShowItemsFromUnimportNamspaces()) { - var result = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, - options with { ExpandedCompletionBehavior = ExpandedCompletionMode.ExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - - // Record how long it takes for the background task to complete *after* core providers returned. - // If telemetry shows that a short wait is all it takes for ExpandedItemsTask to complete in - // majority of the sessions, then we might consider doing that instead of return immediately. - // There could be a race around the usage of this stopwatch, I ignored it since we just need a rough idea: - // we always log the time even if the stopwatch's not started regardless of whether expand items are included intially - // (that number can be obtained via another property.) - AsyncCompletionLogger.LogAdditionalTicksToCompleteDelayedImportCompletionDataPoint((int)stopwatch.ElapsedMilliseconds); - - return result; - }, cancellationToken); - - // Now trigger and wait for core providers to return; - var (nonExpandedContext, nonExpandedCompletionList) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, + // No need to trigger expanded providers at all if the feature is disabled, just trigger core providers and return; + var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); - UpdateSessionData(session, sessionData, nonExpandedCompletionList, triggerLocation); - if (expandedItemsTask.IsCompleted) + UpdateSessionData(session, sessionData, list, triggerLocation); + return context; + } + else if (!_responsiveCompletionEnabled) { - // the task of expanded item is completed, get the result and combine it with result of non-expanded items. - var (expandedContext, expandedCompletionList) = await expandedItemsTask.ConfigureAwait(false); - UpdateSessionData(session, sessionData, expandedCompletionList, triggerLocation); - AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: false); + // We tie the behavior of delaying expand items to editor's "responsive completion" option. + // i.e. "responsive completion" disabled == always wait for all items to be calculated. + var (context, list) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, + options with { ExpandedCompletionBehavior = ExpandedCompletionMode.AllItems }, cancellationToken).ConfigureAwait(false); - return CombineCompletionContext(nonExpandedContext, expandedContext); + UpdateSessionData(session, sessionData, list, triggerLocation); + AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: true, delayed: false); + return context; } else { - // Expanded item task still running. Save it to the session and return non-expanded items immediately. - // Also start the stopwatch since we'd like to know how long it takes for the expand task to finish - // after core providers completed (instead of how long it takes end-to-end). - stopwatch.Start(); + // OK, expand item is enabled but we shouldn't block completion on its results. + // Kick off expand item calculation first in background. + Stopwatch stopwatch = new(); + var expandedItemsTask = Task.Run(async () => + { + var result = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, + options with { ExpandedCompletionBehavior = ExpandedCompletionMode.ExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); + + // Record how long it takes for the background task to complete *after* core providers returned. + // If telemetry shows that a short wait is all it takes for ExpandedItemsTask to complete in + // majority of the sessions, then we might consider doing that instead of return immediately. + // There could be a race around the usage of this stopwatch, I ignored it since we just need a rough idea: + // we always log the time even if the stopwatch's not started regardless of whether expand items are included intially + // (that number can be obtained via another property.) + AsyncCompletionLogger.LogAdditionalTicksToCompleteDelayedImportCompletionDataPoint((int)stopwatch.ElapsedMilliseconds); + + return result; + }, cancellationToken); + + // Now trigger and wait for core providers to return; + var (nonExpandedContext, nonExpandedCompletionList) = await GetCompletionContextWorkerAsync(document, trigger, triggerLocation, + options with { ExpandedCompletionBehavior = ExpandedCompletionMode.NonExpandedItemsOnly }, cancellationToken).ConfigureAwait(false); + UpdateSessionData(session, sessionData, nonExpandedCompletionList, triggerLocation); + + if (expandedItemsTask.IsCompleted) + { + // the task of expanded item is completed, get the result and combine it with result of non-expanded items. + var (expandedContext, expandedCompletionList) = await expandedItemsTask.ConfigureAwait(false); + UpdateSessionData(session, sessionData, expandedCompletionList, triggerLocation); + AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: false); + + return CombineCompletionContext(nonExpandedContext, expandedContext); + } + else + { + // Expanded item task still running. Save it to the session and return non-expanded items immediately. + // Also start the stopwatch since we'd like to know how long it takes for the expand task to finish + // after core providers completed (instead of how long it takes end-to-end). + stopwatch.Start(); - sessionData.ExpandedItemsTask = expandedItemsTask; - AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: true); + sessionData.ExpandedItemsTask = expandedItemsTask; + AsyncCompletionLogger.LogImportCompletionGetContext(isBlocking: false, delayed: true); - return nonExpandedContext; + return nonExpandedContext; + } } } + finally + { + AsyncCompletionLogger.LogSourceGetContextTicksDataPoint((int)totalStopWatch.Elapsed.TotalMilliseconds, isCanceled: cancellationToken.IsCancellationRequested); + } static VSCompletionContext CombineCompletionContext(VSCompletionContext context1, VSCompletionContext context2) { diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs index 308c4b21fa1f8..8e2f7c5bd99f4 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs @@ -32,6 +32,7 @@ public Task> SortCompletionListAsync( AsyncCompletionSessionInitialDataSnapshot data, CancellationToken cancellationToken) { + var stopwatch = SharedStopwatch.StartNew(); var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // This method is called exactly once, so use the opportunity to set a baseline for telemetry. if (sessionData.TargetTypeFilterExperimentEnabled) @@ -43,6 +44,7 @@ public Task> SortCompletionListAsync( // Sort by default comparer of Roslyn CompletionItem var sortedItems = data.InitialList.OrderBy(CompletionItemData.GetOrAddDummyRoslynItem).ToImmutableArray(); + AsyncCompletionLogger.LogItemManagerSortTicksDataPoint((int)stopwatch.Elapsed.TotalMilliseconds); return Task.FromResult(sortedItems); } @@ -51,56 +53,64 @@ public Task> SortCompletionListAsync( AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { - var sessionData = CompletionSessionData.GetOrCreateSessionData(session); + var stopwatch = SharedStopwatch.StartNew(); + try + { + var sessionData = CompletionSessionData.GetOrCreateSessionData(session); - // As explained in more details in the comments for `CompletionSource.GetCompletionContextAsync`, expanded items might - // not be provided upon initial trigger of completion to reduce typing delays, even if they are supposed to be included by default. - // While we do not expect to run in to this scenario very often, we'd still want to minimize the impact on user experience of this feature - // as best as we could when it does occur. So the solution we came up with is this: if we decided to not include expanded items (because the - // computation is running too long,) we will let it run in the background as long as the completion session is still active. Then whenever - // any user input that would cause the completion list to refresh, we will check the state of this background task and add expanded items as part - // of the update if they are available. - // There is a `CompletionContext.IsIncomplete` flag, which is only supported in LSP mode at the moment. Therefore we opt to handle the checking - // and combining the items in Roslyn until the `IsIncomplete` flag is fully supported in classic mode. + // As explained in more details in the comments for `CompletionSource.GetCompletionContextAsync`, expanded items might + // not be provided upon initial trigger of completion to reduce typing delays, even if they are supposed to be included by default. + // While we do not expect to run in to this scenario very often, we'd still want to minimize the impact on user experience of this feature + // as best as we could when it does occur. So the solution we came up with is this: if we decided to not include expanded items (because the + // computation is running too long,) we will let it run in the background as long as the completion session is still active. Then whenever + // any user input that would cause the completion list to refresh, we will check the state of this background task and add expanded items as part + // of the update if they are available. + // There is a `CompletionContext.IsIncomplete` flag, which is only supported in LSP mode at the moment. Therefore we opt to handle the checking + // and combining the items in Roslyn until the `IsIncomplete` flag is fully supported in classic mode. - if (sessionData.CombinedSortedList.HasValue) - { - // Always use the previously saved combined list if available. - data = new AsyncCompletionSessionDataSnapshot(sessionData.CombinedSortedList.Value, data.Snapshot, data.Trigger, data.InitialTrigger, data.SelectedFilters, - data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); - } - else if (sessionData.ExpandedItemsTask != null) - { - var task = sessionData.ExpandedItemsTask; - if (task.Status == TaskStatus.RanToCompletion) + if (sessionData.CombinedSortedList.HasValue) { - // Make sure the task is removed when Adding expanded items, - // so duplicated items won't be added in subsequent list updates. - sessionData.ExpandedItemsTask = null; - - var (expandedContext, _) = await task.ConfigureAwait(false); - if (expandedContext.Items.Length > 0) + // Always use the previously saved combined list if available. + data = new AsyncCompletionSessionDataSnapshot(sessionData.CombinedSortedList.Value, data.Snapshot, data.Trigger, data.InitialTrigger, data.SelectedFilters, + data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); + } + else if (sessionData.ExpandedItemsTask != null) + { + var task = sessionData.ExpandedItemsTask; + if (task.Status == TaskStatus.RanToCompletion) { - // Here we rely on the implementation detail of `CompletionItem.CompareTo`, which always put expand items after regular ones. - var itemsBuilder = ImmutableArray.CreateBuilder(expandedContext.Items.Length + data.InitialSortedList.Length); - itemsBuilder.AddRange(data.InitialSortedList); - itemsBuilder.AddRange(expandedContext.Items); - var combinedList = itemsBuilder.MoveToImmutable(); + // Make sure the task is removed when Adding expanded items, + // so duplicated items won't be added in subsequent list updates. + sessionData.ExpandedItemsTask = null; - // Add expanded items into a combined list, and save it to be used for future updates during the same session. - sessionData.CombinedSortedList = combinedList; - var combinedFilterStates = FilterSet.CombineFilterStates(expandedContext.Filters, data.SelectedFilters); + var (expandedContext, _) = await task.ConfigureAwait(false); + if (expandedContext.Items.Length > 0) + { + // Here we rely on the implementation detail of `CompletionItem.CompareTo`, which always put expand items after regular ones. + var itemsBuilder = ImmutableArray.CreateBuilder(expandedContext.Items.Length + data.InitialSortedList.Length); + itemsBuilder.AddRange(data.InitialSortedList); + itemsBuilder.AddRange(expandedContext.Items); + var combinedList = itemsBuilder.MoveToImmutable(); - data = new AsyncCompletionSessionDataSnapshot(combinedList, data.Snapshot, data.Trigger, data.InitialTrigger, combinedFilterStates, - data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); - } + // Add expanded items into a combined list, and save it to be used for future updates during the same session. + sessionData.CombinedSortedList = combinedList; + var combinedFilterStates = FilterSet.CombineFilterStates(expandedContext.Filters, data.SelectedFilters); + + data = new AsyncCompletionSessionDataSnapshot(combinedList, data.Snapshot, data.Trigger, data.InitialTrigger, combinedFilterStates, + data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); + } - AsyncCompletionLogger.LogSessionWithDelayedImportCompletionIncludedInUpdate(); + AsyncCompletionLogger.LogSessionWithDelayedImportCompletionIncludedInUpdate(); + } } - } - var updater = new CompletionListUpdater(session.ApplicableToSpan, sessionData, data, _recentItemsManager, _globalOptions); - return updater.UpdateCompletionList(cancellationToken); + var updater = new CompletionListUpdater(session.ApplicableToSpan, sessionData, data, _recentItemsManager, _globalOptions); + return updater.UpdateCompletionList(cancellationToken); + } + finally + { + AsyncCompletionLogger.LogItemManagerUpdateDataPoint((int)stopwatch.Elapsed.TotalMilliseconds, isCanceled: cancellationToken.IsCancellationRequested); + } } } } From 6812ec14d38fb31aa475ccdf42fcd687c7a947ac Mon Sep 17 00:00:00 2001 From: Andrew Hall Date: Tue, 22 Feb 2022 14:59:05 -0800 Subject: [PATCH 131/187] Value Tracking: Remove inproc symbol usage (#59473) Remove symbol usage in the Value Tracking command handler, and use the IValueTrackingService instead to find the right symbol at the root. Fixes #59378 --- .../ValueTrackedTreeItemViewModel.cs | 5 +- .../ValueTrackingCommandHandler.cs | 108 ++++++------------ 2 files changed, 39 insertions(+), 74 deletions(-) diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs index 945ae9e206746..995dbe4e19bb5 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackedTreeItemViewModel.cs @@ -83,6 +83,7 @@ private ValueTrackedTreeItemViewModel( internal static async ValueTask CreateAsync( Solution solution, ValueTrackedItem item, + ImmutableArray children, ValueTrackingTreeViewModel treeViewModel, IGlyphService glyphService, IValueTrackingService valueTrackingService, @@ -108,7 +109,7 @@ internal static async ValueTask CreateAsync( globalOptions, threadingContext, fileName, - children: ImmutableArray.Empty); + children); } private void CalculateChildren() @@ -173,7 +174,7 @@ private async Task> CalculateChildrenAsync(Can cancellationToken).ConfigureAwait(false); return await valueTrackedItems.SelectAsArrayAsync((item, cancellationToken) => - CreateAsync(_solution, item, TreeViewModel, _glyphService, _valueTrackingService, _globalOptions, ThreadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); + CreateAsync(_solution, item, children: ImmutableArray.Empty, TreeViewModel, _glyphService, _valueTrackingService, _globalOptions, ThreadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); } } } diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index 735d18a399119..a5c8066ba2ef7 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -5,10 +5,10 @@ using System; using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -90,101 +90,65 @@ public bool ExecuteCommand(ValueTrackingEditorCommandArgs args, CommandExecution _threadingContext.JoinableTaskFactory.RunAsync(async () => { - var selectedSymbol = await GetSelectedSymbolAsync(textSpan, document, cancellationToken).ConfigureAwait(false); - if (selectedSymbol is null) + var service = document.Project.Solution.Workspace.Services.GetRequiredService(); + var items = await service.TrackValueSourceAsync(textSpan, document, cancellationToken).ConfigureAwait(false); + if (items.Length == 0) { - // TODO: Show error dialog return; } - var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); - var location = Location.Create(syntaxTree, textSpan); - - await ShowToolWindowAsync(args.TextView, selectedSymbol, location, document.Project.Solution, cancellationToken).ConfigureAwait(false); + await ShowToolWindowAsync(args.TextView, document, items, cancellationToken).ConfigureAwait(false); }); return true; } - private static async Task GetSelectedSymbolAsync(TextSpan textSpan, Document document, CancellationToken cancellationToken) - { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var selectedNode = root.FindNode(textSpan); - if (selectedNode is null) - { - return null; - } - - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var selectedSymbol = - semanticModel.GetSymbolInfo(selectedNode, cancellationToken).Symbol - ?? semanticModel.GetDeclaredSymbol(selectedNode, cancellationToken); - - if (selectedSymbol is null) - { - return null; - } - - return selectedSymbol switch - { - ILocalSymbol - or IPropertySymbol { SetMethod: not null } - or IFieldSymbol { IsReadOnly: false } - or IEventSymbol - or IParameterSymbol - => selectedSymbol, - - _ => null - }; - } - - private async Task ShowToolWindowAsync(ITextView textView, ISymbol selectedSymbol, Location location, Solution solution, CancellationToken cancellationToken) + private async Task ShowToolWindowAsync(ITextView textView, Document document, ImmutableArray items, CancellationToken cancellationToken) { - var item = await ValueTrackedItem.TryCreateAsync(solution, location, selectedSymbol, cancellationToken: cancellationToken).ConfigureAwait(false); - if (item is null) - { - return; - } - var toolWindow = await GetOrCreateToolWindowAsync(textView, cancellationToken).ConfigureAwait(false); if (toolWindow?.ViewModel is null) { return; } - var valueTrackingService = solution.Workspace.Services.GetRequiredService(); var classificationFormatMap = _classificationFormatMapService.GetClassificationFormatMap(textView); + var solution = document.Project.Solution; + var valueTrackingService = solution.Workspace.Services.GetRequiredService(); + var rootItemMap = items.GroupBy(i => i.Parent, resultSelector: (key, items) => (parent: key, children: items)); - var childItems = await valueTrackingService.TrackValueSourceAsync(solution, item, cancellationToken).ConfigureAwait(false); - - var childViewModels = await childItems.SelectAsArrayAsync((item, cancellationToken) => - ValueTrackedTreeItemViewModel.CreateAsync(solution, item, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken), cancellationToken).ConfigureAwait(false); - - RoslynDebug.AssertNotNull(location.SourceTree); - var document = solution.GetRequiredDocument(location.SourceTree); - var options = _globalOptions.GetClassificationOptions(document.Project.Language); + using var _ = CodeAnalysis.PooledObjects.ArrayBuilder.GetInstance(out var rootItems); - var sourceText = await location.SourceTree.GetTextAsync(cancellationToken).ConfigureAwait(false); - var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(document, location.SourceSpan, options, cancellationToken).ConfigureAwait(false); - var classificationResult = await ClassifiedSpansAndHighlightSpanFactory.ClassifyAsync(documentSpan, options, cancellationToken).ConfigureAwait(false); + foreach (var (parent, children) in rootItemMap) + { + if (parent is null) + { + foreach (var child in children) + { + var root = await ValueTrackedTreeItemViewModel.CreateAsync(solution, child, children: ImmutableArray.Empty, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken).ConfigureAwait(false); + rootItems.Add(root); + } + } + else + { + using var _1 = CodeAnalysis.PooledObjects.ArrayBuilder.GetInstance(out var childItems); + foreach (var child in children) + { + var childViewModel = await ValueTrackedTreeItemViewModel.CreateAsync(solution, child, children: ImmutableArray.Empty, toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken).ConfigureAwait(false); + childItems.Add(childViewModel); + } - var root = new TreeItemViewModel( - location.SourceSpan, - sourceText, - document.Id, - document.FilePath ?? document.Name, - selectedSymbol.GetGlyph(), - classificationResult.ClassifiedSpans, - toolWindow.ViewModel, - _glyphService, - _threadingContext, - solution.Workspace, - childViewModels); + var root = await ValueTrackedTreeItemViewModel.CreateAsync(solution, parent, childItems.ToImmutable(), toolWindow.ViewModel, _glyphService, valueTrackingService, _globalOptions, _threadingContext, cancellationToken).ConfigureAwait(false); + rootItems.Add(root); + } + } await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); toolWindow.ViewModel.Roots.Clear(); - toolWindow.ViewModel.Roots.Add(root); + foreach (var root in rootItems) + { + toolWindow.ViewModel.Roots.Add(root); + } await ShowToolWindowAsync(cancellationToken).ConfigureAwait(true); } From e94072fe9a62b10447143d422e54c50bd88d9d27 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 22 Feb 2022 15:30:35 -0800 Subject: [PATCH 132/187] Further simplify anchor indentation updates --- .../InlineTemporary/InlineTemporaryTests.cs | 10 +++++----- .../Engine/Trivia/CSharpTriviaFormatter.cs | 13 ++----------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs index 1d26f92ade343..eccc90cec61fc 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InlineTemporary/InlineTemporaryTests.cs @@ -1368,9 +1368,9 @@ await TestFixOneAsync(@" { int #if true - y, + y, #endif - z; + z; int a = 1; }"); @@ -1395,7 +1395,7 @@ await TestFixOneAsync(@" #if true #endif - z; + z; int a = 1; }"); @@ -1418,9 +1418,9 @@ await TestFixOneAsync(@" { int y, #if true - z + z #endif - ; + ; int a = 1; }"); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs index d2f26ecb7760b..6b858eb9a44b5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs @@ -69,22 +69,13 @@ protected override LineColumnRule GetLineColumnRuleBetween(SyntaxTrivia trivia1, // [trivia] [whitespace] [token] case if (trivia2.IsKind(SyntaxKind.None)) { - var insertNewLine = this.FormattingRules.GetAdjustNewLinesOperation(this.Token1, this.Token2) != null; - if (IsMultilineComment(trivia1)) { + var insertNewLine = this.FormattingRules.GetAdjustNewLinesOperation(this.Token1, this.Token2) != null; return LineColumnRule.PreserveLinesWithGivenIndentation(lines: insertNewLine ? 1 : 0); } - if (insertNewLine) - { - if (existingWhitespaceBetween.Spaces != this.Spaces) - return LineColumnRule.PreserveWithGivenSpaces(spaces: this.Spaces); - else - return LineColumnRule.PreserveLinesWithDefaultIndentation(lines: 0); - } - - if (existingWhitespaceBetween.Lines > 0 && existingWhitespaceBetween.Spaces != this.Spaces) + if (existingWhitespaceBetween.Spaces != this.Spaces) { return LineColumnRule.PreserveWithGivenSpaces(spaces: this.Spaces); } From 587388c52c4aefb5010e972c619222f4025182f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 22 Feb 2022 15:46:05 -0800 Subject: [PATCH 133/187] Fix race condition in RequestTelemetryLogger (#59669) --- ...stExecutionQueue.RequestTelemetryLogger.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs index 8cf7606e93faf..cce10a8aebd40 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Internal.Log; @@ -17,7 +15,7 @@ internal partial class RequestExecutionQueue /// Logs metadata on LSP requests (duration, success / failure metrics) /// for this particular LSP server instance. /// - internal class RequestTelemetryLogger : IDisposable + internal sealed class RequestTelemetryLogger : IDisposable { private const string QueuedDurationKey = "QueuedDuration"; @@ -26,7 +24,7 @@ internal class RequestTelemetryLogger : IDisposable /// /// Histogram to aggregate the time in queue metrics. /// - private HistogramLogAggregator? _queuedDurationLogAggregator; + private readonly HistogramLogAggregator _queuedDurationLogAggregator; /// /// Histogram to aggregate total request duration metrics. @@ -36,7 +34,7 @@ internal class RequestTelemetryLogger : IDisposable /// This provides highly detailed buckets when duration is in MS, but less detailed /// when the duration is in terms of seconds or minutes. /// - private HistogramLogAggregator? _requestDurationLogAggregator; + private readonly HistogramLogAggregator _requestDurationLogAggregator; /// /// Store request counters in a concurrent dictionary as non-mutating LSP requests can @@ -46,6 +44,8 @@ internal class RequestTelemetryLogger : IDisposable private readonly LogAggregator _findDocumentResults; + private int _disposed; + public RequestTelemetryLogger(string serverTypeName) { _serverTypeName = serverTypeName; @@ -79,10 +79,10 @@ public void UpdateTelemetryData( { // Find the bucket corresponding to the queued duration and update the count of durations in that bucket. // This is not broken down per method as time in queue is not specific to an LSP method. - _queuedDurationLogAggregator?.IncreaseCount(QueuedDurationKey, Convert.ToDecimal(queuedDuration.TotalMilliseconds)); + _queuedDurationLogAggregator.IncreaseCount(QueuedDurationKey, Convert.ToDecimal(queuedDuration.TotalMilliseconds)); // Store the request time metrics per LSP method. - _requestDurationLogAggregator?.IncreaseCount(methodName, Convert.ToDecimal(ComputeLogValue(requestDuration.TotalMilliseconds))); + _requestDurationLogAggregator.IncreaseCount(methodName, Convert.ToDecimal(ComputeLogValue(requestDuration.TotalMilliseconds))); _requestCounters.GetOrAdd(methodName, (_) => new Counter()).IncrementCount(result); } @@ -103,8 +103,12 @@ private static double ComputeLogValue(double durationInMS) /// public void Dispose() { - if (_queuedDurationLogAggregator is null || _queuedDurationLogAggregator.IsEmpty - || _requestDurationLogAggregator is null || _requestDurationLogAggregator.IsEmpty) + if (Interlocked.Exchange(ref _disposed, 1) != 0) + { + return; + } + + if (_queuedDurationLogAggregator.IsEmpty || _requestDurationLogAggregator.IsEmpty) { return; } @@ -150,10 +154,7 @@ public void Dispose() } })); - // Clear telemetry we've published in case dispose is called multiple times. _requestCounters.Clear(); - _queuedDurationLogAggregator = null; - _requestDurationLogAggregator = null; } private class Counter From e6d73d109a294ee8c172284e2043d3df1f825f35 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 10 Feb 2022 16:38:56 -0800 Subject: [PATCH 134/187] Use segmented arrays for VirtualChar storage See https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1460018 --- .../Collections/ImmutableArrayExtensions.cs | 29 +++++++++++++++++++ .../VirtualChars/CSharpVirtualCharService.cs | 7 +++-- .../AbstractVirtualCharService.cs | 9 +++--- .../VirtualCharSequence.Chunks.cs | 13 +++++---- .../VirtualChars/VirtualCharSequence.cs | 7 +++-- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs index 66d29b193e4f2..889885970e3f9 100644 --- a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Collections; #if DEBUG using System.Linq; @@ -862,5 +863,33 @@ internal static int BinarySearch(this ReadOnlySpan a return ~low; } + + internal static int BinarySearch(this ImmutableSegmentedList array, TValue value, Func comparer) + { + int low = 0; + int high = array.Count - 1; + + while (low <= high) + { + int middle = low + ((high - low) >> 1); + int comparison = comparer(array[middle], value); + + if (comparison == 0) + { + return middle; + } + + if (comparison > 0) + { + high = middle - 1; + } + else + { + low = middle + 1; + } + } + + return ~low; + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs index bbf3e04d0a3b1..b2a06a07bb75d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Text; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.LanguageServices; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; @@ -102,7 +103,7 @@ private static VirtualCharSequence TryConvertRawStringToVirtualChars( var tokenText = token.Text; var offset = token.SpanStart; - using var _ = ArrayBuilder.GetInstance(out var result); + var result = ImmutableSegmentedList.CreateBuilder(); var startIndexInclusive = 0; var endIndexExclusive = tokenText.Length; @@ -186,14 +187,14 @@ private static VirtualCharSequence CreateVirtualCharSequence( string tokenText, int offset, int startIndexInclusive, int endIndexExclusive, ArrayBuilder<(char ch, TextSpan span)> charResults) { // Second pass. Convert those characters to Runes. - using var _ = ArrayBuilder.GetInstance(out var runeResults); + var runeResults = ImmutableSegmentedList.CreateBuilder(); ConvertCharactersToRunes(charResults, runeResults); return CreateVirtualCharSequence(tokenText, offset, startIndexInclusive, endIndexExclusive, runeResults); } - private static void ConvertCharactersToRunes(ArrayBuilder<(char ch, TextSpan span)> charResults, ArrayBuilder runeResults) + private static void ConvertCharactersToRunes(ArrayBuilder<(char ch, TextSpan span)> charResults, ImmutableSegmentedList.Builder runeResults) { for (var i = 0; i < charResults.Count;) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs index 7fde02e6be21d..9505a4051f5f5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Text; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -147,7 +148,7 @@ protected static VirtualCharSequence TryConvertSimpleDoubleQuoteString( var startIndexInclusive = startDelimiter.Length; var endIndexExclusive = tokenText.Length - endDelimiter.Length; - using var _ = ArrayBuilder.GetInstance(out var result); + var result = ImmutableSegmentedList.CreateBuilder(); var offset = token.SpanStart; for (var index = startIndexInclusive; index < endIndexExclusive;) @@ -164,7 +165,7 @@ protected static VirtualCharSequence TryConvertSimpleDoubleQuoteString( return default; result.Add(VirtualChar.Create(new Rune(tokenText[index]), span)); - index += result.Last().Span.Length; + index += result[^1].Span.Length; continue; } @@ -178,7 +179,7 @@ protected static VirtualCharSequence TryConvertSimpleDoubleQuoteString( /// /// Returns the number of characters to jump forward (either 1 or 2); /// - protected static int ConvertTextAtIndexToRune(string tokenText, int index, ArrayBuilder result, int offset) + protected static int ConvertTextAtIndexToRune(string tokenText, int index, ImmutableSegmentedList.Builder result, int offset) { if (Rune.TryCreate(tokenText[index], out var rune)) { @@ -208,7 +209,7 @@ protected static bool IsOpenOrCloseBrace(char ch) protected static VirtualCharSequence CreateVirtualCharSequence( string tokenText, int offset, int startIndexInclusive, int endIndexExclusive, - ArrayBuilder result) + ImmutableSegmentedList.Builder result) { // Check if we actually needed to create any special virtual chars. // if not, we can avoid the entire array allocation and just wrap diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Chunks.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Chunks.cs index 6bd61d53b54ba..f719989f794a6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Chunks.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Chunks.cs @@ -5,6 +5,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Text; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars @@ -29,23 +30,23 @@ protected Chunk() } /// - /// Thin wrapper over an actual . + /// Thin wrapper over an actual . /// This will be the common construct we generate when getting the /// for a string token that has escapes in it. /// - private class ImmutableArrayChunk : Chunk + private class ImmutableSegmentedListChunk : Chunk { - private readonly ImmutableArray _array; + private readonly ImmutableSegmentedList _array; - public ImmutableArrayChunk(ImmutableArray array) + public ImmutableSegmentedListChunk(ImmutableSegmentedList array) => _array = array; - public override int Length => _array.Length; + public override int Length => _array.Count; public override VirtualChar this[int index] => _array[index]; public override VirtualChar? Find(int position) { - if (_array.Length == 0) + if (_array.IsEmpty) return null; if (position < _array[0].Span.Start || position >= _array[^1].Span.End) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs index bad6458911b83..52dbe8bb2bc98 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -27,10 +28,10 @@ namespace Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars /// internal partial struct VirtualCharSequence { - public static readonly VirtualCharSequence Empty = Create(ImmutableArray.Empty); + public static readonly VirtualCharSequence Empty = Create(ImmutableSegmentedList.Empty); - public static VirtualCharSequence Create(ImmutableArray virtualChars) - => new(new ImmutableArrayChunk(virtualChars)); + public static VirtualCharSequence Create(ImmutableSegmentedList virtualChars) + => new(new ImmutableSegmentedListChunk(virtualChars)); public static VirtualCharSequence Create(int firstVirtualCharPosition, string underlyingData) => new(new StringChunk(firstVirtualCharPosition, underlyingData)); From 1d023de0b390ab3192d78ee3e2be2fb819bcfc0b Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 22 Feb 2022 20:01:20 -0800 Subject: [PATCH 135/187] Address offline feedback from Sam and Vatsalya - switch from user facing option to feature flag option, off by default. --- .../Portable/Diagnostics/DiagnosticOptions.cs | 4 +-- .../Options/AdvancedOptionPageControl.xaml | 3 -- .../Options/AdvancedOptionPageControl.xaml.cs | 1 - .../Impl/Options/AdvancedOptionPageStrings.cs | 3 -- .../Core/Def/ServicesVSResources.resx | 3 -- .../Core/Def/Telemetry/TelemetryLogger.cs | 21 ++++++++--- .../VisualStudioWorkspaceTelemetryService.cs | 2 +- .../Core/Def/xlf/ServicesVSResources.cs.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.de.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.es.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.fr.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.it.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.ja.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.ko.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.pl.xlf | 5 --- .../Def/xlf/ServicesVSResources.pt-BR.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.ru.xlf | 5 --- .../Core/Def/xlf/ServicesVSResources.tr.xlf | 5 --- .../Def/xlf/ServicesVSResources.zh-Hans.xlf | 5 --- .../Def/xlf/ServicesVSResources.zh-Hant.xlf | 5 --- .../Options/AdvancedOptionPageControl.xaml | 2 -- .../Options/AdvancedOptionPageControl.xaml.vb | 1 - .../Impl/Options/AdvancedOptionPageStrings.vb | 3 -- .../RemoteWorkspaceTelemetryService.cs | 8 +++-- .../ServiceHubTest/TelemetryLoggerTests.cs | 36 +++++++++++++------ 25 files changed, 50 insertions(+), 102 deletions(-) diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs index 93d3920263974..7dc0d10d79f35 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticOptions.cs @@ -21,8 +21,8 @@ internal sealed class DiagnosticOptions : IOptionProvider new FeatureFlagStorageLocation("Lsp.PullDiagnostics")); public static readonly Option2 LogTelemetryForBackgroundAnalyzerExecution = new( - FeatureName, nameof(LogTelemetryForBackgroundAnalyzerExecution), defaultValue: true, - new RoamingProfileStorageLocation($"TextEditor.Specific.LogTelemetryForBackgroundAnalyzerExecution")); + FeatureName, nameof(LogTelemetryForBackgroundAnalyzerExecution), defaultValue: false, + new FeatureFlagStorageLocation($"Roslyn.LogTelemetryForBackgroundAnalyzerExecution")); public ImmutableArray Options { get; } = ImmutableArray.Create( LspPullDiagnosticsFeatureFlag, diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml index ab64d6b02b5aa..45a1797761567 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml @@ -57,9 +57,6 @@ - - { // If the option has not been set by the user, check if the option to remove unused references diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs index a19c50fdca874..a2c8199f320d2 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs @@ -301,9 +301,6 @@ public static string Option_Enable_file_logging_for_diagnostics public static string Option_Skip_analyzers_for_implicitly_triggered_builds => ServicesVSResources.Skip_analyzers_for_implicitly_triggered_builds; - public static string Option_Log_telemetry_for_background_code_analysis - => ServicesVSResources.Log_telemetry_for_background_code_analysis; - public static string Show_inheritance_margin => ServicesVSResources.Show_inheritance_margin; diff --git a/src/VisualStudio/Core/Def/ServicesVSResources.resx b/src/VisualStudio/Core/Def/ServicesVSResources.resx index 79bd32a930656..f559c9ccd768b 100644 --- a/src/VisualStudio/Core/Def/ServicesVSResources.resx +++ b/src/VisualStudio/Core/Def/ServicesVSResources.resx @@ -1642,9 +1642,6 @@ Additional information: {1} Skip analyzers for implicitly triggered builds - - Log telemetry for background code analysis - This action cannot be undone. Do you wish to continue? diff --git a/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs b/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs index 757d1631f4a1b..409e285d63373 100644 --- a/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs +++ b/src/VisualStudio/Core/Def/Telemetry/TelemetryLogger.cs @@ -8,7 +8,9 @@ using System.Diagnostics; using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.Telemetry; using Roslyn.Utilities; @@ -20,8 +22,15 @@ private sealed class Implementation : TelemetryLogger { private readonly TelemetrySession _session; - public Implementation(TelemetrySession session) - => _session = session; + public Implementation(TelemetrySession session, IGlobalOptionService globalOptions) + { + _session = session; + + // Only log "delta" property for block end events if feature flag is enabled. + LogDelta = globalOptions.GetOption(DiagnosticOptions.LogTelemetryForBackgroundAnalyzerExecution); + } + + protected override bool LogDelta { get; } public override bool IsEnabled(FunctionId functionId) => _session.IsOptedIn; @@ -66,6 +75,8 @@ protected override void End(object scope, TelemetryResult result) private static readonly ConcurrentDictionary s_eventMap = new(); private static readonly ConcurrentDictionary<(FunctionId id, string name), string> s_propertyMap = new(); + protected abstract bool LogDelta { get; } + internal static string GetEventName(FunctionId id) => s_eventMap.GetOrAdd(id, id => EventPrefix + GetTelemetryName(id, separator: '/')); @@ -75,8 +86,8 @@ internal static string GetPropertyName(FunctionId id, string name) private static string GetTelemetryName(FunctionId id, char separator) => Enum.GetName(typeof(FunctionId), id)!.Replace('_', separator).ToLowerInvariant(); - public static TelemetryLogger Create(TelemetrySession session) - => new Implementation(session); + public static TelemetryLogger Create(TelemetrySession session, IGlobalOptionService globalOptions) + => new Implementation(session, globalOptions); public abstract bool IsEnabled(FunctionId functionId); protected abstract void PostEvent(TelemetryEvent telemetryEvent); @@ -132,7 +143,7 @@ public void LogBlockEnd(FunctionId functionId, LogMessage logMessage, int blockI Contract.ThrowIfFalse(_pendingScopes.TryRemove(blockId, out var scope)); var endEvent = GetEndEvent(scope); - SetProperties(endEvent, functionId, logMessage, delta); + SetProperties(endEvent, functionId, logMessage, LogDelta ? delta : null); var result = cancellationToken.IsCancellationRequested ? TelemetryResult.UserCancel : TelemetryResult.Success; diff --git a/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs b/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs index 4c56413671d99..493c302a97bf7 100644 --- a/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs +++ b/src/VisualStudio/Core/Def/Telemetry/VisualStudioWorkspaceTelemetryService.cs @@ -38,7 +38,7 @@ protected override ILogger CreateLogger(TelemetrySession telemetrySession) => AggregateLogger.Create( CodeMarkerLogger.Instance, new EtwLogger(FunctionIdOptions.CreateFunctionIsEnabledPredicate(_globalOptions)), - TelemetryLogger.Create(telemetrySession), + TelemetryLogger.Create(telemetrySession, _globalOptions), new FileLogger(_globalOptions), Logger.GetLogger()); diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf index 4ed62d0a6a99c..fde17d09bb2ff 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.cs.xlf @@ -607,11 +607,6 @@ Lokalita - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Nastavit {0} jako abstraktní diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf index d96b1636b6cca..4c4d815654aea 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.de.xlf @@ -607,11 +607,6 @@ Speicherort - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract "{0}" als abstrakt festlegen diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf index c92bb90ab735c..fbe7daf4e4773 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.es.xlf @@ -607,11 +607,6 @@ Ubicación - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Convertir "{0}" en abstracto diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf index dbbf1f256d22a..70a5e8690af1c 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.fr.xlf @@ -607,11 +607,6 @@ Emplacement - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Rendre '{0}' abstrait diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf index 2388f0fc149ce..0bc4a017089f2 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.it.xlf @@ -607,11 +607,6 @@ Posizione - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Rendi astratto '{0}' diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf index 02d73d488d2a9..9b036cbe8215e 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ja.xlf @@ -607,11 +607,6 @@ 場所 - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract '{0}' を抽象化する diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf index 1d1d17cfffa7a..30748321b0139 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ko.xlf @@ -607,11 +607,6 @@ 위치 - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract '{0}'을(를) 추상으로 지정 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf index 6a57d16342992..f154ae6b133dc 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pl.xlf @@ -607,11 +607,6 @@ Lokalizacja - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Ustaw element „{0}” jako abstrakcyjny diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf index 7ac5f5113c2da..9e9816d55afa5 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.pt-BR.xlf @@ -607,11 +607,6 @@ Localização - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Fazer '{0}' abstrato diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf index 7d57e5192c3a2..5e7eedb7924d7 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.ru.xlf @@ -607,11 +607,6 @@ Расположение - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract Сделать "{0}" абстрактным diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf index e3cb18fabdfcc..45a919583d162 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.tr.xlf @@ -607,11 +607,6 @@ Konum - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract '{0}' değerini soyut yap diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf index 12dcc268e6f01..ede0d82c1c12f 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hans.xlf @@ -607,11 +607,6 @@ 位置 - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract 将“{0}”设为抽象 diff --git a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf index e23c9df784a04..dbfe591f75e96 100644 --- a/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf +++ b/src/VisualStudio/Core/Def/xlf/ServicesVSResources.zh-Hant.xlf @@ -607,11 +607,6 @@ 位置 - - Log telemetry for background code analysis - Log telemetry for background code analysis - - Make '{0}' abstract 將 '{0}' 抽象化 diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml index b2790a6a34d2e..7c6481c63c970 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml @@ -45,8 +45,6 @@ Content="{x:Static local:AdvancedOptionPageStrings.Option_Enable_file_logging_for_diagnostics}" /> - AggregateLogger.Create( - TelemetryLogger.Create(telemetrySession), + TelemetryLogger.Create(telemetrySession, _globalOptions), Logger.GetLogger()); } } diff --git a/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs b/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs index 907ae90e7ce24..0ecc0a7d5bddd 100644 --- a/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.cs +++ b/src/Workspaces/Remote/ServiceHubTest/TelemetryLoggerTests.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.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Telemetry; using Microsoft.VisualStudio.Telemetry; @@ -20,6 +17,13 @@ public class TelemetryLoggerTests { private class TestLogger : TelemetryLogger { + public TestLogger(bool logDelta = false) + { + LogDelta = logDelta; + } + + protected override bool LogDelta { get; } + public class TestScope { public readonly TelemetryEvent EndEvent; @@ -104,10 +108,10 @@ public void EventWithProperties() }, InspectProperties(postedEvent)); } - [Fact] - public void LogBlockStartEnd() + [Theory, CombinatorialData] + public void LogBlockStartEnd(bool logDelta) { - var logger = new TestLogger(); + var logger = new TestLogger(logDelta); logger.LogBlockStart(FunctionId.Debugging_EncSession_EditSession_EmitDeltaErrorId, KeyValueLogMessage.Create(p => p.Add("test", "start"), logLevel: LogLevel.Information), blockId: 1, CancellationToken.None); @@ -118,12 +122,22 @@ public void LogBlockStartEnd() Assert.Equal("vs/ide/vbcs/debugging/encsession/editsession/emitdeltaerrorid", scope.EndEvent.Name); - // We don't inspect the property value for "Delta" (time of execution) as that value will vary each time. - AssertEx.Equal(new[] + if (logDelta) + { + // We don't inspect the property value for "Delta" (time of execution) as that value will vary each time. + AssertEx.Equal(new[] + { + "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.test=end", + "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.delta=" + }, InspectProperties(scope.EndEvent, keyToIgnoreValueInspection: "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.delta")); + } + else { - "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.test=end", - "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.delta=" - }, InspectProperties(scope.EndEvent, keyToIgnoreValueInspection: "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.delta")); + AssertEx.Equal(new[] + { + "vs.ide.vbcs.debugging.encsession.editsession.emitdeltaerrorid.test=end" + }, InspectProperties(scope.EndEvent)); + } } } } From 03016afce9f0980b4a740268650edec83b79fc84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 22 Feb 2022 22:26:39 -0800 Subject: [PATCH 136/187] Add external access APIs for O# (#59469) * Add external access apis for O# * Use wrapper pattern * Add FromDocumentAsync * Pass IdeAnalyzerOptions through * Add ImplementTypeOptions to CodeActionOptions. * Pass CodeActionOptions to FixAllContext * OmniSharpDocumentationCommentOptionsWrapper --- eng/Versions.props | 2 +- global.json | 4 +- ...RemoveUnreachableCodeDiagnosticAnalyzer.cs | 2 +- .../Core/Analyzers/Helpers/AnalyzerHelper.cs | 52 ++++--- ...oveUnnecessaryImportsDiagnosticAnalyzer.cs | 2 +- ...tValidateFormatStringDiagnosticAnalyzer.cs | 2 +- .../ValidateFormatStringOption.cs | 3 +- .../ImplementAbstractClassTests.cs | 17 ++- .../ImplementInterfaceTests.cs | 13 +- .../Wrapping/AbstractWrappingTests.cs | 5 +- .../ImplementTypeOptionsStorage.cs | 30 ++++ .../CodeActions/CodeActionOptionsStorage.cs | 2 + .../AbstractCodeActionOrUserDiagnosticTest.cs | 3 +- .../CSharpCodeFixVerifier`2+Test.cs | 35 ++++- .../CSharpCodeRefactoringVerifier`1+Test.cs | 2 +- .../CodeActions/SharedVerifierState.cs | 4 + .../VisualBasicCodeFixVerifier`2+Test.cs | 2 +- ...sualBasicCodeRefactoringVerifier`1+Test.cs | 2 +- .../Test/Preview/PreviewWorkspaceTests.cs | 4 +- ...tAbstractClassOrInterfaceCommandHandler.vb | 2 +- .../ImplementAbstractClassTests.vb | 8 +- .../ImplementInterfaceTests.vb | 8 +- .../AbstractParameterWrappingTests.vb | 6 +- ...CSharpImplementInterfaceCodeFixProvider.cs | 3 +- .../Portable/Diagnostics/AnalyzerHelper.cs | 2 +- .../Diagnostics/WorkspaceAnalyzerOptions.cs | 21 +-- .../AbstractRegexDiagnosticAnalyzer.cs | 2 +- .../RegularExpressionsOptions.cs | 3 +- .../Core/Portable/Fading/FadingOptions.cs | 5 +- ...ctImplementAbstractClassCodeFixProvider.cs | 3 +- .../ImplementType/ImplementTypeOptions.cs | 54 ------- ...lBasicImplementInterfaceCodeFixProvider.vb | 3 +- .../DiagnosticAnalyzerRunner.cs | 4 +- ...OmniSharpSyntaxFormattingOptionsFactory.cs | 134 ++++++++++++++++++ ...mniSharpWorkspaceAnalyzerOptionsFactory.cs | 14 ++ .../OmniSharpCodeFixContextFactory.cs | 35 +++++ ...SharpDocumentationCommentOptionsWrapper.cs | 36 +++++ .../OmniSharpDocumentationCommentSnippet.cs | 33 +++++ ...harpDocumentationCommentsSnippetService.cs | 60 +------- .../Formatting/OmniSharpFormatter.cs | 31 ++++ .../OmniSharpOrganizeImportsOptionsWrapper.cs | 34 +++++ ...OmniSharpSyntaxFormattingOptionsWrapper.cs | 24 ++++ .../OmniSharpImplementTypeOptions.cs | 18 +-- .../Options/AdvancedOptionPageControl.xaml.cs | 8 +- ...alStudioDiagnosticAnalyzerExecutorTests.cs | 6 +- .../Options/AdvancedOptionPageControl.xaml.vb | 8 +- .../XamlOrganizeImportsService.cs | 6 +- ...soft.CodeAnalysis.CSharp.Workspaces.csproj | 1 + .../CSharpOrganizeImportsService.Rewriter.cs | 14 +- .../CSharpOrganizeImportsService.cs | 9 +- .../Core/Portable/Formatting/Formatter.cs | 7 +- .../ImplementTypeInsertionBehavior.cs | 0 .../ImplementType/ImplementTypeOptions.cs | 21 +++ ...ImplementTypePropertyGenerationBehavior.cs | 0 .../Microsoft.CodeAnalysis.Workspaces.csproj | 3 +- .../IOrganizeImportsService.cs | 2 +- .../OrganizeImports/OrganizeImportsOptions.cs | 27 ++++ .../DiagnosticAnalyzer/DiagnosticComputer.cs | 2 +- .../Core/CodeFixes/CodeActionOptions.cs | 11 +- ...ualBasicOrganizeImportsService.Rewriter.vb | 10 +- .../VisualBasicOrganizeImportsService.vb | 14 +- 61 files changed, 611 insertions(+), 267 deletions(-) create mode 100644 src/EditorFeatures/Core/ImplementType/ImplementTypeOptionsStorage.cs delete mode 100644 src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/Analyzers/OmniSharpWorkspaceAnalyzerOptionsFactory.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentSnippet.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs create mode 100644 src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs rename src/{Features => Workspaces}/Core/Portable/ImplementType/ImplementTypeInsertionBehavior.cs (100%) create mode 100644 src/Workspaces/Core/Portable/ImplementType/ImplementTypeOptions.cs rename src/{Features => Workspaces}/Core/Portable/ImplementType/ImplementTypePropertyGenerationBehavior.cs (100%) create mode 100644 src/Workspaces/Core/Portable/OrganizeImports/OrganizeImportsOptions.cs diff --git a/eng/Versions.props b/eng/Versions.props index 46e3a8b7dab53..06c66a342b9d4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,7 +21,7 @@ 3.3.3-beta1.21105.3 6.0.0-rc1.21366.2 - 1.1.1-beta1.22081.4 + 1.1.2-beta1.22122.4 0.1.127-beta 4.0.1 diff --git a/global.json b/global.json index ee7e263ef19f1..8b4d21042c9d0 100644 --- a/global.json +++ b/global.json @@ -12,7 +12,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22117.2", - "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22117.2" + "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22080.1", + "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22080.1" } } diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/CSharpRemoveUnreachableCodeDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/CSharpRemoveUnreachableCodeDiagnosticAnalyzer.cs index 4efaffbe9927e..27d4df2cb6859 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/CSharpRemoveUnreachableCodeDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/CSharpRemoveUnreachableCodeDiagnosticAnalyzer.cs @@ -40,7 +40,7 @@ protected override void InitializeWorker(AnalysisContext context) private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) { - var fadeCode = context.GetIdeOptions().FadeOutUnreachableCode; + var fadeCode = context.Options.GetIdeOptions().FadeOutUnreachableCode; var semanticModel = context.SemanticModel; var cancellationToken = context.CancellationToken; diff --git a/src/Analyzers/Core/Analyzers/Helpers/AnalyzerHelper.cs b/src/Analyzers/Core/Analyzers/Helpers/AnalyzerHelper.cs index 78fc8b8477c4a..b2232b2c2aace 100644 --- a/src/Analyzers/Core/Analyzers/Helpers/AnalyzerHelper.cs +++ b/src/Analyzers/Core/Analyzers/Helpers/AnalyzerHelper.cs @@ -14,46 +14,44 @@ namespace Microsoft.CodeAnalysis.Diagnostics { internal readonly record struct IdeAnalyzerOptions( - bool FadeOutUnusedImports, - bool FadeOutUnreachableCode, - bool ReportInvalidPlaceholdersInStringDotFormatCalls, - bool ReportInvalidRegexPatterns) + bool FadeOutUnusedImports = true, + bool FadeOutUnreachableCode = true, + bool ReportInvalidPlaceholdersInStringDotFormatCalls = true, + bool ReportInvalidRegexPatterns = true) { + public IdeAnalyzerOptions() + : this(FadeOutUnusedImports: true) + { + } + + public static readonly IdeAnalyzerOptions Default = new(); + public static readonly IdeAnalyzerOptions CodeStyleDefault = new( FadeOutUnusedImports: false, FadeOutUnreachableCode: false, ReportInvalidPlaceholdersInStringDotFormatCalls: true, ReportInvalidRegexPatterns: true); - } - - internal static partial class AnalyzerHelper - { - public static IdeAnalyzerOptions GetIdeOptions(this SyntaxTreeAnalysisContext context) -#if CODE_STYLE - => IdeAnalyzerOptions.CodeStyleDefault; -#else - => (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.Tree.Options.Language) : IdeAnalyzerOptions.CodeStyleDefault; -#endif - public static IdeAnalyzerOptions GetIdeOptions(this OperationAnalysisContext context) -#if CODE_STYLE - => IdeAnalyzerOptions.CodeStyleDefault; -#else - => (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.Operation.Language) : IdeAnalyzerOptions.CodeStyleDefault; -#endif +#if !CODE_STYLE + public static IdeAnalyzerOptions FromProject(Project project) + => From(project.Solution.Options, project.Language); - public static IdeAnalyzerOptions GetIdeOptions(this SyntaxNodeAnalysisContext context) -#if CODE_STYLE - => IdeAnalyzerOptions.CodeStyleDefault; -#else - => (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.Node.Language) : IdeAnalyzerOptions.CodeStyleDefault; + public static IdeAnalyzerOptions From(OptionSet options, string language) + => new( + FadeOutUnusedImports: options.GetOption(Microsoft.CodeAnalysis.Fading.FadingOptions.Metadata.FadeOutUnusedImports, language), + FadeOutUnreachableCode: options.GetOption(Microsoft.CodeAnalysis.Fading.FadingOptions.Metadata.FadeOutUnreachableCode, language), + ReportInvalidPlaceholdersInStringDotFormatCalls: options.GetOption(Microsoft.CodeAnalysis.ValidateFormatString.ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, language), + ReportInvalidRegexPatterns: options.GetOption(Microsoft.CodeAnalysis.Features.EmbeddedLanguages.RegularExpressions.LanguageServices.RegularExpressionsOptions.ReportInvalidRegexPatterns, language)); #endif + } - public static IdeAnalyzerOptions GetIdeOptions(this SemanticModelAnalysisContext context) + internal static partial class AnalyzerHelper + { + public static IdeAnalyzerOptions GetIdeOptions(this AnalyzerOptions options) #if CODE_STYLE => IdeAnalyzerOptions.CodeStyleDefault; #else - => (context.Options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.GetIdeOptions(context.SemanticModel.Language) : IdeAnalyzerOptions.CodeStyleDefault; + => (options is WorkspaceAnalyzerOptions workspaceOptions) ? workspaceOptions.IdeOptions : IdeAnalyzerOptions.CodeStyleDefault; #endif public static T GetOption(this SemanticModelAnalysisContext context, Option2 option) diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs index 816dcb501398a..7bc38692f1e06 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsDiagnosticAnalyzer.cs @@ -142,7 +142,7 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context) // for us appropriately. unnecessaryImports = MergeImports(unnecessaryImports); - var fadeOut = context.GetIdeOptions().FadeOutUnusedImports; + var fadeOut = context.Options.GetIdeOptions().FadeOutUnusedImports; DiagnosticDescriptor descriptor; if (GeneratedCodeUtilities.IsGeneratedCode(tree, IsRegularCommentOrDocComment, cancellationToken)) diff --git a/src/Analyzers/Core/Analyzers/ValidateFormatString/AbstractValidateFormatStringDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/ValidateFormatString/AbstractValidateFormatStringDiagnosticAnalyzer.cs index 8a9da43afd2dd..8e06aa10dd658 100644 --- a/src/Analyzers/Core/Analyzers/ValidateFormatString/AbstractValidateFormatStringDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/ValidateFormatString/AbstractValidateFormatStringDiagnosticAnalyzer.cs @@ -96,7 +96,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol for return; } - if (!context.GetIdeOptions().ReportInvalidPlaceholdersInStringDotFormatCalls) + if (!context.Options.GetIdeOptions().ReportInvalidPlaceholdersInStringDotFormatCalls) { return; } diff --git a/src/Analyzers/Core/Analyzers/ValidateFormatString/ValidateFormatStringOption.cs b/src/Analyzers/Core/Analyzers/ValidateFormatString/ValidateFormatStringOption.cs index cf5153efb20b5..57fc450317da5 100644 --- a/src/Analyzers/Core/Analyzers/ValidateFormatString/ValidateFormatStringOption.cs +++ b/src/Analyzers/Core/Analyzers/ValidateFormatString/ValidateFormatStringOption.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.ValidateFormatString @@ -12,7 +13,7 @@ internal class ValidateFormatStringOption new( nameof(ValidateFormatStringOption), nameof(ReportInvalidPlaceholdersInStringDotFormatCalls), - defaultValue: true, + IdeAnalyzerOptions.Default.ReportInvalidPlaceholdersInStringDotFormatCalls, storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.WarnOnInvalidStringDotFormatCalls")); } } diff --git a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs b/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs index 2022170660e07..d6624d0d75a81 100644 --- a/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementAbstractClass/ImplementAbstractClassTests.cs @@ -5,6 +5,7 @@ #nullable disable using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; @@ -1468,6 +1469,11 @@ class T : A [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementAbstractClass)] public async Task TestWithGroupingOff1() { + var options = CodeActionOptions.Default with + { + ImplementTypeOptions = new ImplementTypeOptions(InsertionBehavior: ImplementTypeInsertionBehavior.AtTheEnd) + }; + await TestInRegularAndScriptAsync( @"abstract class Base { @@ -1488,7 +1494,7 @@ class Derived : Base void Goo() { } public override int Prop => throw new System.NotImplementedException(); -}", options: Option(ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd)); +}", codeActionOptions: options); } [WorkItem(17274, "https://github.com/dotnet/roslyn/issues/17274")] @@ -1641,6 +1647,11 @@ public override void M2(T? i = null) [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementAbstractClass)] public async Task TestAutoProperties() { + var options = CodeActionOptions.Default with + { + ImplementTypeOptions = new ImplementTypeOptions(PropertyGenerationBehavior: ImplementTypePropertyGenerationBehavior.PreferAutoProperties) + }; + await TestInRegularAndScript1Async( @"abstract class AbstractClass { @@ -1664,9 +1675,7 @@ class C : AbstractClass public override int ReadOnlyProp { get; } public override int ReadWriteProp { get; set; } public override int WriteOnlyProp { set => throw new System.NotImplementedException(); } -}", parameters: new TestParameters(options: Option( - ImplementTypeOptions.Metadata.PropertyGenerationBehavior, - ImplementTypePropertyGenerationBehavior.PreferAutoProperties))); +}", parameters: new TestParameters(codeActionOptions: options)); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementAbstractClass)] diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs index f072bdc5b8417..093dd5c8b8500 100644 --- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; @@ -7286,10 +7287,10 @@ void M() { } public int Prop => throw new System.NotImplementedException(); }", - Options = + CodeActionOptions = CodeActionOptions.Default with { - { ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd }, - }, + ImplementTypeOptions = new ImplementTypeOptions(InsertionBehavior: ImplementTypeInsertionBehavior.AtTheEnd) + } }.RunAsync(); } @@ -7461,10 +7462,10 @@ class Class : IInterface public int ReadWriteProp { get; set; } public int WriteOnlyProp { set => throw new System.NotImplementedException(); } }", - Options = + CodeActionOptions = CodeActionOptions.Default with { - { ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties }, - }, + ImplementTypeOptions = new ImplementTypeOptions(PropertyGenerationBehavior: ImplementTypePropertyGenerationBehavior.PreferAutoProperties) + } }.RunAsync(); } diff --git a/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs b/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs index 55b01386e6e7f..bc8bccac358ba 100644 --- a/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs +++ b/src/EditorFeatures/CSharpTest/Wrapping/AbstractWrappingTests.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.ImplementType; using Microsoft.CodeAnalysis.SymbolSearch; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Wrapping @@ -19,7 +20,9 @@ protected sealed override ImmutableArray MassageActions(ImmutableArr => FlattenActions(actions); private protected static CodeActionOptions GetIndentionColumn(int column) - => new(SymbolSearchOptions.Default, WrappingColumn: column); + => new(SymbolSearchOptions.Default, + ImplementTypeOptions.Default, + WrappingColumn: column); protected Task TestAllWrappingCasesAsync( string input, diff --git a/src/EditorFeatures/Core/ImplementType/ImplementTypeOptionsStorage.cs b/src/EditorFeatures/Core/ImplementType/ImplementTypeOptionsStorage.cs new file mode 100644 index 0000000000000..139acd773f8a2 --- /dev/null +++ b/src/EditorFeatures/Core/ImplementType/ImplementTypeOptionsStorage.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.ImplementType +{ + internal static class ImplementTypeOptionsStorage + { + public static ImplementTypeOptions GetImplementTypeOptions(this IGlobalOptionService globalOptions, string language) + => new( + InsertionBehavior: globalOptions.GetOption(InsertionBehavior, language), + PropertyGenerationBehavior: globalOptions.GetOption(PropertyGenerationBehavior, language)); + + private const string FeatureName = "ImplementTypeOptions"; + + public static readonly PerLanguageOption2 InsertionBehavior = + new(FeatureName, + "InsertionBehavior", + defaultValue: ImplementTypeOptions.Default.InsertionBehavior, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.ImplementTypeOptions.InsertionBehavior")); + + public static readonly PerLanguageOption2 PropertyGenerationBehavior = + new(FeatureName, + "PropertyGenerationBehavior", + defaultValue: ImplementTypeOptions.Default.PropertyGenerationBehavior, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.ImplementTypeOptions.PropertyGenerationBehavior")); + } +} diff --git a/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs b/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs index c4a5784c02f94..602b692419fdd 100644 --- a/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs +++ b/src/EditorFeatures/Core/Implementation/CodeActions/CodeActionOptionsStorage.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.ImplementType; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SymbolSearch; @@ -20,6 +21,7 @@ internal static CodeActionOptions GetBlockingCodeActionOptions(this IGlobalOptio private static CodeActionOptions GetCodeActionOptions(this IGlobalOptionService globalOptions, string language, bool isBlocking) => new( SearchOptions: globalOptions.GetSymbolSearchOptions(language), + ImplementTypeOptions: globalOptions.GetImplementTypeOptions(language), HideAdvancedMembers: globalOptions.GetOption(CompletionOptionsStorage.HideAdvancedMembers, language), IsBlocking: isBlocking); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs index aa218035e8803..7997a06582b08 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/AbstractCodeActionOrUserDiagnosticTest.cs @@ -376,6 +376,7 @@ internal Task TestInRegularAndScriptAsync( CodeActionPriority? priority = null, CompilationOptions compilationOptions = null, OptionsCollection options = null, + CodeActionOptions? codeActionOptions = null, object fixProviderData = null, ParseOptions parseOptions = null, string title = null, @@ -383,7 +384,7 @@ internal Task TestInRegularAndScriptAsync( { return TestInRegularAndScript1Async( initialMarkup, expectedMarkup, index, - new TestParameters(parseOptions, compilationOptions, options, CodeActionOptions.Default, fixProviderData, index, priority, title: title, testHost: testHost)); + new TestParameters(parseOptions, compilationOptions, options, codeActionOptions ?? CodeActionOptions.Default, fixProviderData, index, priority, title: title, testHost: testHost)); } internal Task TestInRegularAndScript1Async( diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs index a53c5d8cfd2c0..1aa10174c9630 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs @@ -16,6 +16,8 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Text; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Shared.Utilities; #if !CODE_STYLE using Roslyn.Utilities; @@ -73,6 +75,13 @@ public Test() /// internal OptionsCollection Options => _sharedState.Options; +#if !CODE_STYLE + internal CodeActionOptions CodeActionOptions + { + get => _sharedState.CodeActionOptions; + set => _sharedState.CodeActionOptions = value; + } +#endif /// public string? EditorConfig { @@ -97,7 +106,31 @@ protected override async Task RunImplAsync(CancellationToken cancellationToken = #if !CODE_STYLE protected override AnalyzerOptions GetAnalyzerOptions(Project project) - => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project.Solution); + => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project); + + protected override CodeFixContext CreateCodeFixContext(Document document, TextSpan span, ImmutableArray diagnostics, Action> registerCodeFix, CancellationToken cancellationToken) + => new(document, span, diagnostics, registerCodeFix, _sharedState.CodeActionOptions, cancellationToken); + + protected override FixAllContext CreateFixAllContext( + Document? document, + Project project, + CodeFixProvider codeFixProvider, + FixAllScope scope, + string? codeActionEquivalenceKey, + IEnumerable diagnosticIds, + FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, + CancellationToken cancellationToken) + => new(new FixAllState( + fixAllProvider: null, + document, + project, + codeFixProvider, + scope, + codeActionEquivalenceKey, + diagnosticIds, + fixAllDiagnosticProvider, + _ => _sharedState.CodeActionOptions), + new ProgressTracker(), cancellationToken); #endif protected override Diagnostic? TrySelectDiagnosticToFix(ImmutableArray fixableDiagnostics) diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs index 8012ac3db1bef..4218e06bae0d7 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs @@ -102,7 +102,7 @@ protected override ImmutableArray FilterCodeActions(ImmutableArray new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project.Solution); + => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project); /// /// The we want this test to run in. Defaults to if unspecified. diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs index c8679afa8e11c..b5229719dac52 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Testing.Verifiers; #if !CODE_STYLE +using Microsoft.CodeAnalysis.CodeActions; using Roslyn.Utilities; #endif @@ -45,6 +46,9 @@ public SharedVerifierState(AnalyzerTest test, string defaultFileE /// internal OptionsCollection Options { get; } +#if !CODE_STYLE + internal CodeActionOptions CodeActionOptions { get; set; } +#endif internal void Apply() { var (analyzerConfigSource, remainingOptions) = CodeFixVerifierHelper.ConvertOptionsToAnalyzerConfig(_defaultFileExt, EditorConfig, Options); diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs index 1ec477ed7b4a6..35e6c68133148 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs @@ -88,7 +88,7 @@ protected override async Task RunImplAsync(CancellationToken cancellationToken = #if !CODE_STYLE protected override AnalyzerOptions GetAnalyzerOptions(Project project) - => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project.Solution); + => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project); #endif protected override Diagnostic? TrySelectDiagnosticToFix(ImmutableArray fixableDiagnostics) diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs index 727be29d9a845..c27d8f86a8a86 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs @@ -76,7 +76,7 @@ protected override async Task RunImplAsync(CancellationToken cancellationToken) #if !CODE_STYLE protected override AnalyzerOptions GetAnalyzerOptions(Project project) - => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project.Solution); + => new WorkspaceAnalyzerOptions(base.GetAnalyzerOptions(project), project); #endif } } diff --git a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs index 9ad9af6c9bc95..3903bebbb08e5 100644 --- a/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Preview/PreviewWorkspaceTests.cs @@ -266,9 +266,9 @@ public void TestPreviewWorkspaceDoesNotLeakSolution() private static void ExecuteAnalyzers(PreviewWorkspace previewWorkspace, ImmutableArray analyzers) { var analyzerOptions = new AnalyzerOptions(additionalFiles: ImmutableArray.Empty); - var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(analyzerOptions, previewWorkspace.CurrentSolution); - var compilationWithAnalyzersOptions = new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false); var project = previewWorkspace.CurrentSolution.Projects.Single(); + var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(analyzerOptions, project); + var compilationWithAnalyzersOptions = new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, onAnalyzerException: null, concurrentAnalysis: false, logAnalyzerExecutionTime: false); var compilation = project.GetRequiredCompilationAsync(CancellationToken.None).Result; var compilationWithAnalyzers = new CompilationWithAnalyzers(compilation, analyzers, compilationWithAnalyzersOptions); var result = compilationWithAnalyzers.GetAnalysisResultAsync(CancellationToken.None).Result; diff --git a/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb b/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb index 3c7167f549ea6..1a179ce52af3a 100644 --- a/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb @@ -163,7 +163,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Utilities.CommandHandlers Return False End If - Dim options = ImplementTypeOptions.From(document.Project) + Dim options = _globalOptions.GetImplementTypeOptions(document.Project.Language) Dim newDocument = TryGetNewDocument(document, options, identifier, cancellationToken) If newDocument Is Nothing Then diff --git a/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb b/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb index 74bd618c3f1b0..0d19bbdab8213 100644 --- a/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ImplementAbstractClass/ImplementAbstractClassTests.vb @@ -2,10 +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. +Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics Imports Microsoft.CodeAnalysis.ImplementType +Imports Microsoft.CodeAnalysis.SymbolSearch Imports Microsoft.CodeAnalysis.VisualBasic.ImplementAbstractClass Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ImplementAbstractClass @@ -630,9 +632,9 @@ Class C Throw New System.NotImplementedException() End Set End Property -End Class", parameters:=New TestParameters(options:=[Option]( - ImplementTypeOptions.Metadata.PropertyGenerationBehavior, - ImplementTypePropertyGenerationBehavior.PreferAutoProperties))) +End Class", parameters:=New TestParameters(codeActionOptions:=New CodeActionOptions( + SearchOptions:=SymbolSearchOptions.Default, + ImplementTypeOptions:=New ImplementTypeOptions(PropertyGenerationBehavior:=ImplementTypePropertyGenerationBehavior.PreferAutoProperties)))) End Function End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb b/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb index f915762663037..31bd6cd863b48 100644 --- a/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ImplementInterface/ImplementInterfaceTests.vb @@ -2,10 +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. +Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics Imports Microsoft.CodeAnalysis.ImplementType +Imports Microsoft.CodeAnalysis.SymbolSearch Imports Microsoft.CodeAnalysis.VisualBasic.ImplementInterface Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ImplementInterface @@ -4646,9 +4648,9 @@ class Class Throw New System.NotImplementedException() End Set End Property -end class", parameters:=New TestParameters(options:=[Option]( - ImplementTypeOptions.Metadata.PropertyGenerationBehavior, - ImplementTypePropertyGenerationBehavior.PreferAutoProperties))) +end class", parameters:=New TestParameters(codeActionOptions:=New CodeActionOptions( + SearchOptions:=SymbolSearchOptions.Default, + ImplementTypeOptions:=New ImplementTypeOptions(PropertyGenerationBehavior:=ImplementTypePropertyGenerationBehavior.PreferAutoProperties)))) End Function End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb b/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb index 01c9887ad6705..0bd73c3c86d56 100644 --- a/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Wrapping/AbstractParameterWrappingTests.vb @@ -6,6 +6,7 @@ Imports System.Collections.Immutable Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings +Imports Microsoft.CodeAnalysis.ImplementType Imports Microsoft.CodeAnalysis.SymbolSearch Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Wrapping @@ -17,7 +18,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Wrapping End Function Private Protected Shared Function GetIndentionColumn(column As Integer) As CodeActionOptions - Return New CodeActionOptions(SymbolSearchOptions.Default, WrappingColumn:=column) + Return New CodeActionOptions( + SymbolSearchOptions.Default, + ImplementTypeOptions.Default, + WrappingColumn:=column) End Function Protected Function TestAllWrappingCasesAsync( diff --git a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs index ffaa2974eb11c..34e68a1f1bd02 100644 --- a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementInterfaceCodeFixProvider.cs @@ -51,11 +51,10 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var service = document.GetRequiredLanguageService(); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var options = ImplementTypeOptions.From(document.Project); var actions = token.Parent.GetAncestorsOrThis() .Where(_interfaceName) - .Select(n => service.GetCodeActions(document, options, model, n, cancellationToken)) + .Select(n => service.GetCodeActions(document, context.Options.ImplementTypeOptions, model, n, cancellationToken)) .FirstOrDefault(a => !a.IsEmpty); if (actions.IsDefaultOrEmpty) diff --git a/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs b/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs index fb06d30cab4e1..d25f30c640d95 100644 --- a/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs +++ b/src/Features/Core/Portable/Diagnostics/AnalyzerHelper.cs @@ -266,7 +266,7 @@ public static IEnumerable ToAnalyzerPerformanceInfo(thi // in IDE, we always set concurrentAnalysis == false otherwise, we can get into thread starvation due to // async being used with synchronous blocking concurrency. var analyzerOptions = new CompilationWithAnalyzersOptions( - options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution), + options: new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project), onAnalyzerException: null, analyzerExceptionFilter: GetAnalyzerExceptionFilter(), concurrentAnalysis: false, diff --git a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs index 180f99a8a86d7..f2d85b9126ebc 100644 --- a/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs +++ b/src/Features/Core/Portable/Diagnostics/WorkspaceAnalyzerOptions.cs @@ -21,26 +21,19 @@ internal sealed class WorkspaceAnalyzerOptions : AnalyzerOptions { private readonly Solution _solution; - // IDE options for each encountered language - private ImmutableDictionary _ideOptionsCache; + public IdeAnalyzerOptions IdeOptions { get; } - public WorkspaceAnalyzerOptions(AnalyzerOptions options, Solution solution) + public WorkspaceAnalyzerOptions(AnalyzerOptions options, Solution solution, IdeAnalyzerOptions ideOptions) : base(options.AdditionalFiles, options.AnalyzerConfigOptionsProvider) { _solution = solution; - _ideOptionsCache = ImmutableDictionary.Empty; + IdeOptions = ideOptions; } - public IdeAnalyzerOptions GetIdeOptions(string language) - => ImmutableInterlocked.GetOrAdd( - ref _ideOptionsCache, - language, - static (language, solution) => new IdeAnalyzerOptions( - FadeOutUnusedImports: solution.Options.GetOption(Fading.FadingOptions.Metadata.FadeOutUnusedImports, language), - FadeOutUnreachableCode: solution.Options.GetOption(Fading.FadingOptions.Metadata.FadeOutUnreachableCode, language), - ReportInvalidPlaceholdersInStringDotFormatCalls: solution.Options.GetOption(ValidateFormatString.ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, language), - ReportInvalidRegexPatterns: solution.Options.GetOption(Features.EmbeddedLanguages.RegularExpressions.LanguageServices.RegularExpressionsOptions.ReportInvalidRegexPatterns, language)), - _solution); + public WorkspaceAnalyzerOptions(AnalyzerOptions options, Project project) + : this(options, project.Solution, IdeAnalyzerOptions.FromProject(project)) + { + } public HostWorkspaceServices Services => _solution.Workspace.Services; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/AbstractRegexDiagnosticAnalyzer.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/AbstractRegexDiagnosticAnalyzer.cs index 9dc30d83f5350..6c792f00bea7e 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/AbstractRegexDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/AbstractRegexDiagnosticAnalyzer.cs @@ -43,7 +43,7 @@ public void Analyze(SemanticModelAnalysisContext context) var syntaxTree = semanticModel.SyntaxTree; var cancellationToken = context.CancellationToken; - var option = context.GetIdeOptions().ReportInvalidRegexPatterns; + var option = context.Options.GetIdeOptions().ReportInvalidRegexPatterns; if (!option) return; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegularExpressionsOptions.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegularExpressionsOptions.cs index b95e2f02834d9..6285a663b2816 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegularExpressionsOptions.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/LanguageServices/RegularExpressionsOptions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Composition; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options.Providers; @@ -17,7 +18,7 @@ internal class RegularExpressionsOptions new( nameof(RegularExpressionsOptions), nameof(ReportInvalidRegexPatterns), - defaultValue: true, + IdeAnalyzerOptions.Default.ReportInvalidRegexPatterns, storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.ReportInvalidRegexPatterns")); public static PerLanguageOption2 HighlightRelatedRegexComponentsUnderCursor = diff --git a/src/Features/Core/Portable/Fading/FadingOptions.cs b/src/Features/Core/Portable/Fading/FadingOptions.cs index 152f0217ad468..9349c606b1bd4 100644 --- a/src/Features/Core/Portable/Fading/FadingOptions.cs +++ b/src/Features/Core/Portable/Fading/FadingOptions.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Composition; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options.Providers; @@ -29,11 +30,11 @@ public Metadata() private const string FeatureName = "FadingOptions"; public static readonly PerLanguageOption2 FadeOutUnusedImports = new( - FeatureName, "FadeOutUnusedImports", defaultValue: true, + FeatureName, "FadeOutUnusedImports", IdeAnalyzerOptions.Default.FadeOutUnusedImports, storageLocation: new RoamingProfileStorageLocation($"TextEditor.%LANGUAGE%.Specific.FadeOutUnusedImports")); public static readonly PerLanguageOption2 FadeOutUnreachableCode = new( - FeatureName, "FadeOutUnreachableCode", defaultValue: true, + FeatureName, "FadeOutUnreachableCode", IdeAnalyzerOptions.Default.FadeOutUnreachableCode, storageLocation: new RoamingProfileStorageLocation($"TextEditor.%LANGUAGE%.Specific.FadeOutUnreachableCode")); } } diff --git a/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs b/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs index 6295abf449ee6..7ced21bcb6e74 100644 --- a/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs +++ b/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs @@ -41,9 +41,8 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) if (classNode == null) return; - var options = ImplementTypeOptions.From(document.Project); var data = await ImplementAbstractClassData.TryGetDataAsync( - document, classNode, GetClassIdentifier(classNode), options, cancellationToken).ConfigureAwait(false); + document, classNode, GetClassIdentifier(classNode), context.Options.ImplementTypeOptions, cancellationToken).ConfigureAwait(false); if (data == null) return; diff --git a/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs b/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs deleted file mode 100644 index 2a543b075ce87..0000000000000 --- a/src/Features/Core/Portable/ImplementType/ImplementTypeOptions.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Composition; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options.Providers; - -namespace Microsoft.CodeAnalysis.ImplementType -{ - internal readonly record struct ImplementTypeOptions( - ImplementTypeInsertionBehavior InsertionBehavior, - ImplementTypePropertyGenerationBehavior PropertyGenerationBehavior) - { - public static ImplementTypeOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static ImplementTypeOptions From(OptionSet options, string language) - => new( - InsertionBehavior: options.GetOption(Metadata.InsertionBehavior, language), - PropertyGenerationBehavior: options.GetOption(Metadata.PropertyGenerationBehavior, language)); - - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - InsertionBehavior, - PropertyGenerationBehavior); - - private const string FeatureName = "ImplementTypeOptions"; - - public static readonly PerLanguageOption2 InsertionBehavior = - new(FeatureName, - "InsertionBehavior", - defaultValue: ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.ImplementTypeOptions.InsertionBehavior")); - - public static readonly PerLanguageOption2 PropertyGenerationBehavior = - new(FeatureName, - "PropertyGenerationBehavior", - defaultValue: ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.ImplementTypeOptions.PropertyGenerationBehavior")); - } - } -} diff --git a/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb b/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb index 4d4081405d9db..07412631d3d9c 100644 --- a/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb @@ -58,11 +58,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ImplementInterface Return End If - Dim options = ImplementTypeOptions.From(document.Project) Dim service = document.GetLanguageService(Of IImplementInterfaceService)() Dim actions = service.GetCodeActions( document, - options, + context.Options.ImplementTypeOptions, Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False), typeNode, cancellationToken) diff --git a/src/Tools/AnalyzerRunner/DiagnosticAnalyzerRunner.cs b/src/Tools/AnalyzerRunner/DiagnosticAnalyzerRunner.cs index 2cc06bb7721bf..2c5720ee8f2a8 100644 --- a/src/Tools/AnalyzerRunner/DiagnosticAnalyzerRunner.cs +++ b/src/Tools/AnalyzerRunner/DiagnosticAnalyzerRunner.cs @@ -186,7 +186,7 @@ private static async Task TestDocumentPerformanceAs var stopwatch = PerformanceTracker.StartNew(); for (int i = 0; i < analyzerOptionsInternal.TestDocumentIterations; i++) { - var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution); + var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project); CompilationWithAnalyzers compilationWithAnalyzers = compilation.WithAnalyzers(languageAnalyzers, new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, null, analyzerOptionsInternal.RunConcurrent, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: analyzerOptionsInternal.ReportSuppressedDiagnostics)); SyntaxTree tree = await project.GetDocument(documentId).GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); @@ -393,7 +393,7 @@ private static async Task GetProjectAnalysisResultAsync( Compilation compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var newCompilation = compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(compilation.SyntaxTrees); - var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution); + var workspaceAnalyzerOptions = new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project); CompilationWithAnalyzers compilationWithAnalyzers = newCompilation.WithAnalyzers(analyzers, new CompilationWithAnalyzersOptions(workspaceAnalyzerOptions, null, analyzerOptionsInternal.RunConcurrent, logAnalyzerExecutionTime: true, reportSuppressedDiagnostics: analyzerOptionsInternal.ReportSuppressedDiagnostics)); var analystResult = await compilationWithAnalyzers.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false); return analystResult; diff --git a/src/Tools/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs b/src/Tools/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs new file mode 100644 index 0000000000000..f507e20d2587c --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CSharp.Formatting +{ + internal enum OmniSharpLabelPositionOptions + { + LeftMost = LabelPositionOptions.LeftMost, + OneLess = LabelPositionOptions.OneLess, + NoIndent = LabelPositionOptions.NoIndent + } + + internal enum OmniSharpBinaryOperatorSpacingOptions + { + Single = BinaryOperatorSpacingOptions.Single, + Ignore = BinaryOperatorSpacingOptions.Ignore, + Remove = BinaryOperatorSpacingOptions.Remove + } + + internal static class OmniSharpSyntaxFormattingOptionsFactory + { + public static OmniSharpSyntaxFormattingOptionsWrapper Create( + bool useTabs, + int tabSize, + int indentationSize, + string newLine, + bool separateImportDirectiveGroups, + bool spacingAfterMethodDeclarationName, + bool spaceWithinMethodDeclarationParenthesis, + bool spaceBetweenEmptyMethodDeclarationParentheses, + bool spaceAfterMethodCallName, + bool spaceWithinMethodCallParentheses, + bool spaceBetweenEmptyMethodCallParentheses, + bool spaceAfterControlFlowStatementKeyword, + bool spaceWithinExpressionParentheses, + bool spaceWithinCastParentheses, + bool spaceWithinOtherParentheses, + bool spaceAfterCast, + bool spaceBeforeOpenSquareBracket, + bool spaceBetweenEmptySquareBrackets, + bool spaceWithinSquareBrackets, + bool spaceAfterColonInBaseTypeDeclaration, + bool spaceAfterComma, + bool spaceAfterDot, + bool spaceAfterSemicolonsInForStatement, + bool spaceBeforeColonInBaseTypeDeclaration, + bool spaceBeforeComma, + bool spaceBeforeDot, + bool spaceBeforeSemicolonsInForStatement, + OmniSharpBinaryOperatorSpacingOptions spacingAroundBinaryOperator, + bool indentBraces, + bool indentBlock, + bool indentSwitchSection, + bool indentSwitchCaseSection, + bool indentSwitchCaseSectionWhenBlock, + OmniSharpLabelPositionOptions labelPositioning, + bool wrappingPreserveSingleLine, + bool wrappingKeepStatementsOnSingleLine, + bool newLinesForBracesInTypes, + bool newLinesForBracesInMethods, + bool newLinesForBracesInProperties, + bool newLinesForBracesInAccessors, + bool newLinesForBracesInAnonymousMethods, + bool newLinesForBracesInControlBlocks, + bool newLinesForBracesInAnonymousTypes, + bool newLinesForBracesInObjectCollectionArrayInitializers, + bool newLinesForBracesInLambdaExpressionBody, + bool newLineForElse, + bool newLineForCatch, + bool newLineForFinally, + bool newLineForMembersInObjectInit, + bool newLineForMembersInAnonymousTypes, + bool newLineForClausesInQuery) + => new(new CSharpSyntaxFormattingOptions( + useTabs: useTabs, + tabSize: tabSize, + indentationSize: indentationSize, + newLine: newLine, + separateImportDirectiveGroups: separateImportDirectiveGroups, + spacing: + (spacingAfterMethodDeclarationName ? SpacePlacement.AfterMethodDeclarationName : 0) | + (spaceBetweenEmptyMethodDeclarationParentheses ? SpacePlacement.BetweenEmptyMethodDeclarationParentheses : 0) | + (spaceWithinMethodDeclarationParenthesis ? SpacePlacement.WithinMethodDeclarationParenthesis : 0) | + (spaceAfterMethodCallName ? SpacePlacement.AfterMethodCallName : 0) | + (spaceBetweenEmptyMethodCallParentheses ? SpacePlacement.BetweenEmptyMethodCallParentheses : 0) | + (spaceWithinMethodCallParentheses ? SpacePlacement.WithinMethodCallParentheses : 0) | + (spaceAfterControlFlowStatementKeyword ? SpacePlacement.AfterControlFlowStatementKeyword : 0) | + (spaceWithinExpressionParentheses ? SpacePlacement.WithinExpressionParentheses : 0) | + (spaceWithinCastParentheses ? SpacePlacement.WithinCastParentheses : 0) | + (spaceBeforeSemicolonsInForStatement ? SpacePlacement.BeforeSemicolonsInForStatement : 0) | + (spaceAfterSemicolonsInForStatement ? SpacePlacement.AfterSemicolonsInForStatement : 0) | + (spaceWithinOtherParentheses ? SpacePlacement.WithinOtherParentheses : 0) | + (spaceAfterCast ? SpacePlacement.AfterCast : 0) | + (spaceBeforeOpenSquareBracket ? SpacePlacement.BeforeOpenSquareBracket : 0) | + (spaceBetweenEmptySquareBrackets ? SpacePlacement.BetweenEmptySquareBrackets : 0) | + (spaceWithinSquareBrackets ? SpacePlacement.WithinSquareBrackets : 0) | + (spaceAfterColonInBaseTypeDeclaration ? SpacePlacement.AfterColonInBaseTypeDeclaration : 0) | + (spaceBeforeColonInBaseTypeDeclaration ? SpacePlacement.BeforeColonInBaseTypeDeclaration : 0) | + (spaceAfterComma ? SpacePlacement.AfterComma : 0) | + (spaceBeforeComma ? SpacePlacement.BeforeComma : 0) | + (spaceAfterDot ? SpacePlacement.AfterDot : 0) | + (spaceBeforeDot ? SpacePlacement.BeforeDot : 0), + spacingAroundBinaryOperator: (BinaryOperatorSpacingOptions)spacingAroundBinaryOperator, + newLines: + (newLineForMembersInObjectInit ? NewLinePlacement.BeforeMembersInObjectInitializers : 0) | + (newLineForMembersInAnonymousTypes ? NewLinePlacement.BeforeMembersInAnonymousTypes : 0) | + (newLineForElse ? NewLinePlacement.BeforeElse : 0) | + (newLineForCatch ? NewLinePlacement.BeforeCatch : 0) | + (newLineForFinally ? NewLinePlacement.BeforeFinally : 0) | + (newLinesForBracesInTypes ? NewLinePlacement.BeforeOpenBraceInTypes : 0) | + (newLinesForBracesInAnonymousTypes ? NewLinePlacement.BeforeOpenBraceInAnonymousTypes : 0) | + (newLinesForBracesInObjectCollectionArrayInitializers ? NewLinePlacement.BeforeOpenBraceInObjectCollectionArrayInitializers : 0) | + (newLinesForBracesInProperties ? NewLinePlacement.BeforeOpenBraceInProperties : 0) | + (newLinesForBracesInMethods ? NewLinePlacement.BeforeOpenBraceInMethods : 0) | + (newLinesForBracesInAccessors ? NewLinePlacement.BeforeOpenBraceInAccessors : 0) | + (newLinesForBracesInAnonymousMethods ? NewLinePlacement.BeforeOpenBraceInAnonymousMethods : 0) | + (newLinesForBracesInLambdaExpressionBody ? NewLinePlacement.BeforeOpenBraceInLambdaExpressionBody : 0) | + (newLinesForBracesInControlBlocks ? NewLinePlacement.BeforeOpenBraceInControlBlocks : 0) | + (newLineForClausesInQuery ? NewLinePlacement.BetweenQueryExpressionClauses : 0), + labelPositioning: (LabelPositionOptions)labelPositioning, + indentation: + (indentBraces ? IndentationPlacement.Braces : 0) | + (indentBlock ? IndentationPlacement.BlockContents : 0) | + (indentSwitchCaseSection ? IndentationPlacement.SwitchCaseContents : 0) | + (indentSwitchCaseSectionWhenBlock ? IndentationPlacement.SwitchCaseContentsWhenBlock : 0) | + (indentSwitchSection ? IndentationPlacement.SwitchSection : 0), + wrappingKeepStatementsOnSingleLine: wrappingKeepStatementsOnSingleLine, + wrappingPreserveSingleLine: wrappingPreserveSingleLine)); + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/Analyzers/OmniSharpWorkspaceAnalyzerOptionsFactory.cs b/src/Tools/ExternalAccess/OmniSharp/Analyzers/OmniSharpWorkspaceAnalyzerOptionsFactory.cs new file mode 100644 index 0000000000000..bf6932ceaea87 --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/Analyzers/OmniSharpWorkspaceAnalyzerOptionsFactory.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Analyzers +{ + internal static class OmniSharpWorkspaceAnalyzerOptionsFactory + { + public static AnalyzerOptions Create(Solution solution, AnalyzerOptions options) + => new WorkspaceAnalyzerOptions(options, solution, IdeAnalyzerOptions.Default); + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs new file mode 100644 index 0000000000000..b4f31dd36f8ba --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Threading; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; +using Microsoft.CodeAnalysis.ImplementType; +using Microsoft.CodeAnalysis.SymbolSearch; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions +{ + internal static class OmniSharpCodeFixContextFactory + { + public static CodeFixContext Create( + Document document, + TextSpan span, + ImmutableArray diagnostics, + Action> registerCodeFix, + OmniSharpImplementTypeOptions implementTypeOptions, + CancellationToken cancellationToken) + => new(document, span, diagnostics, registerCodeFix, GetCodeActionOptions(implementTypeOptions), cancellationToken); + + private static CodeActionOptions GetCodeActionOptions(OmniSharpImplementTypeOptions implementTypeOptions) + => new( + SymbolSearchOptions.Default, + new ImplementTypeOptions( + InsertionBehavior: (ImplementTypeInsertionBehavior)implementTypeOptions.InsertionBehavior, + PropertyGenerationBehavior: (ImplementTypePropertyGenerationBehavior)implementTypeOptions.PropertyGenerationBehavior)); + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs new file mode 100644 index 0000000000000..29cd5c5a935a4 --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.DocumentationComments; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.DocumentationComments +{ + internal readonly struct OmniSharpDocumentationCommentOptionsWrapper + { + internal readonly DocumentationCommentOptions UnderlyingObject; + + internal OmniSharpDocumentationCommentOptionsWrapper(DocumentationCommentOptions underlyingObject) + => UnderlyingObject = underlyingObject; + + public OmniSharpDocumentationCommentOptionsWrapper( + bool autoXmlDocCommentGeneration, + int tabSize, + bool useTabs, + string newLine) + : this(new(autoXmlDocCommentGeneration, tabSize, useTabs, newLine)) + { + } + + public static async ValueTask FromDocumentAsync( + Document document, + bool autoXmlDocCommentGeneration, + CancellationToken cancellationToken) + { + var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + return new(DocumentationCommentOptions.From(documentOptions) with { AutoXmlDocCommentGeneration = autoXmlDocCommentGeneration }); + } + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentSnippet.cs b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentSnippet.cs new file mode 100644 index 0000000000000..8f1080792120a --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentSnippet.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.DocumentationComments +{ + internal sealed class OmniSharpDocumentationCommentSnippet + { + /// + /// The span in the original text that should be replaced with the documentation comment. + /// + public TextSpan SpanToReplace { get; } + + /// + /// The documentation comment text to replace the span with + /// + public string SnippetText { get; } + + /// + /// The offset within where the caret should be positioned after replacement + /// + public int CaretOffset { get; } + + internal OmniSharpDocumentationCommentSnippet(TextSpan spanToReplace, string snippetText, int caretOffset) + { + SpanToReplace = spanToReplace; + SnippetText = snippetText; + CaretOffset = caretOffset; + } + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs index cd74c914f1153..33c5570262629 100644 --- a/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs +++ b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentsSnippetService.cs @@ -5,7 +5,6 @@ using System.Threading; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -18,25 +17,11 @@ internal static class OmniSharpDocumentationCommentsSnippetService SyntaxTree syntaxTree, SourceText text, int position, - DocumentOptionSet options, + OmniSharpDocumentationCommentOptionsWrapper options, CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - var docCommentOptions = DocumentationCommentOptions.From(options); - return Translate(service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, text, position, docCommentOptions, cancellationToken)); - } - - public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetOnCommandInvoke( - Document document, - SyntaxTree syntaxTree, - SourceText text, - int position, - DocumentOptionSet options, - CancellationToken cancellationToken) - { - var service = document.GetRequiredLanguageService(); - var docCommentOptions = DocumentationCommentOptions.From(options); - return Translate(service.GetDocumentationCommentSnippetOnCommandInvoke(syntaxTree, text, position, docCommentOptions, cancellationToken)); + return Translate(service.GetDocumentationCommentSnippetOnCharacterTyped(syntaxTree, text, position, options.UnderlyingObject, cancellationToken)); } public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetOnEnterTyped( @@ -44,51 +29,14 @@ internal static class OmniSharpDocumentationCommentsSnippetService SyntaxTree syntaxTree, SourceText text, int position, - DocumentOptionSet options, + OmniSharpDocumentationCommentOptionsWrapper options, CancellationToken cancellationToken) { var service = document.GetRequiredLanguageService(); - var docCommentOptions = DocumentationCommentOptions.From(options); - return Translate(service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, text, position, docCommentOptions, cancellationToken)); - } - - public static OmniSharpDocumentationCommentSnippet? GetDocumentationCommentSnippetFromPreviousLine( - Document document, - DocumentOptionSet options, - TextLine currentLine, - TextLine previousLine) - { - var service = document.GetRequiredLanguageService(); - var docCommentOptions = DocumentationCommentOptions.From(options); - return Translate(service.GetDocumentationCommentSnippetFromPreviousLine(docCommentOptions, currentLine, previousLine)); + return Translate(service.GetDocumentationCommentSnippetOnEnterTyped(syntaxTree, text, position, options.UnderlyingObject, cancellationToken)); } private static OmniSharpDocumentationCommentSnippet? Translate(DocumentationCommentSnippet? result) => result == null ? null : new(result.SpanToReplace, result.SnippetText, result.CaretOffset); } - - internal sealed class OmniSharpDocumentationCommentSnippet - { - /// - /// The span in the original text that should be replaced with the documentation comment. - /// - public TextSpan SpanToReplace { get; } - - /// - /// The documentation comment text to replace the span with - /// - public string SnippetText { get; } - - /// - /// The offset within where the caret should be positioned after replacement - /// - public int CaretOffset { get; } - - internal OmniSharpDocumentationCommentSnippet(TextSpan spanToReplace, string snippetText, int caretOffset) - { - SpanToReplace = spanToReplace; - SnippetText = snippetText; - CaretOffset = caretOffset; - } - } } diff --git a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs new file mode 100644 index 0000000000000..cadbac84be4ea --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpFormatter.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.OrganizeImports; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; + +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.UnderlyingObject, rules: null, cancellationToken); + + public static async Task OrganizeImportsAsync(Document document, OmniSharpOrganizeImportsOptionsWrapper options, CancellationToken cancellationToken) + { + var organizeImportsService = document.GetLanguageService(); + if (organizeImportsService is null) + { + return document; + } + + return await organizeImportsService.OrganizeImportsAsync(document, options.UnderlyingObject, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs new file mode 100644 index 0000000000000..e6721fffbdcd7 --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.OrganizeImports; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting +{ + internal readonly struct OmniSharpOrganizeImportsOptionsWrapper + { + internal readonly OrganizeImportsOptions UnderlyingObject; + + private OmniSharpOrganizeImportsOptionsWrapper(OrganizeImportsOptions underlyingObject) + { + UnderlyingObject = underlyingObject; + } + + public OmniSharpOrganizeImportsOptionsWrapper( + bool placeSystemNamespaceFirst, + bool separateImportDirectiveGroups, + string newLine) : this(new OrganizeImportsOptions( + placeSystemNamespaceFirst, + separateImportDirectiveGroups, + newLine)) + { + } + + public static async ValueTask FromDocumentAsync(Document document, CancellationToken cancellationToken) + => new(await OrganizeImportsOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false)); + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs new file mode 100644 index 0000000000000..5ff2ccf0b9e0f --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Formatting; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting +{ + internal readonly record struct OmniSharpSyntaxFormattingOptionsWrapper + { + internal readonly SyntaxFormattingOptions UnderlyingObject; + + internal OmniSharpSyntaxFormattingOptionsWrapper(SyntaxFormattingOptions underlyingObject) + => UnderlyingObject = underlyingObject; + + public static async ValueTask FromDocumentAsync(Document document, CancellationToken cancellationToken) + { + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + return new OmniSharpSyntaxFormattingOptionsWrapper(options); + } + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs b/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs index 6a7c48179da23..9d4b8ab5cad31 100644 --- a/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs +++ b/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs @@ -3,24 +3,12 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.ImplementType; -using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType { - internal static class OmniSharpImplementTypeOptions - { - public static OmniSharpImplementTypeInsertionBehavior GetInsertionBehavior(OptionSet options, string language) - => (OmniSharpImplementTypeInsertionBehavior)options.GetOption(ImplementTypeOptions.Metadata.InsertionBehavior, language); - - public static OptionSet SetInsertionBehavior(OptionSet options, string language, OmniSharpImplementTypeInsertionBehavior value) - => options.WithChangedOption(ImplementTypeOptions.Metadata.InsertionBehavior, language, (ImplementTypeInsertionBehavior)value); - - public static OmniSharpImplementTypePropertyGenerationBehavior GetPropertyGenerationBehavior(OptionSet options, string language) - => (OmniSharpImplementTypePropertyGenerationBehavior)options.GetOption(ImplementTypeOptions.Metadata.PropertyGenerationBehavior, language); - - public static OptionSet SetPropertyGenerationBehavior(OptionSet options, string language, OmniSharpImplementTypePropertyGenerationBehavior value) - => options.WithChangedOption(ImplementTypeOptions.Metadata.PropertyGenerationBehavior, language, (ImplementTypePropertyGenerationBehavior)value); - } + internal readonly record struct OmniSharpImplementTypeOptions( + OmniSharpImplementTypeInsertionBehavior InsertionBehavior, + OmniSharpImplementTypePropertyGenerationBehavior PropertyGenerationBehavior); internal enum OmniSharpImplementTypeInsertionBehavior { diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index fbc2016f006c2..d21c61cedf71f 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -117,11 +117,11 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptions.Metadata.DontPutOutOrRefOnStruct, LanguageNames.CSharp); - BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.CSharp); - BindToOption(at_the_end, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.CSharp); + BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptionsStorage.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.CSharp); + BindToOption(at_the_end, ImplementTypeOptionsStorage.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.CSharp); - BindToOption(prefer_throwing_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.CSharp); - BindToOption(prefer_auto_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.CSharp); + BindToOption(prefer_throwing_properties, ImplementTypeOptionsStorage.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.CSharp); + BindToOption(prefer_auto_properties, ImplementTypeOptionsStorage.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.CSharp); BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.CSharp); diff --git a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs index ae55dec64f47b..71012b803c84f 100644 --- a/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/VisualStudioDiagnosticAnalyzerExecutorTests.cs @@ -162,7 +162,7 @@ void Method() var compilationWithAnalyzers = (await project.GetCompilationAsync()).WithAnalyzers( analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray(), - new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution)); + new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project)); // no result for open file only analyzer unless forced var result = await runner.AnalyzeProjectAsync(project, compilationWithAnalyzers, forceExecuteAllAnalyzers: false, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); @@ -204,7 +204,7 @@ void Method() var analyzers = analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray(); var compilationWithAnalyzers = (await project.GetCompilationAsync()) - .WithAnalyzers(analyzers, new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution)); + .WithAnalyzers(analyzers, new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project)); var result = await runner.AnalyzeProjectAsync(project, compilationWithAnalyzers, forceExecuteAllAnalyzers: false, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken: CancellationToken.None); @@ -229,7 +229,7 @@ private static async Task AnalyzeAsync(TestWorkspace w var analyzerDriver = (await project.GetCompilationAsync()).WithAnalyzers( analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray(), - new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution)); + new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project)); var result = await executor.AnalyzeProjectAsync(project, analyzerDriver, forceExecuteAllAnalyzers: true, logPerformanceInfo: false, getTelemetryInfo: false, cancellationToken); diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index 59c1f2df469c8..3286c99c8c72c 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -137,11 +137,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptions.Metadata.DontPutOutOrRefOnStruct, LanguageNames.VisualBasic) ' Implement Interface or Abstract Class - BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.VisualBasic) - BindToOption(at_the_end, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.VisualBasic) + BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptionsStorage.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.VisualBasic) + BindToOption(at_the_end, ImplementTypeOptionsStorage.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.VisualBasic) - BindToOption(prefer_throwing_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.VisualBasic) - BindToOption(prefer_auto_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.VisualBasic) + BindToOption(prefer_throwing_properties, ImplementTypeOptionsStorage.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.VisualBasic) + BindToOption(prefer_auto_properties, ImplementTypeOptionsStorage.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.VisualBasic) ' Inline hints BindToOption(DisplayAllHintsWhilePressingAltF1, InlineHintsViewOptions.DisplayAllHintsWhilePressingAltF1) diff --git a/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlOrganizeImportsService.cs b/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlOrganizeImportsService.cs index 79a4fd226ad64..a3fdda566823b 100644 --- a/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlOrganizeImportsService.cs +++ b/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlOrganizeImportsService.cs @@ -28,11 +28,9 @@ public XamlOrganizeImportsService(IXamlOrganizeNamespacesService organizeService _organizeService = organizeService; } - public async Task OrganizeImportsAsync(Document document, CancellationToken cancellationToken) + public async Task OrganizeImportsAsync(Document document, OrganizeImportsOptions options, CancellationToken cancellationToken) { - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var placeSystemNamespaceFirst = options.GetOption(GenerationOptions.PlaceSystemNamespaceFirst); - return await _organizeService.OrganizeNamespacesAsync(document, placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false) ?? document; + return await _organizeService.OrganizeNamespacesAsync(document, options.PlaceSystemNamespaceFirst, cancellationToken).ConfigureAwait(false) ?? document; } public string SortImportsDisplayStringWithAccelerator diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index 24fc04129f356..fb0483f25a55e 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -46,6 +46,7 @@ + diff --git a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs index 1b71495259de6..c9d1bc1adb201 100644 --- a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs @@ -4,8 +4,10 @@ using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; +using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -13,7 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp.OrganizeImports { internal partial class CSharpOrganizeImportsService { - private class Rewriter : CSharpSyntaxRewriter + private sealed class Rewriter : CSharpSyntaxRewriter { private readonly bool _placeSystemNamespaceFirst; private readonly bool _separateGroups; @@ -21,13 +23,11 @@ private class Rewriter : CSharpSyntaxRewriter public readonly IList TextChanges = new List(); - public Rewriter(bool placeSystemNamespaceFirst, - bool separateGroups, - SyntaxTrivia newLineTrivia) + public Rewriter(OrganizeImportsOptions options) { - _placeSystemNamespaceFirst = placeSystemNamespaceFirst; - _separateGroups = separateGroups; - _newLineTrivia = newLineTrivia; + _placeSystemNamespaceFirst = options.PlaceSystemNamespaceFirst; + _separateGroups = options.SeparateImportDirectiveGroups; + _newLineTrivia = CSharpSyntaxGeneratorInternal.Instance.EndOfLine(options.NewLine); } public override SyntaxNode VisitCompilationUnit(CompilationUnitSyntax node) diff --git a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs index d659b6b46b022..e7fc05da2fca2 100644 --- a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs +++ b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs @@ -25,16 +25,11 @@ public CSharpOrganizeImportsService() { } - public async Task OrganizeImportsAsync(Document document, CancellationToken cancellationToken) + public async Task OrganizeImportsAsync(Document document, OrganizeImportsOptions options, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var placeSystemNamespaceFirst = options.GetOption(GenerationOptions.PlaceSystemNamespaceFirst); - var blankLineBetweenGroups = options.GetOption(GenerationOptions.SeparateImportDirectiveGroups); - var newLineTrivia = CSharpSyntaxGeneratorInternal.Instance.EndOfLine(options.GetOption(FormattingOptions2.NewLine)); - - var rewriter = new Rewriter(placeSystemNamespaceFirst, blankLineBetweenGroups, newLineTrivia); + var rewriter = new Rewriter(options); var newRoot = rewriter.Visit(root); return document.WithSyntaxRoot(newRoot); diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 95327a7f3fbe5..5574dc9dd9fb9 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -310,15 +310,16 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, IEnum /// The document to organize. /// The cancellation token that the operation will observe. /// The document with organized imports. If the language does not support organizing imports, or if no changes were made, this method returns . - public static Task OrganizeImportsAsync(Document document, CancellationToken cancellationToken = default) + public static async Task OrganizeImportsAsync(Document document, CancellationToken cancellationToken = default) { var organizeImportsService = document.GetLanguageService(); if (organizeImportsService is null) { - return Task.FromResult(document); + return document; } - return organizeImportsService.OrganizeImportsAsync(document, cancellationToken); + var options = await OrganizeImportsOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + return await organizeImportsService.OrganizeImportsAsync(document, options, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Features/Core/Portable/ImplementType/ImplementTypeInsertionBehavior.cs b/src/Workspaces/Core/Portable/ImplementType/ImplementTypeInsertionBehavior.cs similarity index 100% rename from src/Features/Core/Portable/ImplementType/ImplementTypeInsertionBehavior.cs rename to src/Workspaces/Core/Portable/ImplementType/ImplementTypeInsertionBehavior.cs diff --git a/src/Workspaces/Core/Portable/ImplementType/ImplementTypeOptions.cs b/src/Workspaces/Core/Portable/ImplementType/ImplementTypeOptions.cs new file mode 100644 index 0000000000000..307a8ffc66f5b --- /dev/null +++ b/src/Workspaces/Core/Portable/ImplementType/ImplementTypeOptions.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.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.ImplementType +{ + [DataContract] + internal readonly record struct ImplementTypeOptions( + [property: DataMember(Order = 0)] ImplementTypeInsertionBehavior InsertionBehavior = ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, + [property: DataMember(Order = 1)] ImplementTypePropertyGenerationBehavior PropertyGenerationBehavior = ImplementTypePropertyGenerationBehavior.PreferThrowingProperties) + { + public ImplementTypeOptions() + : this(InsertionBehavior: ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind) + { + } + + public static readonly ImplementTypeOptions Default = new(); + } +} diff --git a/src/Features/Core/Portable/ImplementType/ImplementTypePropertyGenerationBehavior.cs b/src/Workspaces/Core/Portable/ImplementType/ImplementTypePropertyGenerationBehavior.cs similarity index 100% rename from src/Features/Core/Portable/ImplementType/ImplementTypePropertyGenerationBehavior.cs rename to src/Workspaces/Core/Portable/ImplementType/ImplementTypePropertyGenerationBehavior.cs diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index 69768a3e7c101..161653267be59 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -44,6 +44,7 @@ + @@ -80,7 +81,7 @@ - + - - false - true - false - CurrentRuntime - - - true - - diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props index b422825f395e9..043251f4afb09 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -183,8 +183,6 @@ - - diff --git a/src/Compilers/Core/Portable/CaseInsensitiveComparison.cs b/src/Compilers/Core/Portable/CaseInsensitiveComparison.cs index 865c51408aaf7..406fd8b909efc 100644 --- a/src/Compilers/Core/Portable/CaseInsensitiveComparison.cs +++ b/src/Compilers/Core/Portable/CaseInsensitiveComparison.cs @@ -117,7 +117,6 @@ public override int Compare(string? str1, string? str2) return str1.Length - str2.Length; } -#if !NET20 public int Compare(ReadOnlySpan str1, ReadOnlySpan str2) { int len = Math.Min(str1.Length, str2.Length); @@ -133,7 +132,6 @@ public int Compare(ReadOnlySpan str1, ReadOnlySpan str2) // return the smaller string, or 0 if they are equal in length return str1.Length - str2.Length; } -#endif private static bool AreEqualLowerUnicode(char c1, char c2) { @@ -168,7 +166,6 @@ public override bool Equals(string? str1, string? str2) return true; } -#if !NET20 public bool Equals(ReadOnlySpan str1, ReadOnlySpan str2) { if (str1.Length != str2.Length) @@ -186,7 +183,6 @@ public bool Equals(ReadOnlySpan str1, ReadOnlySpan str2) return true; } -#endif public static bool EndsWith(string value, string possibleEnd) { @@ -293,7 +289,6 @@ public override int GetHashCode(string str) /// public static bool Equals(string left, string right) => s_comparer.Equals(left, right); -#if !NET20 /// /// Determines if two strings are equal according to Unicode rules for case-insensitive /// identifier comparison (lower-case mapping). @@ -305,7 +300,6 @@ public override int GetHashCode(string str) /// These are also the rules used for VB identifier comparison. /// public static bool Equals(ReadOnlySpan left, ReadOnlySpan right) => s_comparer.Equals(left, right); -#endif /// /// Determines if the string 'value' end with string 'possibleEnd'. @@ -335,7 +329,6 @@ public override int GetHashCode(string str) /// public static int Compare(string left, string right) => s_comparer.Compare(left, right); -#if !NET20 /// /// Compares two strings according to the Unicode rules for case-insensitive /// identifier comparison (lower-case mapping). @@ -347,7 +340,6 @@ public override int GetHashCode(string str) /// These are also the rules used for VB identifier comparison. /// public static int Compare(ReadOnlySpan left, ReadOnlySpan right) => s_comparer.Compare(left, right); -#endif /// /// Gets a case-insensitive hash code for Unicode identifiers. diff --git a/src/Compilers/Core/Portable/DiaSymReader/SymUnmanagedFactory.cs b/src/Compilers/Core/Portable/DiaSymReader/SymUnmanagedFactory.cs index 583a06894dbc4..8e5e428a6cac2 100644 --- a/src/Compilers/Core/Portable/DiaSymReader/SymUnmanagedFactory.cs +++ b/src/Compilers/Core/Portable/DiaSymReader/SymUnmanagedFactory.cs @@ -60,7 +60,6 @@ internal static string DiaSymReaderModuleName private delegate void NativeFactory(ref Guid id, [MarshalAs(UnmanagedType.IUnknown)] out object instance); -#if !NET20 private static readonly Lazy> s_lazyGetEnvironmentVariable = new Lazy>(() => { try @@ -80,18 +79,13 @@ internal static string DiaSymReaderModuleName return null; }); -#endif // internal for testing internal static string GetEnvironmentVariable(string name) { try { -#if NET20 - return Environment.GetEnvironmentVariable(name); -#else return s_lazyGetEnvironmentVariable.Value?.Invoke(name); -#endif } catch { @@ -122,7 +116,7 @@ private static object TryLoadFromAlternativePath(Guid clsid, string factoryName) Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } -#if NET20 || NETSTANDARD1_1 +#if NETSTANDARD1_1 var creator = (NativeFactory)Marshal.GetDelegateForFunctionPointer(createAddress, typeof(NativeFactory)); #else var creator = Marshal.GetDelegateForFunctionPointer(createAddress); @@ -144,11 +138,7 @@ private static Type GetComTypeType(ref Type lazyType, Guid clsid) { if (lazyType == null) { -#if NET20 - lazyType = Type.GetTypeFromCLSID(clsid); -#else lazyType = Marshal.GetTypeFromCLSID(clsid); -#endif } return lazyType; diff --git a/src/Compilers/Core/Portable/InternalUtilities/Debug.cs b/src/Compilers/Core/Portable/InternalUtilities/Debug.cs index d56abfaf0631b..d2833310c6f51 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/Debug.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/Debug.cs @@ -38,7 +38,7 @@ public static void AssertNotNull([NotNull] T value) [Conditional("DEBUG")] internal static void AssertOrFailFast([DoesNotReturnIf(false)] bool condition, string? message = null) { -#if NET20 || NETSTANDARD1_3 +#if NETSTANDARD1_3 Debug.Assert(condition); #else if (!condition) diff --git a/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs b/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs index b53cf5cea8948..5947d753111a6 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FailFast.cs @@ -31,14 +31,12 @@ internal static void OnFatalException(Exception exception) Debugger.Break(); } -#if !NET20 // don't fail fast with an aggregate exception that is masking true exception if (exception is AggregateException aggregate && aggregate.InnerExceptions.Count == 1) { exception = aggregate.InnerExceptions[0]; } -#endif DumpStackTrace(exception: exception); Environment.FailFast(exception.ToString(), exception); @@ -78,11 +76,9 @@ internal static void DumpStackTrace(Exception? exception = null, string? message } } -#if !NET20 Console.WriteLine("Stack trace of handler"); var stackTrace = new StackTrace(); Console.WriteLine(stackTrace.ToString()); -#endif Console.Out.Flush(); } diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs index af766990930ed..2f3390cc0cd62 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs @@ -9,11 +9,6 @@ using System.Threading; using Roslyn.Utilities; -#if NET20 -// Some APIs referenced by documentation comments are not available on .NET Framework 2.0. -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved -#endif - namespace Microsoft.CodeAnalysis.ErrorReporting { internal static class FatalError @@ -57,11 +52,6 @@ public static void OverwriteHandler(ErrorReporterHandler? value) s_handler = value; } - // In the result provider, we aren't copying our handler to somewhere else, so we don't - // need this method. It's too much of a challenge to shared code to work in - // old versions of the runtime since APIs changed over time. -#if !NET20 - /// /// Copies the handler in this instance to the linked copy of this type in this other assembly. /// @@ -85,8 +75,6 @@ public static void CopyHandlerTo(Assembly assembly) } } -#endif - /// /// Use in an exception filter to report an error without catching the exception. /// The error is reported by calling . @@ -241,12 +229,10 @@ private static void Report(Exception exception, ErrorSeverity severity = ErrorSe return; } -#if !NET20 if (exception is AggregateException aggregate && aggregate.InnerExceptions.Count == 1 && aggregate.InnerExceptions[0].Data[s_reportedMarker] != null) { return; } -#endif if (!exception.Data.IsReadOnly) { diff --git a/src/Compilers/Core/Portable/InternalUtilities/RoslynString.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynString.cs index e0a485898b521..f722791434fc2 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/RoslynString.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynString.cs @@ -12,10 +12,8 @@ internal static class RoslynString public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] string? value) => string.IsNullOrEmpty(value); -#if !NET20 /// public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] string? value) => string.IsNullOrWhiteSpace(value); -#endif } } diff --git a/src/Dependencies/PooledObjects/ObjectPool`1.cs b/src/Dependencies/PooledObjects/ObjectPool`1.cs index 8e99345c053ea..d9dca9dfb14f8 100644 --- a/src/Dependencies/PooledObjects/ObjectPool`1.cs +++ b/src/Dependencies/PooledObjects/ObjectPool`1.cs @@ -22,10 +22,6 @@ namespace Microsoft.CodeAnalysis.PooledObjects { -#if NET20 - internal delegate TReturn Func(TArg arg); -#endif - /// /// Generic implementation of object pooling pattern with predefined pool size limit. The main /// purpose is that limited number of frequently used objects can be kept in the pool for diff --git a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/NetFX20/AssemblyInfo.cs b/src/ExpressionEvaluator/CSharp/Source/ResultProvider/NetFX20/AssemblyInfo.cs deleted file mode 100644 index 8aa94524789fc..0000000000000 --- a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/NetFX20/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -[assembly: System.Runtime.Versioning.TargetFramework(".NETFramework,Version=v2.0")] diff --git a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/NetFX20/CSharpResultProvider.NetFX20.csproj b/src/ExpressionEvaluator/CSharp/Source/ResultProvider/NetFX20/CSharpResultProvider.NetFX20.csproj deleted file mode 100644 index 86a726ea33c7a..0000000000000 --- a/src/ExpressionEvaluator/CSharp/Source/ResultProvider/NetFX20/CSharpResultProvider.NetFX20.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - - - Library - Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator - Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider - net20 - true - full - - - - Compiler\Parser\RoslynString.cs - - - Compiler\Parser\UnicodeCharacterUtilities.cs - - - Compiler\Parser\CharacterInfo.cs - - - Compiler\Syntax\SyntaxKind.cs - - - Compiler\Syntax\SyntaxKindFacts.cs - - - Compiler\SymbolDisplay\ObjectDisplay.cs - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj index e1f9cf121dabc..8b9e968f1b1bd 100644 --- a/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj +++ b/src/ExpressionEvaluator/Core/Source/FunctionResolver/Microsoft.CodeAnalysis.FunctionResolver.csproj @@ -6,7 +6,7 @@ Microsoft.CodeAnalysis.ExpressionEvaluator Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver true - net45;netstandard2.0 + netstandard2.0 diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/NetFX20/Helpers/Placeholders.cs b/src/ExpressionEvaluator/Core/Source/ResultProvider/NetFX20/Helpers/Placeholders.cs deleted file mode 100644 index 296e4ef118261..0000000000000 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/NetFX20/Helpers/Placeholders.cs +++ /dev/null @@ -1,113 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable disable - -using System; -using System.Reflection; -using System.Runtime.Versioning; - -[assembly: TargetFramework(".NETFramework,Version=v2.0")] - -namespace Microsoft.CodeAnalysis -{ - internal static class ReflectionTypeExtensions - { - // Replaces a missing 4.5 method. - internal static Type GetTypeInfo(this Type type) - { - return type; - } - - // Replaces a missing 4.5 method. - public static FieldInfo GetDeclaredField(this Type type, string name) - { - return type.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); - } - - // Replaces a missing 4.5 method. - public static MethodInfo GetDeclaredMethod(this Type type, string name, Type[] parameterTypes) - { - return type.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly, null, parameterTypes, null); - } - } - - /// - /// Required by . - /// - internal static class IErrorTypeSymbol - { - } - - /// - /// Required by - /// - internal static class Environment - { - public static void FailFast(string message) => System.Environment.FailFast(message); - public static void FailFast(string message, Exception exception) => System.Environment.FailFast(exception.ToString()); - public static string NewLine => System.Environment.NewLine; - public static int ProcessorCount => System.Environment.ProcessorCount; - } -} - -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - // To allow this dll to use extension methods even though we are targeting CLR v2, re-define ExtensionAttribute - internal class ExtensionAttribute : Attribute - { - } - - /// - /// This satisfies a cref on . - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] - internal class DynamicAttribute : Attribute - { - } - - /// - /// This satisfies a cref on . - /// - internal interface INotifyCompletion - { - void OnCompleted(); - } -} - -namespace System.Text -{ - internal static class StringBuilderExtensions - { - public static void Clear(this StringBuilder builder) - { - builder.Length = 0; // Matches the real definition. - } - } -} - -namespace System.Runtime.Versioning -{ - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] - internal sealed class TargetFrameworkAttribute : Attribute - { - public string FrameworkName { get; } - public string FrameworkDisplayName { get; set; } - - public TargetFrameworkAttribute(string frameworkName) - => FrameworkName = frameworkName; - } -} - -namespace System.Threading -{ - public readonly struct CancellationToken - { - /// - /// .NET Framework 2.0 does not support cancellation via . - /// - public bool IsCancellationRequested => false; - } -} diff --git a/src/ExpressionEvaluator/Core/Source/ResultProvider/NetFX20/ResultProvider.NetFX20.csproj b/src/ExpressionEvaluator/Core/Source/ResultProvider/NetFX20/ResultProvider.NetFX20.csproj deleted file mode 100644 index 921c8bf93f3f7..0000000000000 --- a/src/ExpressionEvaluator/Core/Source/ResultProvider/NetFX20/ResultProvider.NetFX20.csproj +++ /dev/null @@ -1,86 +0,0 @@ - - - - - Library - Microsoft.CodeAnalysis.ExpressionEvaluator - Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider - $(DefineConstants);NET20 - true - net20 - true - full - - - - Compiler\CaseInsensitiveComparison.cs - - - Compiler\Collections\PooledStringBuilder.cs - - - Compiler\InternalUtilities\Debug.cs - - - Compiler\InternalUtilities\FailFast.cs - - - Compiler\InternalUtilities\FatalError.cs - - - Compiler\InternalUtilities\EnumField.cs - - - Compiler\InternalUtilities\ExceptionUtilities.cs - - - Compiler\InternalUtilities\NullableAttributes.cs - - - Compiler\InternalUtilities\ObjectPool`1.cs - - - Compiler\SymbolDisplay\ObjectDisplayExtensions.cs - - - Compiler\SymbolDisplay\ObjectDisplayOptions.cs - - - Compiler\SymbolDisplay\SymbolDisplayPartKind.cs - - - Compiler\Symbols\WellKnownMemberNames.cs - - - Compiler\Xml\XmlCharType.cs - - - Helpers\DateTimeUtilities.cs - - - ExpressionCompiler\CustomTypeInfo.cs - - - ExpressionCompiler\DynamicFlagsCustomTypeInfo.cs - - - ExpressionCompiler\ExpressionEvaluatorFatalError.cs - - - Helpers\SystemHelpers.cs - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/AssemblyInfo.vb b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/AssemblyInfo.vb deleted file mode 100644 index e4ce048da21db..0000000000000 --- a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/AssemblyInfo.vb +++ /dev/null @@ -1,5 +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. - - diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj deleted file mode 100644 index bb240cea6c5e2..0000000000000 --- a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - - Library - Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider - net20 - true - - - - - - - - Compiler\Scanner\CharacterInfo.vb - - - Compiler\Scanner\KeywordTable.vb - - - Compiler\SymbolDisplay\ObjectDisplay.vb - - - Compiler\Syntax\SyntaxKind.vb - - - Compiler\Syntax\SyntaxKindFacts.vb - - - Generated\Syntax.xml.GetText.Generated.vb - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/NuGet/VisualStudio/VS.ExternalAPIs.Roslyn.Package.csproj b/src/NuGet/VisualStudio/VS.ExternalAPIs.Roslyn.Package.csproj index a37588aae1c42..6667c0e5259b3 100644 --- a/src/NuGet/VisualStudio/VS.ExternalAPIs.Roslyn.Package.csproj +++ b/src/NuGet/VisualStudio/VS.ExternalAPIs.Roslyn.Package.csproj @@ -58,11 +58,8 @@ - - - @@ -108,11 +105,6 @@ <_File Include="$(ArtifactsBinDir)Microsoft.CodeAnalysis.CSharp.ExpressionCompiler\$(Configuration)\netstandard2.0\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler.dll" TargetDir="" /> <_File Include="$(ArtifactsBinDir)Microsoft.CodeAnalysis.ExpressionCompiler\$(Configuration)\netstandard2.0\Microsoft.CodeAnalysis.ExpressionEvaluator.ExpressionCompiler.dll" TargetDir="" /> - <_File Include="$(ArtifactsBinDir)BasicResultProvider.NetFX20\$(Configuration)\net20\Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider.dll" TargetDir="RemoteDebugger\net20" /> - <_File Include="$(ArtifactsBinDir)CSharpResultProvider.NetFX20\$(Configuration)\net20\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider.dll" TargetDir="RemoteDebugger\net20" /> - <_File Include="$(ArtifactsBinDir)ResultProvider.NetFX20\$(Configuration)\net20\Microsoft.CodeAnalysis.ExpressionEvaluator.ResultProvider.dll" TargetDir="RemoteDebugger\net20" /> - <_File Include="$(ArtifactsBinDir)Microsoft.CodeAnalysis.FunctionResolver\$(Configuration)\net45\Microsoft.CodeAnalysis.ExpressionEvaluator.FunctionResolver.dll" TargetDir="RemoteDebugger\net45" /> - <_File Include="$(ArtifactsBinDir)Microsoft.CodeAnalysis.CSharp.ResultProvider\$(Configuration)\netstandard2.0\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ResultProvider.vsdconfig" TargetDir="LanguageServiceRegistration\ExpressionEvaluatorPackage" /> <_File Include="$(ArtifactsBinDir)Microsoft.CodeAnalysis.CSharp.ExpressionCompiler\$(Configuration)\netstandard2.0\Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.ExpressionCompiler.vsdconfig" TargetDir="LanguageServiceRegistration\ExpressionEvaluatorPackage" /> <_File Include="$(ArtifactsBinDir)Microsoft.CodeAnalysis.VisualBasic.ResultProvider\$(Configuration)\netstandard2.0\Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider.vsdconfig" TargetDir="LanguageServiceRegistration\ExpressionEvaluatorPackage" /> diff --git a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs index 90d8e051795ac..a56d36bbf93cc 100644 --- a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs +++ b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs @@ -169,10 +169,6 @@ private bool CheckExternalApis(TextWriter textWriter) textWriter.WriteLine("Verifying contents of VS.ExternalAPIs.Roslyn"); textWriter.WriteLine("\tRoot Folder"); verifyFolder(""); - textWriter.WriteLine("\tRemote Debugger net20"); - verifyFolder(@"RemoteDebugger\net20"); - textWriter.WriteLine("\tRemote Debugger net50"); - verifyFolder(@"RemoteDebugger\net45"); return allGood; void verifyFolder(string folderRelativeName) diff --git a/src/Tools/BuildBoss/ProjectCheckerUtil.cs b/src/Tools/BuildBoss/ProjectCheckerUtil.cs index 0cb613b77a64f..c7b907afdef00 100644 --- a/src/Tools/BuildBoss/ProjectCheckerUtil.cs +++ b/src/Tools/BuildBoss/ProjectCheckerUtil.cs @@ -287,7 +287,6 @@ private bool CheckTargetFrameworks(TextWriter textWriter) { switch (targetFramework) { - case "net20": case "net472": case "netcoreapp3.1": case "net6.0": From 9251b4c2294ed31b6a51b67986c5437aa537ee41 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 23 Feb 2022 16:06:50 -0800 Subject: [PATCH 152/187] Expose the underlying ICompilationTracker from a GeneratedFileReplacingCompilationTracker This is just a simple conversion of the field to a property. --- ...eneratedFileReplacingCompilationTracker.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs index ff751699e5a1a..85ac5e30afb5f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.GeneratedFileReplacingCompilationTracker.cs @@ -20,7 +20,6 @@ internal partial class SolutionState /// private class GeneratedFileReplacingCompilationTracker : ICompilationTracker { - private readonly ICompilationTracker _underlyingTracker; private readonly SourceGeneratedDocumentState _replacedGeneratedDocumentState; private AsyncLazy? _lazyDependentChecksum; @@ -32,17 +31,18 @@ private class GeneratedFileReplacingCompilationTracker : ICompilationTracker [DisallowNull] private Compilation? _compilationWithReplacement; + public ICompilationTracker UnderlyingTracker { get; } public SkeletonReferenceCache SkeletonReferenceCache { get; } - public ProjectState ProjectState => _underlyingTracker.ProjectState; + public ProjectState ProjectState => UnderlyingTracker.ProjectState; public GeneratedFileReplacingCompilationTracker(ICompilationTracker underlyingTracker, SourceGeneratedDocumentState replacementDocumentState) { - _underlyingTracker = underlyingTracker; + UnderlyingTracker = underlyingTracker; _replacedGeneratedDocumentState = replacementDocumentState; SkeletonReferenceCache = underlyingTracker.SkeletonReferenceCache.Clone(); } - public GeneratorDriver? GeneratorDriver => _underlyingTracker.GeneratorDriver; + public GeneratorDriver? GeneratorDriver => UnderlyingTracker.GeneratorDriver; public bool ContainsAssemblyOrModuleOrDynamic(ISymbol symbol, bool primary) { @@ -79,8 +79,8 @@ public async Task GetCompilationAsync(SolutionState solution, Cance return _compilationWithReplacement; } - var underlyingCompilation = await _underlyingTracker.GetCompilationAsync(solution, cancellationToken).ConfigureAwait(false); - var underlyingSourceGeneratedDocuments = await _underlyingTracker.GetSourceGeneratedDocumentStatesAsync(solution, cancellationToken).ConfigureAwait(false); + var underlyingCompilation = await UnderlyingTracker.GetCompilationAsync(solution, cancellationToken).ConfigureAwait(false); + var underlyingSourceGeneratedDocuments = await UnderlyingTracker.GetSourceGeneratedDocumentStatesAsync(solution, cancellationToken).ConfigureAwait(false); underlyingSourceGeneratedDocuments.TryGetState(_replacedGeneratedDocumentState.Id, out var existingState); @@ -110,10 +110,10 @@ public async Task GetCompilationAsync(SolutionState solution, Cance } public Task GetDependentVersionAsync(SolutionState solution, CancellationToken cancellationToken) - => _underlyingTracker.GetDependentVersionAsync(solution, cancellationToken); + => UnderlyingTracker.GetDependentVersionAsync(solution, cancellationToken); public Task GetDependentSemanticVersionAsync(SolutionState solution, CancellationToken cancellationToken) - => _underlyingTracker.GetDependentSemanticVersionAsync(solution, cancellationToken); + => UnderlyingTracker.GetDependentSemanticVersionAsync(solution, cancellationToken); public Task GetDependentChecksumAsync(SolutionState solution, CancellationToken cancellationToken) { @@ -129,7 +129,7 @@ public Task GetDependentChecksumAsync(SolutionState solution, Cancella private async Task ComputeDependentChecksumAsync(SolutionState solution, CancellationToken cancellationToken) => Checksum.Create( - await _underlyingTracker.GetDependentChecksumAsync(solution, cancellationToken).ConfigureAwait(false), + await UnderlyingTracker.GetDependentChecksumAsync(solution, cancellationToken).ConfigureAwait(false), await _replacedGeneratedDocumentState.GetChecksumAsync(cancellationToken).ConfigureAwait(false)); public CompilationReference? GetPartialMetadataReference(ProjectState fromProject, ProjectReference projectReference) @@ -146,7 +146,7 @@ await _underlyingTracker.GetDependentChecksumAsync(solution, cancellationToken). public async ValueTask> GetSourceGeneratedDocumentStatesAsync(SolutionState solution, CancellationToken cancellationToken) { - var underlyingGeneratedDocumentStates = await _underlyingTracker.GetSourceGeneratedDocumentStatesAsync(solution, cancellationToken).ConfigureAwait(false); + var underlyingGeneratedDocumentStates = await UnderlyingTracker.GetSourceGeneratedDocumentStatesAsync(solution, cancellationToken).ConfigureAwait(false); if (underlyingGeneratedDocumentStates.Contains(_replacedGeneratedDocumentState.Id)) { @@ -166,7 +166,7 @@ public async ValueTask> GetSour public Task HasSuccessfullyLoadedAsync(SolutionState solution, CancellationToken cancellationToken) { - return _underlyingTracker.HasSuccessfullyLoadedAsync(solution, cancellationToken); + return UnderlyingTracker.HasSuccessfullyLoadedAsync(solution, cancellationToken); } public bool TryGetCompilation([NotNullWhen(true)] out Compilation? compilation) @@ -183,7 +183,7 @@ public bool TryGetCompilation([NotNullWhen(true)] out Compilation? compilation) } else { - return _underlyingTracker.TryGetSourceGeneratedDocumentStateForAlreadyGeneratedId(documentId); + return UnderlyingTracker.TryGetSourceGeneratedDocumentStateForAlreadyGeneratedId(documentId); } } } From e316c4217a7d9144d410c5d5d8ebef179620332f Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 23 Feb 2022 16:17:13 -0800 Subject: [PATCH 153/187] Don't lose the frozen source generated document state In this particular code path, we were passing null for the frozen source generated document state when we could have passed it along. The original logic here was frozen source generated documents only normally appear when somebody calls ITextSnapshot.GetOpenDocumentInCurrentContextWithChanges, since we need to ensure that there is a generated document with that exact snapshot for the feature to work. We should never see that in a "primary" solution in any way. However, solution sync uses CurrentSolution as a cache for the previously synced complete solution, which meant we'd still end up calling this anyways. Although we could (and probably should) avoid caching these special solutions entirely, this avoids any surprises here and makes things behave like normal, and avoids odd cases where the CompilationTrackers are out of sync with this field. This partially addresses https://github.com/dotnet/roslyn/issues/57082 --- .../Core/Portable/Workspace/Solution/SolutionState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index e79f594cd88c7..cfa66a9c6a946 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -314,7 +314,7 @@ private SolutionState CreatePrimarySolution( _filePathToDocumentIdsMap, _dependencyGraph, _lazyAnalyzers, - frozenSourceGeneratedDocument: null); + _frozenSourceGeneratedDocumentState); } private BranchId GetBranchId() From 314bfbc95b0b90f6e713ff9b73e813d4a53db615 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Wed, 23 Feb 2022 16:20:13 -0800 Subject: [PATCH 154/187] Correctly handle a cached solution having frozen generated documents When we are synchronizing solutions, the previous assumption in this code was incorrect -- we absolutely can end up with a frozen generated document in CurrentSolution, since the remote workspace caches things there. We're going to just run with that and consider that normal, so we'll fix up the code to support it cleanly. Fixes https://github.com/dotnet/roslyn/issues/57082 Fixes https://github.com/dotnet/roslyn/issues/56998 --- .../Services/SolutionServiceTests.cs | 21 ++++++++++++---- .../Portable/Workspace/Solution/Solution.cs | 15 +++++++++++ .../Workspace/Solution/SolutionState.cs | 25 +++++++++++++++++++ .../Host/RemoteWorkspace.SolutionCreator.cs | 8 +++--- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index c6854515d50e2..106bc3236765c 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -486,18 +486,29 @@ public async Task TestFrozenSourceGeneratedDocument() .AddAnalyzerReference(new AnalyzerFileReference(typeof(Microsoft.CodeAnalysis.TestSourceGenerator.HelloWorldGenerator).Assembly.Location, new TestAnalyzerAssemblyLoader())) .Solution; + // First sync the solution over that has a generator var assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); var solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); var synched = await remoteWorkspace.GetSolutionAsync(assetProvider, solutionChecksum, fromPrimaryBranch: true, workspaceVersion: 0, projectId: null, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); + // Now freeze with some content var documentIdentity = (await solution.Projects.Single().GetSourceGeneratedDocumentsAsync()).First().Identity; - var frozenText = SourceText.From("// Hello, World!"); - solution = solution.WithFrozenSourceGeneratedDocument(documentIdentity, frozenText).Project.Solution; + var frozenText1 = SourceText.From("// Hello, World!"); + var frozenSolution1 = solution.WithFrozenSourceGeneratedDocument(documentIdentity, frozenText1).Project.Solution; - assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, solution); - solutionChecksum = await solution.State.GetChecksumAsync(CancellationToken.None); - synched = await remoteWorkspace.GetSolutionAsync(assetProvider, solutionChecksum, fromPrimaryBranch: false, workspaceVersion: 1, projectId: null, CancellationToken.None); + assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, frozenSolution1); + solutionChecksum = await frozenSolution1.State.GetChecksumAsync(CancellationToken.None); + synched = await remoteWorkspace.GetSolutionAsync(assetProvider, solutionChecksum, fromPrimaryBranch: true, workspaceVersion: 1, projectId: null, CancellationToken.None); + Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); + + // Try freezing with some different content from the original solution + var frozenText2 = SourceText.From("// Hello, World! A second time!"); + var frozenSolution2 = solution.WithFrozenSourceGeneratedDocument(documentIdentity, frozenText2).Project.Solution; + + assetProvider = await GetAssetProviderAsync(workspace, remoteWorkspace, frozenSolution2); + solutionChecksum = await frozenSolution2.State.GetChecksumAsync(CancellationToken.None); + synched = await remoteWorkspace.GetSolutionAsync(assetProvider, solutionChecksum, fromPrimaryBranch: true, workspaceVersion: 2, projectId: null, CancellationToken.None); Assert.Equal(solutionChecksum, await synched.State.GetChecksumAsync(CancellationToken.None)); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index 9422d0778002c..36858b84eb0fe 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -1758,6 +1758,21 @@ internal Document WithFrozenSourceGeneratedDocument(SourceGeneratedDocumentIdent return newProject.GetOrCreateSourceGeneratedDocument(newDocumentState); } + /// + /// Undoes the operation of ; any frozen source generated document is allowed + /// to have it's real output again. + /// + internal Solution WithoutFrozenSourceGeneratedDocuments() + { + var newState = _state.WithoutFrozenSourceGeneratedDocuments(); + if (newState == _state) + { + return this; + } + + return new Solution(newState); + } + /// /// Gets an objects that lists the added, changed and removed projects between /// this solution and the specified solution. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index cfa66a9c6a946..84e56211b30c6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -1908,6 +1908,31 @@ public SolutionState WithFrozenSourceGeneratedDocument(SourceGeneratedDocumentId frozenSourceGeneratedDocument: newGeneratedState); } + /// + /// Undoes the operation of ; any frozen source generated document is allowed + /// to have it's real output again. + /// + public SolutionState WithoutFrozenSourceGeneratedDocuments() + { + // If there's nothing frozen, there's nothing to do. + if (_frozenSourceGeneratedDocumentState == null) + return this; + + var projectId = _frozenSourceGeneratedDocumentState.Id.ProjectId; + + // Since we previously froze this document, we should have a CompilationTracker entry for it, and it should be a + // GeneratedFileReplacingCompilationTracker. To undo the operation, we'll just restore the original CompilationTracker. + var newTrackerMap = CreateCompilationTrackerMap(projectId, _dependencyGraph); + Contract.ThrowIfFalse(newTrackerMap.TryGetValue(projectId, out var existingTracker)); + var replacingItemTracker = existingTracker as GeneratedFileReplacingCompilationTracker; + Contract.ThrowIfNull(replacingItemTracker); + newTrackerMap = newTrackerMap.SetItem(projectId, replacingItemTracker.UnderlyingTracker); + + return this.Branch( + projectIdToTrackerMap: newTrackerMap, + frozenSourceGeneratedDocument: null); + } + /// /// Symbols need to be either or . /// diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index e650ef41b4af7..4e653a4220e18 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -57,6 +57,10 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum) { var solution = _baseSolution; + // If we previously froze a source generated document and then held onto that, unfreeze it now. We'll re-freeze the new document + // if needed again later. + solution = solution.WithoutFrozenSourceGeneratedDocuments(); + var oldSolutionChecksums = await solution.State.GetStateChecksumsAsync(_cancellationToken).ConfigureAwait(false); var newSolutionChecksums = await _assetProvider.GetAssetAsync(newSolutionChecksum, _cancellationToken).ConfigureAwait(false); @@ -85,10 +89,6 @@ public async Task CreateSolutionAsync(Checksum newSolutionChecksum) newSolutionChecksums.AnalyzerReferences, _cancellationToken).ConfigureAwait(false)); } - // The old solution should never have any frozen source generated documents -- those are only created and forked off of - // a workspaces's CurrentSolution - Contract.ThrowIfFalse(solution.State.FrozenSourceGeneratedDocumentState == null); - if (newSolutionChecksums.FrozenSourceGeneratedDocumentIdentity != Checksum.Null && newSolutionChecksums.FrozenSourceGeneratedDocumentText != Checksum.Null) { var identity = await _assetProvider.GetAssetAsync(newSolutionChecksums.FrozenSourceGeneratedDocumentIdentity, _cancellationToken).ConfigureAwait(false); From 2a29b3c7015ac750e15ac8a054479232dbf00cd3 Mon Sep 17 00:00:00 2001 From: "gel@microsoft.com" Date: Wed, 23 Feb 2022 16:24:05 -0800 Subject: [PATCH 155/187] Revert change to a CompletionItem property which pythia depends on Also add required helpers so we can remove the dependency --- .../IntelliSense/AsyncCompletion/CompletionSource.cs | 2 +- .../Handlers/Completion/CompletionHandler.cs | 4 +--- .../CompletionProviders/CrefCompletionProvider.cs | 2 +- .../Core/Portable/Completion/CommonCompletionItem.cs | 1 - .../Completion/Providers/SymbolCompletionItem.cs | 10 ++++++++-- .../Pythia/Api/PythiaCompletionProviderBase.cs | 9 +++++++-- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index 5603b0d71f2be..97b93cf5a2fd7 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -558,7 +558,7 @@ private VSCompletionItem Convert( // roslynItem generated by providers can contain an insertionText in a property bag. // We will not use it but other providers may need it. // We actually will calculate the insertion text once again when called TryCommit. - if (!roslynItem.Properties.TryGetValue(CommonCompletionItem.InsertionTextProperty, out var insertionText)) + if (!SymbolCompletionItem.TryGetInsertionText(roslynItem, out var insertionText)) { insertionText = roslynItem.DisplayText; } diff --git a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs index eeb5cd2079a70..5f9b25e1deb8d 100644 --- a/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs +++ b/src/EditorFeatures/Core/Implementation/LanguageServer/Handlers/Completion/CompletionHandler.cs @@ -260,9 +260,7 @@ static async Task CreateCompletionItemAsync( // If the feature flag is off, return an InsertText. else { - completionItem.InsertText = item.Properties.ContainsKey(CommonCompletionItem.InsertionTextProperty) - ? item.Properties[CommonCompletionItem.InsertionTextProperty] - : completeDisplayText; + completionItem.InsertText = SymbolCompletionItem.TryGetInsertionText(item, out var insertionText) ? insertionText : completeDisplayText; } var commitCharacters = GetCommitCharacters(item, commitCharacterRulesCache, supportsVSExtensions); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs index 7d3964708e97e..b54ec7bf944ea 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs @@ -419,7 +419,7 @@ private static CompletionItemRules GetRules(string displayText) protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) { - if (!selectedItem.Properties.TryGetValue(CommonCompletionItem.InsertionTextProperty, out var insertionText)) + if (!SymbolCompletionItem.TryGetInsertionText(selectedItem, out var insertionText)) { insertionText = selectedItem.DisplayText; } diff --git a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs index 4a5a720b8ccb0..57d1b58dc29f3 100644 --- a/src/Features/Core/Portable/Completion/CommonCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/CommonCompletionItem.cs @@ -12,7 +12,6 @@ namespace Microsoft.CodeAnalysis.Completion internal static class CommonCompletionItem { public const string DescriptionProperty = nameof(DescriptionProperty); - public const string InsertionTextProperty = nameof(InsertionTextProperty); public static CompletionItem Create( string displayText, diff --git a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs index c8f90f98d8176..09e9cfd965db1 100644 --- a/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs +++ b/src/Features/Core/Portable/Completion/Providers/SymbolCompletionItem.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -17,6 +18,8 @@ namespace Microsoft.CodeAnalysis.Completion.Providers { internal static partial class SymbolCompletionItem { + private const string InsertionTextProperty = "InsertionText"; + private static readonly Func, CompletionItem, CompletionItem> s_addSymbolEncoding = AddSymbolEncoding; private static readonly Func, CompletionItem, CompletionItem> s_addSymbolInfo = AddSymbolInfo; private static readonly char[] s_projectSeperators = new[] { ';' }; @@ -43,7 +46,7 @@ private static CompletionItem CreateWorker( if (insertionText != null) { - props = props.Add(CommonCompletionItem.InsertionTextProperty, insertionText); + props = props.Add(InsertionTextProperty, insertionText); } props = props.Add("ContextPosition", contextPosition.ToString()); @@ -254,7 +257,10 @@ public static int GetDescriptionPosition(CompletionItem item) => GetContextPosition(item); public static string GetInsertionText(CompletionItem item) - => item.Properties[CommonCompletionItem.InsertionTextProperty]; + => item.Properties[InsertionTextProperty]; + + public static bool TryGetInsertionText(CompletionItem item, [NotNullWhen(true)] out string? insertionText) + => item.Properties.TryGetValue(InsertionTextProperty, out insertionText); // COMPAT OVERLOAD: This is used by IntelliCode. public static CompletionItem CreateWithSymbolId( diff --git a/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs b/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs index f14cb3063a358..f7c387b26a020 100644 --- a/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs +++ b/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs @@ -4,12 +4,12 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api @@ -48,11 +48,16 @@ public static ImmutableArray CreateRecommendedKeywordDisplayP => RecommendedKeyword.CreateDisplayParts(keyword, toolTip); public static Task GetDescriptionAsync(CompletionItem item, Document document, SymbolDescriptionOptions displayOptions, CancellationToken cancellationToken) - => SymbolCompletionItem.GetDescriptionAsync(item, document, displayOptions, cancellationToken); + => SymbolCompletionItem.HasSymbols(item) + ? SymbolCompletionItem.GetDescriptionAsync(item, document, displayOptions, cancellationToken) + : Task.FromResult(CommonCompletionItem.GetDescription(item)); public static CompletionDescription GetDescription(CompletionItem item) => CommonCompletionItem.GetDescription(item); + public static bool TryGetInsertionText(CompletionItem item, [NotNullWhen(true)] out string? insertionText) + => SymbolCompletionItem.TryGetInsertionText(item, out insertionText); + public override Task GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default) => base.GetChangeAsync(document, item, commitKey, cancellationToken); From dcbfe28dba68c6daa672b473d39aea75254e25dc Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 23 Feb 2022 18:07:03 -0800 Subject: [PATCH 156/187] recurse into trivia --- .../EmbeddedLanguageClassificationService.cs | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs index 943cd54964cf1..a6798f4be85a3 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/EmbeddedLanguageClassificationService.cs @@ -91,24 +91,44 @@ public void Recurse( private void ProcessToken(SyntaxToken token, CancellationToken cancellationToken) { - if (!token.Span.IntersectsWith(_textSpan) || !_syntaxTokenKinds.Contains(token.RawKind)) - return; + cancellationToken.ThrowIfCancellationRequested(); + ProcessTriviaList(token.LeadingTrivia, cancellationToken); + ClassifyToken(token, cancellationToken); + ProcessTriviaList(token.TrailingTrivia, cancellationToken); + } - foreach (var language in _languagesProvider.Languages) + private readonly void ClassifyToken(SyntaxToken token, CancellationToken cancellationToken) + { + if (token.Span.IntersectsWith(_textSpan) && _syntaxTokenKinds.Contains(token.RawKind)) { - var classifier = language.Classifier; - if (classifier != null) + foreach (var language in _languagesProvider.Languages) { - var count = _result.Count; - classifier.AddClassifications(token, _semanticModel, _options, _result, cancellationToken); - if (_result.Count != count) + var classifier = language.Classifier; + if (classifier != null) { - // This classifier added values. No need to check the other ones. - return; + var count = _result.Count; + classifier.AddClassifications(token, _semanticModel, _options, _result, cancellationToken); + if (_result.Count != count) + { + // This classifier added values. No need to check the other ones. + return; + } } } } } + + private void ProcessTriviaList(SyntaxTriviaList triviaList, CancellationToken cancellationToken) + { + foreach (var trivia in triviaList) + ProcessTrivia(trivia, cancellationToken); + } + + private void ProcessTrivia(SyntaxTrivia trivia, CancellationToken cancellationToken) + { + if (trivia.HasStructure && trivia.FullSpan.IntersectsWith(_textSpan)) + Recurse(trivia.GetStructure()!, cancellationToken); + } } } } From 3929ab94c955d4ecc4161434392cd760b91085eb Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Thu, 24 Feb 2022 13:48:09 +0200 Subject: [PATCH 157/187] Add MetadataAsSource test for records --- .../MetadataAsSourceTests.CSharp.cs | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs index a2c690b4be570..8e30c8285a1a0 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs @@ -444,6 +444,117 @@ public void F() await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, languageVersion: "Preview", metadataLanguageVersion: "Preview", expected: expected, signaturesOnly: signaturesOnly); } + + [Theory, CombinatorialData, Trait(Traits.Feature, Traits.Features.MetadataAsSource)] + [WorkItem(44566, "https://github.com/dotnet/roslyn/issues/44566")] + public async Task TestRecordType(bool signaturesOnly) + { + var metadataSource = "public record R;"; + var symbolName = "R"; + + var expected = signaturesOnly switch + { + true => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +#endregion + +#nullable enable + +using System; +using System.Runtime.CompilerServices; +using System.Text; + +public record [|R|] : IEquatable +{{ + public R(); + [CompilerGenerated] + protected R(R original); + + protected virtual Type EqualityContract {{ get; }} + + [CompilerGenerated] + public virtual R $(); + [CompilerGenerated] + public override bool Equals(object? obj); + [CompilerGenerated] + public virtual bool Equals(R? other); + [CompilerGenerated] + public override int GetHashCode(); + [CompilerGenerated] + public override string ToString(); + [CompilerGenerated] + protected virtual bool PrintMembers(StringBuilder builder); + + [CompilerGenerated] + public static bool operator ==(R? left, R? right); + [CompilerGenerated] + public static bool operator !=(R? left, R? right); +}}", + false => $@"#region {FeaturesResources.Assembly} ReferencedAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null +// {CodeAnalysisResources.InMemoryAssembly} +// Decompiled with ICSharpCode.Decompiler {ICSharpCodeDecompilerVersion} +#endregion + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; + +public record [|R|] +{{ + [CompilerGenerated] + public override string ToString() + {{ + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append(""R""); + stringBuilder.Append("" {{ ""); + if (PrintMembers(stringBuilder)) + {{ + stringBuilder.Append(' '); + }} + + stringBuilder.Append('}}'); + return stringBuilder.ToString(); + }} + + [CompilerGenerated] + protected virtual bool PrintMembers(StringBuilder builder) + {{ + return false; + }} + + [CompilerGenerated] + public override int GetHashCode() + {{ + return EqualityComparer.Default.GetHashCode(EqualityContract); + }} + + [CompilerGenerated] + public virtual bool Equals(R? other) + {{ + return (object)this == other || ((object)other != null && EqualityContract == other!.EqualityContract); + }} + + [CompilerGenerated] + protected R(R original) + {{ + }} + + public R() + {{ + }} +}} +#if false // {CSharpEditorResources.Decompilation_log} +{string.Format(CSharpEditorResources._0_items_in_cache, 6)} +------------------ +{string.Format(CSharpEditorResources.Resolve_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} +{string.Format(CSharpEditorResources.Found_single_assembly_0, "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")} +{string.Format(CSharpEditorResources.Load_from_0, "mscorlib.v4_6_1038_0.dll")} +#endif", + }; + + await GenerateAndVerifySourceAsync(metadataSource, symbolName, LanguageNames.CSharp, expected: expected, signaturesOnly: signaturesOnly); + } } } } From e908eb40ff6a5a7bf7fbe3aa9c82280624df6cb9 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Thu, 24 Feb 2022 11:31:07 -0800 Subject: [PATCH 158/187] Remove NetFx20 NuSpec (#59742) This was created in part to support our targeting of `net20`. It's no longer needed as we have removed `net20` support. Removing. --- .../Microsoft.NetFX20.nuspec | 23 -------------- src/Dependencies/Microsoft.NetFX20/Readme.txt | 28 ------------------ .../Microsoft.NetFX20/Version.targets | 9 ------ .../Microsoft.NetFX20/mscorlib.dll | Bin 1789952 -> 0 bytes 4 files changed, 60 deletions(-) delete mode 100644 src/Dependencies/Microsoft.NetFX20/Microsoft.NetFX20.nuspec delete mode 100644 src/Dependencies/Microsoft.NetFX20/Readme.txt delete mode 100644 src/Dependencies/Microsoft.NetFX20/Version.targets delete mode 100644 src/Dependencies/Microsoft.NetFX20/mscorlib.dll diff --git a/src/Dependencies/Microsoft.NetFX20/Microsoft.NetFX20.nuspec b/src/Dependencies/Microsoft.NetFX20/Microsoft.NetFX20.nuspec deleted file mode 100644 index b5af6645d03f7..0000000000000 --- a/src/Dependencies/Microsoft.NetFX20/Microsoft.NetFX20.nuspec +++ /dev/null @@ -1,23 +0,0 @@ - - - - Microsoft.NetFX20 - Microsoft Framework 2.0 reference assemblies - - Microsoft Framework 2.0 reference assemblies. -Supported Platforms: -- .NET Framework 2.0 - - en-US - true - $version$ - Microsoft - http://go.microsoft.com/fwlink/?LinkId=394369 - http://msdn.com/roslyn - Microsoft Framework 2.0 reference assemblies - Reference Assemblies mscorlib - - - - - diff --git a/src/Dependencies/Microsoft.NetFX20/Readme.txt b/src/Dependencies/Microsoft.NetFX20/Readme.txt deleted file mode 100644 index e7f10464418f5..0000000000000 --- a/src/Dependencies/Microsoft.NetFX20/Readme.txt +++ /dev/null @@ -1,28 +0,0 @@ -Generated from mscorlib 2.0 using RefAsmGen: - -refasmgen /contracts+ mscorlib.dll /o:Contracts\mscorlib.dll - -and then clearing the CorFlags.Requires32Bit big in the COR header flag to change the architecture to AnyCPU. - -Script to clear the flag: - -#r "System.Reflection.Metadata.1.0.19-rc.nupkg" - -using (var stream = new FileStream(args[0], FileMode.Open, FileAccess.ReadWrite, FileShare.Read)) -using (var peReader = new PEReader(stream)) -using (var writer = new BinaryWriter(stream)) -{ - const int OffsetFromStartOfCorHeaderToFlags = - sizeof(int) // byte count - + sizeof(short) // major version - + sizeof(short) // minor version - + sizeof(long); // metadata directory - - stream.Position = peReader.PEHeaders.CorHeaderStartOffset + OffsetFromStartOfCorHeaderToFlags; - - var flags = peReader.PEHeaders.CorHeader.Flags; - Console.WriteLine($"Current flags: {flags}"); - flags &= ~CorFlags.Requires32Bit; - Console.WriteLine($"New flags: {flags}"); - writer.Write((uint)flags); -} \ No newline at end of file diff --git a/src/Dependencies/Microsoft.NetFX20/Version.targets b/src/Dependencies/Microsoft.NetFX20/Version.targets deleted file mode 100644 index abdcde8deda72..0000000000000 --- a/src/Dependencies/Microsoft.NetFX20/Version.targets +++ /dev/null @@ -1,9 +0,0 @@ - - - - - 1.0.3 - $(RoslynSemanticVersion) - Release - - \ No newline at end of file diff --git a/src/Dependencies/Microsoft.NetFX20/mscorlib.dll b/src/Dependencies/Microsoft.NetFX20/mscorlib.dll deleted file mode 100644 index 675c0df084a6f2dc230314ae0ef53edf38a67195..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1789952 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%0A07DZer0uw{6E#WBl%r2Y4%cznJ$?N3fx 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
8}%n-onTC9%#D3=eG+!_T{6!iOX}}i6I|+zC0hJ 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 z z=j}*0*2T`ILf6*wE6TxEffA)Vl_#D|H@aRTW^rLzjZTY&KQzL8Ra(!L(;E} zz`vrFZxR+@Kc^bI*2|4#=Nh)q{xrq8DVXima-9oL-Ym=1U!0%0DXRD{R19(B7rtj( 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

`ceK9@#U~~3rShTL$L@CiImTzC0%`)c32&R| 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

5_C=x*}bbt^s>px*^?^Zb`6& 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%0D$#x2IJ#_h&! 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%0ADa>4PHVNm(*gQ4 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 T1!5`?Q-PQY#8lvKRN(&uU%JBi From ee6486db646504f791f14c61a2200d7a9f26995b Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 24 Feb 2022 15:52:15 -0800 Subject: [PATCH 159/187] Update message to reflect cleanup behavior Fixes https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1485697 --- src/Features/CSharp/Portable/CSharpFeaturesResources.resx | 6 +++--- .../CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs | 2 +- .../CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf | 6 +++--- .../CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf | 6 +++--- .../LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs | 2 +- 16 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index e82bc83107f26..f044789c39656 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -519,8 +519,8 @@ Convert to method - - Add/remove braces for single-line control statements + + Add required braces for single-line control statements Apply 'this.' qualification preferences @@ -645,7 +645,7 @@ Apply embedded statements on same line preferences (experimental) - + Apply local over anonymous function preferences diff --git a/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs b/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs index b1c88bcad6c82..c4a28fd2f1197 100644 --- a/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs +++ b/src/Features/CSharp/Portable/CodeCleanup/CSharpCodeCleanupService.cs @@ -193,7 +193,7 @@ internal class CSharpCodeCleanupService : AbstractCodeCleanupService // Code-block preferences // csharp_prefer_braces - new DiagnosticSet(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements, + new DiagnosticSet(CSharpFeaturesResources.Add_required_braces_for_single_line_control_statements, IDEDiagnosticIds.AddBracesDiagnosticId), // csharp_prefer_simple_using_statement diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index d14b2fafbddd3..85c8bbbd044d4 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -17,9 +17,9 @@ Přidat chybějící direktivy using {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Přidat/odebrat složené závorky pro řídicí příkazy na jeden řádek + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 2a8fda261d4c0..c12082919f2b8 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -17,9 +17,9 @@ Fehlende using-Anweisungen hinzufügen {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Geschweifte Klammern für einzeilige Steuerungsanweisungen hinzufügen/entfernen + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 09c8aebc9540b..80ab9c22659f4 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -17,9 +17,9 @@ Añadir valores using que faltan {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Agregar o quitar llaves para instrucciones de control de una línea + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index b2be4ab9126d9..01e211a5331a2 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -17,9 +17,9 @@ Ajouter les instructions using manquantes {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Ajouter/supprimer des accolades pour les instructions de contrôle sur une seule ligne + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index a651cf7a87ead..b9c2e6a6c5a2b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -17,9 +17,9 @@ Aggiungi using mancanti {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Aggiungi/rimuovi le parentesi graffe per le istruzioni di controllo a riga singola + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index faed6869b9668..1f9b8b637eaff 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -17,9 +17,9 @@ 不足している using を追加する {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - 単一行のコントロール ステートメントに対する波かっこの追加/削除を行います + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 9999e9349645a..1de66d4ea5304 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -17,9 +17,9 @@ 누락된 using 추가 {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - 한 줄 제어문에 대해 중괄호 추가/제거 + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 288dae211b406..fa44f615f1c3b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -17,9 +17,9 @@ Dodaj brakujące instrukcje using {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Dodaj/usuń nawiasy klamrowe w przypadku jednowierszowych instrukcji sterowania + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index df5edd75aceec..7007a8aa54150 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -17,9 +17,9 @@ Adicionar usings ausentes {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Adicionar/remover as chaves das instruções de controle de linha única + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index c88ca3b2d1bf3..b2a6c537e66e3 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -17,9 +17,9 @@ Добавить отсутствующие директивы using {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Добавлять или удалять фигурные скобки для однострочных операторов управления + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index cc283ae8ae080..0c473c063dddf 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -17,9 +17,9 @@ Eksik usings Ekle {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - Tek satır denetim deyimleri için küme ayracı ekle/küme ayraçlarını kaldır + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index cfaa7f0442b7b..6c100501b63c1 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -17,9 +17,9 @@ 添加缺少的 using {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - 添加/删除单行控制语句的括号 + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 696f9451cf792..f496739b07296 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -17,9 +17,9 @@ 新增遺漏的 using {Locked="using"} "using" is a C# keyword and should not be localized. - - Add/remove braces for single-line control statements - 新增/移除單行控制項陳述式的括弧 + + Add required braces for single-line control statements + Add required braces for single-line control statements diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs index 6e412876dd3cf..9829840d0637f 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodeCleanupFixerDiagnosticIds.cs @@ -39,7 +39,7 @@ internal static class CSharpCodeCleanUpFixerDiagnosticIds [Order(After = AbstractCodeCleanUpFixer.SortImportsFixId)] [ConfigurationKey("unused")] [HelpLink($"https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/{IDEDiagnosticIds.AddBracesDiagnosticId}")] - [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Add_remove_braces_for_single_line_control_statements))] + [LocalizedName(typeof(CSharpFeaturesResources), nameof(CSharpFeaturesResources.Add_required_braces_for_single_line_control_statements))] public static readonly FixIdDefinition? AddBracesDiagnosticId; [Export] From e9d409eb2bac54a260f3afefda449ff0bf93ad0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 24 Feb 2022 16:43:18 -0800 Subject: [PATCH 160/187] Add missing API to IVSTypeScriptDiagnosticService (#59751) --- .../Api/IVSTypeScriptDiagnosticService.cs | 4 ++++ ...TypeScriptDiagnosticsUpdatedArgsWrapper.cs | 22 +++++++++++++++++++ .../VSTypeScriptDiagnosticService.cs | 21 ++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticsUpdatedArgsWrapper.cs diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.cs index c32fb9c89a5ff..c3393483bdba4 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/IVSTypeScriptDiagnosticService.cs @@ -2,14 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api { internal interface IVSTypeScriptDiagnosticService { Task> GetPushDiagnosticsAsync(Workspace workspace, ProjectId projectId, DocumentId documentId, object id, bool includeSuppressedDiagnostics, CancellationToken cancellationToken); + + IDisposable RegisterDiagnosticsUpdatedEventHandler(Action action); } } diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticsUpdatedArgsWrapper.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticsUpdatedArgsWrapper.cs new file mode 100644 index 0000000000000..d328391d5e8e7 --- /dev/null +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticsUpdatedArgsWrapper.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api +{ + internal readonly struct VSTypeScriptDiagnosticsUpdatedArgsWrapper + { + internal readonly DiagnosticsUpdatedArgs UnderlyingObject; + + public VSTypeScriptDiagnosticsUpdatedArgsWrapper(DiagnosticsUpdatedArgs underlyingObject) + => UnderlyingObject = underlyingObject; + + public Solution? Solution + => UnderlyingObject.Solution; + + public DocumentId? DocumentId + => UnderlyingObject.DocumentId; + } +} diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs index f39932bdce1c4..dfbf6de3db972 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptDiagnosticService.cs @@ -33,5 +33,26 @@ public async Task> GetPushDiagnostics var result = await _service.GetPushDiagnosticsAsync(workspace, projectId, documentId, id, includeSuppressedDiagnostics, _globalOptions.GetDiagnosticMode(InternalDiagnosticsOptions.NormalDiagnosticMode), cancellationToken).ConfigureAwait(false); return result.SelectAsArray(data => new VSTypeScriptDiagnosticData(data)); } + + public IDisposable RegisterDiagnosticsUpdatedEventHandler(Action action) + => new EventHandlerWrapper(_service, action); + + private sealed class EventHandlerWrapper : IDisposable + { + private readonly IDiagnosticService _service; + private readonly EventHandler _handler; + + internal EventHandlerWrapper(IDiagnosticService service, Action action) + { + _service = service; + _handler = (sender, args) => action(new VSTypeScriptDiagnosticsUpdatedArgsWrapper(args)); + _service.DiagnosticsUpdated += _handler; + } + + public void Dispose() + { + _service.DiagnosticsUpdated -= _handler; + } + } } } From 99a15bb77187cd69f1b934ff937f147baaa3def9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 24 Feb 2022 17:07:10 -0800 Subject: [PATCH 161/187] Add missing O# context factories (#59727) --- .../CodeActions/OmniSharpCodeActionOptions.cs | 22 +++++++++ .../OmniSharpCodeFixContextFactory.cs | 47 ++++++++++++++----- 2 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeActionOptions.cs diff --git a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeActionOptions.cs b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeActionOptions.cs new file mode 100644 index 0000000000000..c60ad8943d52b --- /dev/null +++ b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeActionOptions.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; +using Microsoft.CodeAnalysis.ImplementType; +using Microsoft.CodeAnalysis.SymbolSearch; + +namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions +{ + internal readonly record struct OmniSharpCodeActionOptions( + OmniSharpImplementTypeOptions ImplementTypeOptions) + { + internal CodeActionOptions GetCodeActionOptions() + => new( + SymbolSearchOptions.Default, + new ImplementTypeOptions( + InsertionBehavior: (ImplementTypeInsertionBehavior)ImplementTypeOptions.InsertionBehavior, + PropertyGenerationBehavior: (ImplementTypePropertyGenerationBehavior)ImplementTypeOptions.PropertyGenerationBehavior)); + } +} diff --git a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs index b4f31dd36f8ba..29d5af4e76485 100644 --- a/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs +++ b/src/Tools/ExternalAccess/OmniSharp/CodeActions/OmniSharpCodeFixContextFactory.cs @@ -3,33 +3,56 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType; -using Microsoft.CodeAnalysis.ImplementType; -using Microsoft.CodeAnalysis.SymbolSearch; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CodeActions { internal static class OmniSharpCodeFixContextFactory { - public static CodeFixContext Create( + public static CodeFixContext CreateCodeFixContext( Document document, TextSpan span, ImmutableArray diagnostics, Action> registerCodeFix, - OmniSharpImplementTypeOptions implementTypeOptions, + OmniSharpCodeActionOptions options, CancellationToken cancellationToken) - => new(document, span, diagnostics, registerCodeFix, GetCodeActionOptions(implementTypeOptions), cancellationToken); + => new(document, span, diagnostics, registerCodeFix, options.GetCodeActionOptions(), cancellationToken); - private static CodeActionOptions GetCodeActionOptions(OmniSharpImplementTypeOptions implementTypeOptions) - => new( - SymbolSearchOptions.Default, - new ImplementTypeOptions( - InsertionBehavior: (ImplementTypeInsertionBehavior)implementTypeOptions.InsertionBehavior, - PropertyGenerationBehavior: (ImplementTypePropertyGenerationBehavior)implementTypeOptions.PropertyGenerationBehavior)); + public static CodeRefactoringContext CreateCodeRefactoringContext( + Document document, + TextSpan span, + Action registerRefactoring, + OmniSharpCodeActionOptions options, + CancellationToken cancellationToken) + => new(document, span, registerRefactoring, options.GetCodeActionOptions(), cancellationToken); + + public static FixAllContext CreateFixAllContext( + Document? document, + Project project, + CodeFixProvider codeFixProvider, + FixAllScope scope, + string? codeActionEquivalenceKey, + IEnumerable diagnosticIds, + FixAllContext.DiagnosticProvider fixAllDiagnosticProvider, + Func optionsProvider, + CancellationToken cancellationToken) + => new(new FixAllState( + fixAllProvider: null, + document, + project, + codeFixProvider, + scope, + codeActionEquivalenceKey, + diagnosticIds, + fixAllDiagnosticProvider, + language => optionsProvider(language).GetCodeActionOptions()), + new ProgressTracker(), cancellationToken); } } From cd9b31f315abf673fea07dc6142e83c7e916c3fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 24 Feb 2022 19:05:27 -0800 Subject: [PATCH 162/187] LSP breakpoint placement (#59428) --- eng/Versions.props | 2 +- .../Protocol/DefaultCapabilitiesProvider.cs | 1 + .../ValidateBreakableRangeHandler.cs | 54 ++++++++++ .../ValidateBreakableRangeTests.cs | 101 ++++++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/Features/LanguageServer/Protocol/Handler/Breakpoints/ValidateBreakableRangeHandler.cs create mode 100644 src/Features/LanguageServer/ProtocolUnitTests/ValidateBreakableRange/ValidateBreakableRangeTests.cs diff --git a/eng/Versions.props b/eng/Versions.props index 935eaa1113fd8..8b8b2d3a2ebf7 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -29,7 +29,7 @@ 17.0.487 5.0.0-alpha1.19409.1 5.0.0-preview.1.20112.8 - 17.1.11 + 17.2.3 17.0.31723.112 16.5.0