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
+ }
+ ```
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/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/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/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/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/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/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.
+
+ Žá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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 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.
+
+ 関数ポインター '{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.
+
+ 함수 포인터 '{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.
+
+ Ż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.
+
+ 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.
+
+ Нет перегруженного метода для "{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.
+
+ '{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.
+
+ “{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.
+
+ '{0}' 沒有任何多載符合函式指標 '{1}'
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/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/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/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));
+ }
}
}
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)
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/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;
+ }
+ }
}
}
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 = 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/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/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs b/src/EditorFeatures/TestUtilities/Classification/AbstractClassifierTests.cs
index 47fea4a45c791..eb270c3040c43 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/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs
index 7915e0bcbe9f3..b383d9830331e 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;
@@ -24,9 +26,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,31 +283,31 @@ 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 }, Array.Empty(), LanguageNames.CSharp);
+ protected Task CreateTestLspServerAsync(string markup, LSP.ClientCapabilities? clientCapabilities = null)
+ => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), LanguageNames.CSharp, clientCapabilities);
- protected Task CreateVisualBasicTestLspServerAsync(string markup)
- => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), LanguageNames.VisualBasic);
+ protected Task CreateVisualBasicTestLspServerAsync(string markup, LSP.ClientCapabilities? clientCapabilities = null)
+ => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), 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, Array.Empty());
+ protected Task CreateTestLspServerAsync(string[] markups, LSP.ClientCapabilities? clientCapabilities = null)
+ => CreateTestLspServerAsync(markups, Array.Empty(), LanguageNames.CSharp, clientCapabilities);
- private protected Task CreateTestLspServerAsync(string markup, WellKnownLspServerKinds serverKind)
- => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), LanguageNames.CSharp, serverKind);
+ private protected Task CreateTestLspServerAsync(string markup, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind)
+ => CreateTestLspServerAsync(new string[] { markup }, Array.Empty(), LanguageNames.CSharp, clientCapabilities, serverKind);
///
/// 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);
+ protected Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups, LSP.ClientCapabilities? clientCapabilities = null)
+ => CreateTestLspServerAsync(markups, sourceGeneratedMarkups, LanguageNames.CSharp, clientCapabilities);
- private Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups, string languageName, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer)
+ private Task CreateTestLspServerAsync(string[] markups, string[] sourceGeneratedMarkups, string languageName, LSP.ClientCapabilities? clientCapabilities, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer)
{
var exportProvider = Composition.ExportProviderFactory.CreateExportProvider();
var syntaxTreeConfigurationService = exportProvider.GetExportedValue();
@@ -314,10 +320,10 @@ private Task CreateTestLspServerAsync(string[] markups, string[]
_ => 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;
@@ -336,10 +342,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 await TestLspServer.CreateAsync(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);
@@ -347,7 +357,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 await TestLspServer.CreateAsync(workspace);
+ return await TestLspServer.CreateAsync(workspace, clientCapabilities ?? new LSP.ClientCapabilities(), serverKind);
}
///
@@ -409,20 +419,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;
@@ -468,16 +464,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;
+
+ public LSP.ClientCapabilities ClientCapabilities { get; }
- private TestLspServer(TestWorkspace testWorkspace, Dictionary> locations, WellKnownLspServerKinds serverKind)
+ private TestLspServer(TestWorkspace testWorkspace, Dictionary> locations, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind)
{
TestWorkspace = testWorkspace;
+ ClientCapabilities = clientCapabilities;
_locations = locations;
- _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);
@@ -487,17 +494,61 @@ private TestLspServer(TestWorkspace testWorkspace, Dictionary CreateAsync(TestWorkspace workspace, WellKnownLspServerKinds serverKind = WellKnownLspServerKinds.AlwaysActiveVSLspServer)
+ private static JsonMessageFormatter CreateJsonMessageFormatter()
+ {
+ var messageFormatter = new JsonMessageFormatter();
+ LSP.VSInternalExtensionUtilities.AddVSInternalExtensionConverters(messageFormatter.JsonSerializer);
+ return messageFormatter;
+ }
+
+ internal static async Task CreateAsync(TestWorkspace testWorkspace, LSP.ClientCapabilities clientCapabilities, WellKnownLspServerKinds serverKind)
{
- var locations = await GetAnnotatedLocationsAsync(workspace, workspace.CurrentSolution);
- return new TestLspServer(workspace, locations, serverKind);
+ var locations = await GetAnnotatedLocationsAsync(testWorkspace, testWorkspace.CurrentSolution);
+ var server = new TestLspServer(testWorkspace, locations, clientCapabilities, serverKind);
+
+ await server.ExecuteRequestAsync(LSP.Methods.InitializeName, new LSP.InitializeParams
+ {
+ Capabilities = clientCapabilities,
+ }, CancellationToken.None);
+
+ return server;
}
- public Task ExecuteRequestAsync(string methodName, RequestType request, LSP.ClientCapabilities clientCapabilities,
- string? clientName, CancellationToken cancellationToken) where RequestType : class
+ private static LanguageServerTarget CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace, WellKnownLspServerKinds serverKind)
{
- return _requestDispatcher.ExecuteRequestAsync(
- _executionQueue, methodName, request, clientCapabilities, clientName, cancellationToken);
+ 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)
@@ -511,8 +562,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)
@@ -529,8 +579,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)
@@ -545,26 +594,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() => _languageServer.GetTestAccessor().GetDispatcherAccessor();
- internal RequestDispatcher.TestAccessor GetDispatcherAccessor() => _requestDispatcher.GetTestAccessor();
+ internal LspWorkspaceManager.TestAccessor GetManagerAccessor() => _languageServer.GetTestAccessor().GetManagerAccessor();
- internal LspWorkspaceManager.TestAccessor GetManagerAccessor() => _executionQueue.GetTestAccessor().GetLspWorkspaceManager().GetTestAccessor();
+ internal LspWorkspaceManager GetManager() => _languageServer.GetTestAccessor().GetQueueAccessor().GetLspWorkspaceManager();
- internal LspWorkspaceManager GetManager() => _executionQueue.GetTestAccessor().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/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/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs
index c28c4dcc1c907..1c186eab3a7d8 100644
--- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs
+++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs
@@ -39,25 +39,23 @@ 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;
+ private readonly ConditionalWeakTable, ImmutableDictionary>> _projectFixersMap = new();
// Shared by project fixers and workspace fixers.
- private ImmutableDictionary> _fixerToFixableIdsMap = ImmutableDictionary>.Empty;
private readonly Lazy> _lazyFixerToMetadataMap;
+ private readonly ConditionalWeakTable _analyzerReferenceToFixersMap = new();
+ private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeFixProvider = r => new ProjectCodeFixProvider(r);
+ private readonly ImmutableDictionary>> _configurationProvidersMap;
+ private readonly ImmutableArray> _errorLoggers;
- private readonly Func>>> _getFixerPriorityMap;
+ private ImmutableDictionary>>>? _lazyWorkspaceFixersMap;
private ImmutableDictionary>>? _lazyFixerPriorityMap;
- private readonly ConditionalWeakTable _analyzerReferenceToFixersMap;
- private readonly ConditionalWeakTable.CreateValueCallback _createProjectCodeFixProvider;
-
- private readonly ImmutableDictionary>> _configurationProvidersMap;
- private readonly IEnumerable> _errorLoggers;
-
- private ImmutableDictionary
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 be77a7208fd3b..2c109c1af015c 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 209ba3ffddadc..e7ce92f0ebd3d 100644
--- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs
+++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/DocumentPullDiagnosticHandler.cs
@@ -14,12 +14,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 904847297062a..74fe3bdc683b6 100644
--- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs
+++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/Experimental/ExperimentalDocumentPullDiagnosticsHandler.cs
@@ -21,6 +21,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;
@@ -34,8 +35,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 9eaf7950dada5..66b6856ff74e6 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 6026835647f00..fa26460db8f72 100644
--- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs
+++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs
@@ -14,10 +14,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 59177647804ed..a4d23525bf948 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]
@@ -579,7 +587,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);
@@ -613,7 +621,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);
@@ -638,7 +646,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);
@@ -668,7 +676,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);
@@ -705,7 +713,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);
@@ -726,7 +734,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);
@@ -761,7 +769,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
@@ -833,7 +841,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.
@@ -892,7 +900,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
@@ -949,7 +957,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
@@ -1004,7 +1012,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
@@ -1060,7 +1068,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
@@ -1117,8 +1125,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)
@@ -1136,8 +1142,6 @@ private static async Task