From 8c62f62cd03765c54f194a5aa395aedc38e02402 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Thu, 28 May 2020 00:41:53 +0200 Subject: [PATCH 01/22] Fix exception in declare-as-nullable code fix (#44338) IParameterSymbol.DeclaringSyntaxReferences will be empty if the analyzed method call is targeting a method with no source code available (external assembly). --- .../CSharpDeclareAsNullableCodeFixTests.cs | 48 +++++++++++++++++++ .../CSharpDeclareAsNullableCodeFixProvider.cs | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index 14805370be12d..60509141f8ff6 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -832,6 +832,22 @@ void M2(string? x) { } }", parameters: s_nullableFeature); } + [Fact] + [WorkItem(44338, "https://github.com/dotnet/roslyn/issues/44338")] + public async Task NoFixInvocationOfExternalMethod_NamedArgument() + { + await TestMissingInRegularAndScriptAsync( +@"#nullable enable +class Program +{ + void M() + { + var list = new System.Collections.Generic.List(); + list.Add(item: [|null|]); + } +}", parameters: s_nullableFeature); + } + [Fact] public async Task FixInvocation_NamedArgument_OutOfOrder() { @@ -856,6 +872,22 @@ void M2(int i, string? x) { } }", parameters: s_nullableFeature); } + [Fact] + [WorkItem(44338, "https://github.com/dotnet/roslyn/issues/44338")] + public async Task NoFixInvocationOfExternalMethod_NamedArgument_OutOfOrder() + { + await TestMissingInRegularAndScriptAsync( +@"#nullable enable +class Program +{ + void M() + { + var dict = new System.Collections.Generic.Dictionary(); + dict.Add(value: 0, key: [|null|]); + } +}", parameters: s_nullableFeature); + } + [Fact] public async Task FixInvocation_NamedArgument_Partial() { @@ -896,6 +928,22 @@ void M2(string? x) { } }", parameters: s_nullableFeature); } + [Fact] + [WorkItem(44338, "https://github.com/dotnet/roslyn/issues/44338")] + public async Task NoFixInvocationOfExternalMethod_PositionArgument() + { + await TestMissingInRegularAndScriptAsync( +@"#nullable enable +class Program +{ + void M() + { + var list = new System.Collections.Generic.List(); + list.Add([|null|]); + } +}", parameters: s_nullableFeature); + } + [Fact] public async Task FixInvocation_PositionArgument_SecondPosition() { diff --git a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index 51fd0291f8510..4727fc3c255f0 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -332,7 +332,7 @@ declarator.Parent is VariableDeclarationSyntax declaration && static TypeSyntax? TryGetParameterTypeSyntax(IParameterSymbol? parameterSymbol) { if (parameterSymbol is object && - parameterSymbol.DeclaringSyntaxReferences[0].GetSyntax() is ParameterSyntax parameterSyntax && + parameterSymbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is ParameterSyntax parameterSyntax && parameterSymbol.ContainingSymbol is IMethodSymbol method && method.GetAllMethodSymbolsOfPartialParts().Length == 1) { From a8d8245b71a2ad82d2d0dfe046aec9230e41d353 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 15 Jun 2020 17:46:39 -0700 Subject: [PATCH 02/22] Add API review notes for d16.7 --- docs/compilers/API Notes/6-11-20.md | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/compilers/API Notes/6-11-20.md diff --git a/docs/compilers/API Notes/6-11-20.md b/docs/compilers/API Notes/6-11-20.md new file mode 100644 index 0000000000000..c3dd3d10823e6 --- /dev/null +++ b/docs/compilers/API Notes/6-11-20.md @@ -0,0 +1,61 @@ +### `BaseTypeSyntax` Design + +In master, we currently have added an optional argument list to `SimpleBaseTypeSyntax` for record primary constructors. However, there is an argument to be made that we should add a new subtype of `BaseTypeSyntax`, something like `PrimaryConstructorTypeSyntax`, to represent these new base type clauses. This was an explicit extension point added in the original design for this very purpose. For either of these designs, GetSymbolInfo will behave consistently: if the `BaseTypeSyntax` has an argument list (either because it's a separate type or because it's a `PrimaryConstructorTypeSyntax`), it will return the constructor symbol or candidates as appropriate. If there are no arguments, then it will return nothing. + +Pros for keeping the optional argument list on `SimpleBaseTypeSyntax`: +* The primary constructor arguments feel like an extension to existing nodes, not a totally new node type +* Existing analyzers/fixers might pick things up right without much modification + +Pros for extending `BaseTypeSyntax`: +* This extension point was put in explicitly for this +* Existing code might be less likely to erroneously use it +* There was some feeling that it would serve as a more clear delineation for future additions to this syntax (if such C# features ever come to pass) + +#### Conclusion + +We lean towards extending `BaseTypeSyntax` in the next preview. + +### `CompilationOutputFilePaths` + +The plural when this struct only has one path in it currently felt wrong. Some options for renaming: + +* `CompilationOutputFileInfo` +* `CompilationOutputInfo` +* `CompilationOutputFiles` + +We leave it up to whoever does the rename to choose among these. + +### `SolutionInfo.Create` + +Add `EditorBrowsable(Never)` to the backcompat overload. + +### `SymbolKind.FunctionPointer` + +Suffix with `Type` for consistency with the other kinds that are also `TypeKind`s. + +### `SyntaxFactory.ParseType` + +The new overload should use `ParseOptions` instead of the specific C#/VB type in both factories. +Most of the Parse overloads that take a `ParseOptions` here use the non-specific one, and it can be very convenient for IDE APIs here. +While it is marginally less type-safe, the ergonomic and consistency benefits are a deciding factor. + +### `WithExpressionSyntax.Receiver` + +This should be renamed to `Expression` for consistency with other `SyntaxNode`s. + +### `SymbolFinder.Find(Derived(Classes|Interfaces)|Interfaces)Async` + +We should reconsider adding multiple overloads, and instead remove the default parameters from the existing overload and introduce a new overload with `transitive` set to `true` to match current behavior. +This is our standard modus operandi for these scenarios. + +### `INegatedPatternOperation.NegatedPattern` + +Rename to just `Pattern`. + +### `IWithOperation.Value` + +Rename to `Operand`. + +### `Renamer.RenameDocumentAsync` + +Make this and the related structs internal unless we have a partner team ready to consume the API in 16.7. We can make the API public again when someone is ready to consume and validate the API shape. From 773b34102731a25d517afee3cd653003023ee9f2 Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Tue, 16 Jun 2020 13:47:19 +0200 Subject: [PATCH 03/22] Support FieldDeclarationSyntax in CS8618 fixer --- .../CSharpDeclareAsNullableCodeFixTests.cs | 19 +++++++++++++++++++ .../CSharpDeclareAsNullableCodeFixProvider.cs | 10 +++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index 14805370be12d..7ace3e6d8071d 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -966,6 +966,25 @@ class C class C { string? S { get; } +}", + parameters: s_nullableFeature); + } + + [Fact] + public async Task FixFieldDeclaration_Unassigned() + { + await TestInRegularAndScript1Async( +@"#nullable enable + +class C +{ + private string [|_value|]; +}", +@"#nullable enable + +class C +{ + private string? _value; }", parameters: s_nullableFeature); } diff --git a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index 51fd0291f8510..9e3adcae8e70d 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -280,6 +280,13 @@ declarator.Parent is VariableDeclarationSyntax declaration && return propertyDeclarationSyntax.Type; } + // string x; + // Unassigned value that's not marked as null + if (node is FieldDeclarationSyntax fieldDeclarationSyntax) + { + return fieldDeclarationSyntax.Declaration.Type; + } + // void M(string x = null) { } if (node.Parent.IsParentKind(SyntaxKind.Parameter, out ParameterSyntax? optionalParameter)) { @@ -351,7 +358,8 @@ private static bool IsExpressionSupported(SyntaxNode node) SyntaxKind.DefaultLiteralExpression, SyntaxKind.ConditionalExpression, SyntaxKind.ConditionalAccessExpression, - SyntaxKind.PropertyDeclaration); + SyntaxKind.PropertyDeclaration, + SyntaxKind.FieldDeclaration); } private class MyCodeAction : CodeAction.DocumentChangeAction From 5b3577bb0c16eb1307f6fbdb26df09047540f624 Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Wed, 17 Jun 2020 13:13:13 +0200 Subject: [PATCH 04/22] Fix the failure --- .../Nullable/CSharpDeclareAsNullableCodeFixProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index 9e3adcae8e70d..a8ec21fff2c29 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -282,9 +282,9 @@ declarator.Parent is VariableDeclarationSyntax declaration && // string x; // Unassigned value that's not marked as null - if (node is FieldDeclarationSyntax fieldDeclarationSyntax) + if (node is VariableDeclaratorSyntax && node.Parent is VariableDeclarationSyntax variableDeclarationSyntax) { - return fieldDeclarationSyntax.Declaration.Type; + return variableDeclarationSyntax.Type; } // void M(string x = null) { } @@ -359,7 +359,7 @@ private static bool IsExpressionSupported(SyntaxNode node) SyntaxKind.ConditionalExpression, SyntaxKind.ConditionalAccessExpression, SyntaxKind.PropertyDeclaration, - SyntaxKind.FieldDeclaration); + SyntaxKind.VariableDeclarator); } private class MyCodeAction : CodeAction.DocumentChangeAction From 1477f00d7d8ed82d1a6f909bb6b01739e931583d Mon Sep 17 00:00:00 2001 From: Youssef Victor <31348972+Youssef1313@users.noreply.github.com> Date: Wed, 17 Jun 2020 13:14:58 +0200 Subject: [PATCH 05/22] Update CSharpDeclareAsNullableCodeFixTests.cs --- .../Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index 7ace3e6d8071d..a6d00e33f9785 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -971,6 +971,7 @@ class C } [Fact] + [WorkItem(44983, "https://github.com/dotnet/roslyn/issues/44983")] public async Task FixFieldDeclaration_Unassigned() { await TestInRegularAndScript1Async( From a6a49efcc69c98beb716d21d49ef74a5432d43cc Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Wed, 17 Jun 2020 21:29:17 +0200 Subject: [PATCH 06/22] Apply feedback --- .../Nullable/CSharpDeclareAsNullableCodeFixProvider.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index a8ec21fff2c29..9bde063178b72 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -282,9 +282,10 @@ declarator.Parent is VariableDeclarationSyntax declaration && // string x; // Unassigned value that's not marked as null - if (node is VariableDeclaratorSyntax && node.Parent is VariableDeclarationSyntax variableDeclarationSyntax) + if (node is VariableDeclaratorSyntax { Parent: VariableDeclarationSyntax { Parent: FieldDeclarationSyntax _ } declarationSyntax } && + declarationSyntax.Variables.Count == 1) { - return variableDeclarationSyntax.Type; + return declarationSyntax.Type; } // void M(string x = null) { } From e833d922da8cf7b74a1ac61b6ebce7164a279e4a Mon Sep 17 00:00:00 2001 From: Youssef Victor <31348972+Youssef1313@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:26:29 +0200 Subject: [PATCH 07/22] Update CSharpDeclareAsNullableCodeFixTests.cs --- .../Nullable/CSharpDeclareAsNullableCodeFixTests.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index a6d00e33f9785..ea8e61f090d35 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -990,4 +990,17 @@ class C parameters: s_nullableFeature); } } + + [Fact] + [WorkItem(44983, "https://github.com/dotnet/roslyn/issues/44983")] + public async Task MultipleDeclarator_NoDiagnostic() + { + await TestMissingInRegularAndScriptAsync( +@"#nullable enable +class Program +{ + string s, s2 = ""hello"" +}", parameters: s_nullableFeature); + } + } } From 0335c20a0138577e3683e8299c67bd3de3106c00 Mon Sep 17 00:00:00 2001 From: Youssef Victor <31348972+Youssef1313@users.noreply.github.com> Date: Wed, 17 Jun 2020 23:40:58 +0200 Subject: [PATCH 08/22] Update CSharpDeclareAsNullableCodeFixTests.cs --- .../Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index ea8e61f090d35..d7e4e8705f521 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -989,7 +989,6 @@ class C }", parameters: s_nullableFeature); } - } [Fact] [WorkItem(44983, "https://github.com/dotnet/roslyn/issues/44983")] From 617846f202b28a258f855f9caf05bc48b0c14de8 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 17 Jun 2020 14:55:09 -0700 Subject: [PATCH 09/22] Update src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs --- .../Nullable/CSharpDeclareAsNullableCodeFixTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index d7e4e8705f521..663fe9ab7bc9c 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -970,8 +970,7 @@ class C parameters: s_nullableFeature); } - [Fact] - [WorkItem(44983, "https://github.com/dotnet/roslyn/issues/44983")] + [Fact, WorkItem(44983, "https://github.com/dotnet/roslyn/issues/44983")] public async Task FixFieldDeclaration_Unassigned() { await TestInRegularAndScript1Async( From 27f94efeff5bf3865bf181cf7ff2371d14c31664 Mon Sep 17 00:00:00 2001 From: Youssef Victor <31348972+Youssef1313@users.noreply.github.com> Date: Thu, 18 Jun 2020 08:36:55 +0200 Subject: [PATCH 10/22] Add missing semi-colon in test --- .../Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index 663fe9ab7bc9c..dd7451857dcfd 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -997,7 +997,7 @@ await TestMissingInRegularAndScriptAsync( @"#nullable enable class Program { - string s, s2 = ""hello"" + string s, s2 = ""hello""; }", parameters: s_nullableFeature); } } From 2313621c0c1d66f247c84749f721598e249e18f6 Mon Sep 17 00:00:00 2001 From: Youssef Victor <31348972+Youssef1313@users.noreply.github.com> Date: Thu, 18 Jun 2020 20:35:15 +0200 Subject: [PATCH 11/22] Update src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs Co-authored-by: Jason Malinowski --- .../Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs index dd7451857dcfd..9a49294320dd5 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Nullable/CSharpDeclareAsNullableCodeFixTests.cs @@ -997,7 +997,7 @@ await TestMissingInRegularAndScriptAsync( @"#nullable enable class Program { - string s, s2 = ""hello""; + string [|s|], s2 = ""hello""; }", parameters: s_nullableFeature); } } From 6ec37b1b0727a20bd5e9fb7ad3c5a5b0c0459e12 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 19 Jun 2020 15:37:49 -0700 Subject: [PATCH 12/22] Make VisitType tail recursive in more cases. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 50 +++++++++++++++---- .../CSharp/Test/Emit/Emit/EndToEndTests.cs | 4 +- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 1a87abac041f6..97181e9224b6f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -656,12 +656,20 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS case TypeKind.Struct: case TypeKind.Interface: case TypeKind.Delegate: - foreach (var typeArg in ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics) + var namedType = (NamedTypeSymbol)current; + if (namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.IsEmpty) + { + return null; + } + + int i; + for (i = 0; i < namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Length - 1; i++) { // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[i], canDigThroughNullable); var result = VisitType( - typeWithAnnotationsOpt: canDigThroughNullable ? default : typeArg, - type: canDigThroughNullable ? typeArg.NullableUnderlyingTypeOrSelf : null, + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, typeWithAnnotationsPredicate, typePredicate, arg, @@ -672,7 +680,9 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS return result; } } - return null; + + next = namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[i]; + break; case TypeKind.Array: next = ((ArrayTypeSymbol)current).ElementTypeWithAnnotations; @@ -683,7 +693,15 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS break; case TypeKind.FunctionPointer: - return visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable); + { + var result = visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable, out next); + if (result is object) + { + return result; + } + + break; + } default: throw ExceptionUtilities.UnexpectedValue(current.TypeKind); @@ -694,9 +712,18 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS type = canDigThroughNullable ? next.NullableUnderlyingTypeOrSelf : null; } - static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable) + static (TypeWithAnnotations, TypeSymbol?) getNextIterationElements(TypeWithAnnotations type, bool canDigThroughNullable) + => canDigThroughNullable ? (default(TypeWithAnnotations), type.NullableUnderlyingTypeOrSelf) : (type, null); + + static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, out TypeWithAnnotations next) { MethodSymbol currentPointer = type.Signature; + if (currentPointer.ParameterCount == 0) + { + next = currentPointer.ReturnTypeWithAnnotations; + return null; + } + var result = VisitType( typeWithAnnotationsOpt: canDigThroughNullable ? default : currentPointer.ReturnTypeWithAnnotations, type: canDigThroughNullable ? currentPointer.ReturnTypeWithAnnotations.NullableUnderlyingTypeOrSelf : null, @@ -707,14 +734,17 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS useDefaultType); if (result is object) { + next = default; return result; } - foreach (var parameter in currentPointer.Parameters) + int i; + for (i = 0; i < currentPointer.ParameterCount - 1; i++) { + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(currentPointer.Parameters[i].TypeWithAnnotations, canDigThroughNullable); result = VisitType( - typeWithAnnotationsOpt: canDigThroughNullable ? default : parameter.TypeWithAnnotations, - type: canDigThroughNullable ? parameter.TypeWithAnnotations.NullableUnderlyingTypeOrSelf : null, + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, typeWithAnnotationsPredicate, typePredicate, arg, @@ -722,10 +752,12 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS useDefaultType); if (result is object) { + next = default; return result; } } + next = currentPointer.Parameters[i].TypeWithAnnotations; return null; } } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EndToEndTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EndToEndTests.cs index 481f268e5dbd1..0c20a4cc2e280 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EndToEndTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EndToEndTests.cs @@ -227,7 +227,7 @@ public static void Main(string[] args) } } - [ConditionalFact(typeof(WindowsOnly))] + [ConditionalFact(typeof(WindowsOrLinuxOnly))] public void NestedIfStatements() { int nestingLevel = (ExecutionConditionUtil.Architecture, ExecutionConditionUtil.Configuration) switch @@ -273,7 +273,7 @@ static void Main() } [WorkItem(42361, "https://github.com/dotnet/roslyn/issues/42361")] - [ConditionalFact(typeof(WindowsOnly))] + [ConditionalFact(typeof(WindowsOrLinuxOnly))] public void Constraints() { int n = (ExecutionConditionUtil.Architecture, ExecutionConditionUtil.Configuration) switch From 314cbb3d98026cc72521ec2235bfc767c12a5d97 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 22 Jun 2020 13:12:26 -0700 Subject: [PATCH 13/22] Cache `TypeArgumentsWithAnnotationsNoUseSiteDiagnostics` in a local. --- .../CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 97181e9224b6f..07e6a2f6cbfa5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -656,17 +656,17 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS case TypeKind.Struct: case TypeKind.Interface: case TypeKind.Delegate: - var namedType = (NamedTypeSymbol)current; - if (namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.IsEmpty) + var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + if (typeArguments.IsEmpty) { return null; } int i; - for (i = 0; i < namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Length - 1; i++) + for (i = 0; i < typeArguments.Length - 1; i++) { // Let's try to avoid early resolution of nullable types - (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[i], canDigThroughNullable); + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeArguments[i], canDigThroughNullable); var result = VisitType( typeWithAnnotationsOpt: nextTypeWithAnnotations, type: nextType, @@ -681,7 +681,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref HashS } } - next = namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[i]; + next = typeArguments[i]; break; case TypeKind.Array: From 7ca148c85d37b3a19ea3a8ba4e3ecfc9a612f837 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Jun 2020 14:03:49 -0700 Subject: [PATCH 14/22] Support datetime completion in interpolation format clauses. --- ...mpletionCommandHandlerTests_DateAndTime.vb | 230 ++++++++++++++++++ ...mpletionCommandHandlerTests_DateAndTime.vb | 210 ++++++++++++++++ .../DateAndTimeEmbeddedCompletionProvider.cs | 12 +- .../DateAndTimeEmbeddedLanguage.cs | 35 ++- .../DateAndTimePatternDetector.cs | 56 +++-- .../VirtualChars/CSharpVirtualCharService.cs | 13 +- .../Services/SyntaxFacts/CSharpSyntaxKinds.cs | 2 + .../Core/Services/SyntaxFacts/ISyntaxKinds.cs | 2 + .../VisualBasicVirtualCharService.vb | 3 +- .../SyntaxFacts/VisualBasicSyntaxKinds.vb | 2 + 10 files changed, 524 insertions(+), 41 deletions(-) create mode 100644 src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_DateAndTime.vb diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb index bef42e96ac7a6..f857bddccc0ec 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_DateAndTime.vb @@ -162,5 +162,235 @@ class c End If End Using End Function + + + Public Async Function ExplicitInvoke_StringInterpolation1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("G", inlineDescription:=FeaturesResources.general_long_date_time) + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("_ = $""Text {d:G}""", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function ExplicitInvoke_StringInterpolation2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("G", inlineDescription:=FeaturesResources.general_long_date_time) + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("_ = @$""Text {d:G}""", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function ExplicitInvoke_OverwriteExisting_StringInterpolation1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ff") + state.SendDownKey() + Await state.AssertSelectedCompletionItem("FF") + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("_ = $""Text {d:FF}""", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function ExplicitInvoke_OverwriteExisting_StringInterpolation2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ff") + state.SendDownKey() + Await state.AssertSelectedCompletionItem("FF") + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("_ = @$""Text {d:FF}""", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function TypeChar_BeginningOfWord_StringInterpolation1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendTypeChars("f") + Await state.AssertCompletionSession() + Await state.AssertSelectedCompletionItem("f") + End Using + End Function + + + Public Async Function TypeChar_BeginningOfWord_StringInterpolation2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendTypeChars("f") + Await state.AssertCompletionSession() + Await state.AssertSelectedCompletionItem("f") + End Using + End Function + + + Public Async Function TestExample1_StringInterpolation1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( +, showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + state.SendTypeChars("ss") + Await state.AssertSelectedCompletionItem("ss", inlineDescription:=FeaturesResources.second_2_digits) + + Dim description = Await state.GetSelectedItemDescriptionAsync() + Dim text = description.Text + + If CultureInfo.CurrentCulture.Name <> "en-US" Then + Assert.Contains($"hh:mm:ss (en-US) → 01:45:30", text) + Assert.Contains($"hh:mm:ss ({CultureInfo.CurrentCulture.Name}) → 01:45:30", text) + Else + Assert.Contains("hh:mm:ss → 01:45:30", text) + End If + End Using + End Function + + + Public Async Function TestExample1_StringInterpolation2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( +, showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + state.SendTypeChars("ss") + Await state.AssertSelectedCompletionItem("ss", inlineDescription:=FeaturesResources.second_2_digits) + + Dim description = Await state.GetSelectedItemDescriptionAsync() + Dim text = description.Text + + If CultureInfo.CurrentCulture.Name <> "en-US" Then + Assert.Contains($"hh:mm:ss (en-US) → 01:45:30", text) + Assert.Contains($"hh:mm:ss ({CultureInfo.CurrentCulture.Name}) → 01:45:30", text) + Else + Assert.Contains("hh:mm:ss → 01:45:30", text) + End If + End Using + End Function + + + Public Async Function TypeChar_MiddleOfWord_StringInterpolation1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendTypeChars("f") + Await state.AssertNoCompletionSession() + End Using + End Function + + + Public Async Function TypeChar_MiddleOfWord_StringInterpolation2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendTypeChars("f") + Await state.AssertNoCompletionSession() + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_DateAndTime.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_DateAndTime.vb new file mode 100644 index 0000000000000..92a21f677f648 --- /dev/null +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_DateAndTime.vb @@ -0,0 +1,210 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Globalization + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense + <[UseExportProvider]> + Public Class VisualBasicCompletionCommandHandlerTests_DateAndTime + + Public Async Function ExplicitInvoke() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("G", inlineDescription:=FeaturesResources.general_long_date_time) + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("d.ToString(""G"")", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function ExplicitInvoke_OverwriteExisting() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ff") + state.SendDownKey() + Await state.AssertSelectedCompletionItem("FF") + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("d.ToString("":FF"")", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function TypeChar_BeginningOfWord() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendTypeChars("f") + Await state.AssertCompletionSession() + Await state.AssertSelectedCompletionItem("f") + End Using + End Function + + + Public Async Function TypeChar_MiddleOfWord() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendTypeChars("f") + Await state.AssertNoCompletionSession() + End Using + End Function + + + Public Async Function TestExample1() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( +) + + state.SendInvokeCompletionList() + state.SendTypeChars("ss") + Await state.AssertSelectedCompletionItem("ss", inlineDescription:=FeaturesResources.second_2_digits) + + Dim description = Await state.GetSelectedItemDescriptionAsync() + Dim text = description.Text + + If CultureInfo.CurrentCulture.Name <> "en-US" Then + Assert.Contains($"hh:mm:ss (en-US) → 01:45:30", text) + Assert.Contains($"hh:mm:ss ({CultureInfo.CurrentCulture.Name}) → 01:45:30", text) + Else + Assert.Contains("hh:mm:ss → 01:45:30", text) + End If + End Using + End Function + + + Public Async Function ExplicitInvoke_StringInterpolation() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("G", inlineDescription:=FeaturesResources.general_long_date_time) + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("Dim str = $""Text {d:G}""", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function ExplicitInvoke_OverwriteExisting_StringInterpolation() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendInvokeCompletionList() + Await state.AssertSelectedCompletionItem("ff") + state.SendDownKey() + Await state.AssertSelectedCompletionItem("FF") + state.SendTab() + Await state.AssertNoCompletionSession() + Assert.Contains("Dim str = $""Text {d:FF}""", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + Public Async Function TypeChar_BeginningOfWord_StringInterpolation() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendTypeChars("f") + Await state.AssertCompletionSession() + Await state.AssertSelectedCompletionItem("f") + End Using + End Function + + + Public Async Function TestExample1_StringInterpolation() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( +) + + state.SendInvokeCompletionList() + state.SendTypeChars("ss") + Await state.AssertSelectedCompletionItem("ss", inlineDescription:=FeaturesResources.second_2_digits) + + Dim description = Await state.GetSelectedItemDescriptionAsync() + Dim text = description.Text + + If CultureInfo.CurrentCulture.Name <> "en-US" Then + Assert.Contains($"hh:mm:ss (en-US) → 01:45:30", text) + Assert.Contains($"hh:mm:ss ({CultureInfo.CurrentCulture.Name}) → 01:45:30", text) + Else + Assert.Contains("hh:mm:ss → 01:45:30", text) + End If + End Using + End Function + + + Public Async Function TypeChar_MiddleOfWord_StringInterpolation() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + ) + + state.SendTypeChars("f") + Await state.AssertNoCompletionSession() + End Using + End Function + End Class +End Namespace diff --git a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs index 265038edfe578..fbde229e581a3 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/DateAndTime/DateAndTimeEmbeddedCompletionProvider.cs @@ -10,8 +10,10 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.EmbeddedLanguages.DateAndTime; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -80,9 +82,15 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) if (stringTokenOpt == null) return; + var syntaxFacts = document.GetRequiredLanguageService(); var stringToken = stringTokenOpt.Value; - if (position <= stringToken.SpanStart || position >= stringToken.Span.End) - return; + + // If we're not in an interpolation, at least make sure we're within the bounds of the string. + if (stringToken.RawKind != syntaxFacts.SyntaxKinds.InterpolatedStringTextToken) + { + if (position <= stringToken.SpanStart || position >= stringToken.Span.End) + return; + } // Note: it's acceptable if this fails to convert. We just won't show the example in that case. var virtualChars = _language.Info.VirtualCharService.TryConvertToVirtualChars(stringToken); diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs index d9511cfc8e75f..e0f5a878eef9d 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimeEmbeddedLanguage.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification.Classifiers; @@ -28,16 +29,42 @@ public DateAndTimeEmbeddedLanguage(EmbeddedLanguageInfo info) internal async Task TryGetDateAndTimeTokenAtPositionAsync( Document document, int position, CancellationToken cancellationToken) { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var token = root.FindToken(position); var syntaxFacts = document.GetRequiredLanguageService(); - if (!DateAndTimePatternDetector.IsPossiblyDateAndTimeToken(token, syntaxFacts, out _, out _)) + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var token = GetToken(syntaxFacts, root, position); + if (!DateAndTimePatternDetector.IsPossiblyDateAndTimeArgumentToken(token, syntaxFacts, out _, out _) && + token.RawKind != syntaxFacts.SyntaxKinds.InterpolatedStringTextToken) + { return null; + } var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var detector = DateAndTimePatternDetector.TryGetOrCreate(semanticModel, this.Info); - return detector != null && detector.IsDateAndTimeToken(token, cancellationToken) + return detector != null && detector.IsDateAndTimeToken(token, syntaxFacts, cancellationToken) ? token : (SyntaxToken?)null; } + + private static SyntaxToken GetToken(ISyntaxFactsService syntaxFacts, SyntaxNode root, int position) + { + var token = root.FindToken(position); + var syntaxKinds = syntaxFacts.SyntaxKinds; + + if (token.RawKind == syntaxKinds.CloseBraceToken) + { + // Might be here: $"Date is: {date:$$}" or + // $"Date is: {date:G$$}" + // + // If so, we want to return the InterpolatedStringTextToken following the `:` + var previous = token.GetPreviousToken(); + if (previous.RawKind == syntaxKinds.InterpolatedStringTextToken) + return previous; + + if (previous.RawKind == syntaxKinds.ColonToken) + return previous.GetNextToken(includeZeroWidth: true); + } + + return token; + } } } diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs index 2e608aec657b1..c7352c0dbc781 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs @@ -77,7 +77,7 @@ public DateAndTimePatternDetector( /// format string passed into an method call. If so, and will be the argument and invocatoin the string literal was passed as. /// - public static bool IsPossiblyDateAndTimeToken( + public static bool IsPossiblyDateAndTimeArgumentToken( SyntaxToken token, ISyntaxFacts syntaxFacts, [NotNullWhen(true)] out SyntaxNode? argumentNode, [NotNullWhen(true)] out SyntaxNode? invocationExpression) @@ -131,34 +131,40 @@ private static bool IsMethodArgument(SyntaxToken token, ISyntaxFacts syntaxFacts => syntaxFacts.IsLiteralExpression(token.Parent) && syntaxFacts.IsArgument(token.Parent!.Parent); - public bool IsDateAndTimeToken(SyntaxToken token, CancellationToken cancellationToken) + public bool IsDateAndTimeToken(SyntaxToken token, ISyntaxFacts syntaxFacts, CancellationToken cancellationToken) { - if (!IsPossiblyDateAndTimeToken( + if (IsPossiblyDateAndTimeArgumentToken( token, _info.SyntaxFacts, out var argumentNode, out var invocationOrCreation)) { - return false; - } - - // if we couldn't determine the arg name or arg index, can't proceed. - var (argName, argIndex) = GetArgumentNameOrIndex(argumentNode); - if (argName == null && argIndex == null) - return false; - - // If we had a specified arg name and it isn't 'format', then it's not a DateTime - // 'format' param we care about. - if (argName != null && argName != FormatName) - return false; - - var symbolInfo = _semanticModel.GetSymbolInfo(invocationOrCreation, cancellationToken); - var method = symbolInfo.Symbol; - if (TryAnalyzeInvocation(method, argName, argIndex)) - return true; + // if we couldn't determine the arg name or arg index, can't proceed. + var (argName, argIndex) = GetArgumentNameOrIndex(argumentNode); + if (argName == null && argIndex == null) + return false; + + // If we had a specified arg name and it isn't 'format', then it's not a DateTime + // 'format' param we care about. + if (argName != null && argName != FormatName) + return false; + + var symbolInfo = _semanticModel.GetSymbolInfo(invocationOrCreation, cancellationToken); + var method = symbolInfo.Symbol; + if (TryAnalyzeInvocation(method, argName, argIndex)) + return true; - foreach (var candidate in symbolInfo.CandidateSymbols) + foreach (var candidate in symbolInfo.CandidateSymbols) + { + if (TryAnalyzeInvocation(candidate, argName, argIndex)) + return true; + } + } + else if (token.RawKind == syntaxFacts.SyntaxKinds.InterpolatedStringTextToken) { - if (TryAnalyzeInvocation(candidate, argName, argIndex)) - return true; + var interpolationFormatClause = token.Parent!; + var interpolation = interpolationFormatClause.Parent!; + var expression = syntaxFacts.GetExpressionOfInterpolation(interpolation); + var type = _semanticModel.GetTypeInfo(expression, cancellationToken).Type; + return IsDateTimeType(type); } return false; @@ -187,8 +193,8 @@ private bool TryAnalyzeInvocation(ISymbol? symbol, string? argName, int? argInde IsDateTimeType(method.ContainingType) && AnalyzeStringLiteral(method, argName, argIndex); - private bool IsDateTimeType(INamedTypeSymbol containingType) - => _dateTimeType.Equals(containingType) || _dateTimeOffsetType.Equals(containingType); + private bool IsDateTimeType(ITypeSymbol? type) + => _dateTimeType.Equals(type) || _dateTimeOffsetType.Equals(type); private static bool AnalyzeStringLiteral(IMethodSymbol method, string? argName, int? argIndex) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs index 06f24bdaaa8d4..f3e0ba619b3d1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs @@ -39,9 +39,7 @@ protected override VirtualCharSequence TryConvertToVirtualCharsWorker(SyntaxToke // we won't classify any escape characters. And there is no way that these strings would // be Regex/Json snippets. So it's easier to just bail out and return nothing. if (IsInDirective(token.Parent)) - { return default; - } Debug.Assert(!token.ContainsDiagnostics); if (token.Kind() == SyntaxKind.StringLiteralToken) @@ -52,16 +50,15 @@ protected override VirtualCharSequence TryConvertToVirtualCharsWorker(SyntaxToke } if (token.Kind() == SyntaxKind.CharacterLiteralToken) - { return TryConvertStringToVirtualChars(token, "'", "'", escapeBraces: false); - } if (token.Kind() == SyntaxKind.InterpolatedStringTextToken) { - // The sections between `}` and `{` are InterpolatedStringTextToken *as are* the - // format specifiers in an interpolated string. We only want to get the virtual - // chars for this first type. - if (token.Parent.Parent is InterpolatedStringExpressionSyntax interpolatedString) + var parent = token.Parent; + if (parent is InterpolationFormatClauseSyntax) + parent = parent.Parent; + + if (parent.Parent is InterpolatedStringExpressionSyntax interpolatedString) { return interpolatedString.StringStartToken.Kind() == SyntaxKind.InterpolatedVerbatimStringStartToken ? TryConvertVerbatimStringToVirtualChars(token, "", "", escapeBraces: true) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 19068c7cfd8b1..c49794f836e55 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -28,6 +28,8 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int SingleLineCommentTrivia => (int)SyntaxKind.SingleLineCommentTrivia; public int? MultiLineCommentTrivia => (int)SyntaxKind.MultiLineCommentTrivia; + public int CloseBraceToken => (int)SyntaxKind.CloseBraceToken; + public int ColonToken => (int)SyntaxKind.ColonToken; public int CharacterLiteralToken => (int)SyntaxKind.CharacterLiteralToken; public int DotToken => (int)SyntaxKind.DotToken; public int InterpolatedStringTextToken => (int)SyntaxKind.InterpolatedStringTextToken; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index 57769b66c2cf9..d4f73a8d16531 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -52,6 +52,8 @@ internal interface ISyntaxKinds #region tokens + int CloseBraceToken { get; } + int ColonToken { get; } int DotToken { get; } int EndOfFileToken { get; } int HashToken { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/EmbeddedLanguages/VirtualChars/VisualBasicVirtualCharService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/EmbeddedLanguages/VirtualChars/VisualBasicVirtualCharService.vb index b39967bd7c37e..294ace7927880 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/EmbeddedLanguages/VirtualChars/VisualBasicVirtualCharService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/EmbeddedLanguages/VirtualChars/VisualBasicVirtualCharService.vb @@ -32,8 +32,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EmbeddedLanguages.VirtualChars Return TryConvertSimpleDoubleQuoteString(token, """", """", escapeBraces:=False) End If - If token.Kind() = SyntaxKind.InterpolatedStringTextToken AndAlso - TypeOf token.Parent.Parent Is InterpolatedStringExpressionSyntax Then + If token.Kind() = SyntaxKind.InterpolatedStringTextToken Then Return TryConvertSimpleDoubleQuoteString(token, "", "", escapeBraces:=True) End If diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index f53501d167039..4c0033c41086c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -26,6 +26,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Public ReadOnly Property SingleLineCommentTrivia As Integer = SyntaxKind.CommentTrivia Implements ISyntaxKinds.SingleLineCommentTrivia Private ReadOnly Property MultiLineCommentTrivia As Integer? = Nothing Implements ISyntaxKinds.MultiLineCommentTrivia + Public ReadOnly Property CloseBraceToken As Integer = SyntaxKind.CloseBraceToken Implements ISyntaxKinds.CloseBraceToken + Public ReadOnly Property ColonToken As Integer = SyntaxKind.ColonToken Implements ISyntaxKinds.ColonToken Public ReadOnly Property CharacterLiteralToken As Integer = SyntaxKind.CharacterLiteralToken Implements ISyntaxKinds.CharacterLiteralToken Public ReadOnly Property DotToken As Integer = SyntaxKind.DotToken Implements ISyntaxKinds.DotToken Public ReadOnly Property InterpolatedStringTextToken As Integer = SyntaxKind.InterpolatedStringTextToken Implements ISyntaxKinds.InterpolatedStringTextToken From 45588ccd6084ac139def30b05466c7c7375989cb Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Mon, 22 Jun 2020 14:19:27 -0700 Subject: [PATCH 15/22] Safety --- .../LanguageServices/DateAndTimePatternDetector.cs | 9 ++++++--- .../CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs | 2 ++ .../Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs | 6 ++++++ .../Services/SyntaxFacts/VisualBasicSyntaxKinds.vb | 2 ++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs b/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs index c7352c0dbc781..7cac89ce2c2d8 100644 --- a/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs +++ b/src/Workspaces/Core/Portable/EmbeddedLanguages/DateAndTime/LanguageServices/DateAndTimePatternDetector.cs @@ -162,9 +162,12 @@ public bool IsDateAndTimeToken(SyntaxToken token, ISyntaxFacts syntaxFacts, Canc { var interpolationFormatClause = token.Parent!; var interpolation = interpolationFormatClause.Parent!; - var expression = syntaxFacts.GetExpressionOfInterpolation(interpolation); - var type = _semanticModel.GetTypeInfo(expression, cancellationToken).Type; - return IsDateTimeType(type); + if (interpolation!.RawKind == syntaxFacts.SyntaxKinds.Interpolation) + { + var expression = syntaxFacts.GetExpressionOfInterpolation(interpolation); + var type = _semanticModel.GetTypeInfo(expression, cancellationToken).Type; + return IsDateTimeType(type); + } } return false; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index c49794f836e55..cafb34ad6d784 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -90,5 +90,7 @@ public TSyntaxKind Convert(int kind) where TSyntaxKind : struct public int TypeArgumentList => (int)SyntaxKind.TypeArgumentList; public int? GlobalStatement => (int)SyntaxKind.GlobalStatement; + + public int Interpolation => (int)SyntaxKind.Interpolation; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index d4f73a8d16531..e158b99e5645b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -133,5 +133,11 @@ internal interface ISyntaxKinds int TypeArgumentList { get; } #endregion + + #region other + + int Interpolation { get; } + + #endregion } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 4c0033c41086c..a997fdad3a43b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -90,5 +90,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Public ReadOnly Property TypeArgumentList As Integer = SyntaxKind.TypeArgumentList Implements ISyntaxKinds.TypeArgumentList Public ReadOnly Property GlobalStatement As Integer? = Nothing Implements ISyntaxKinds.GlobalStatement + + Public ReadOnly Property Interpolation As Integer = SyntaxKind.Interpolation Implements ISyntaxKinds.Interpolation End Class End Namespace From a69d15b974b7beab13a88f028c370244ea8b8c48 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 22 Jun 2020 17:48:52 -0700 Subject: [PATCH 16/22] Remove IActiveStatementSpanTracker from the MEF catalog This reverts commit d04ebc2e1e0f0738998bd0806dcb19114adb683c. --- .../CSharpEditAndContinueAnalyzerTests.cs | 46 +++++----- .../CSharpEditAndContinueTestHelpers.cs | 10 +- .../Helpers/EditingTestBase.cs | 15 +-- .../EditAndContinue/Helpers/Extensions.cs | 15 +-- .../EditAndContinue/StatementEditingTests.cs | 17 +--- .../ActiveStatementSpanTracker.cs | 41 --------- .../ActiveStatementSpanTrackerFactory.cs | 57 ++++++++++++ .../EditAndContinueWorkspaceServiceTests.cs | 14 +-- .../EditSessionActiveStatementsTests.cs | 4 +- .../EditAndContinueTestHelpers.cs | 13 +-- .../TestActiveStatementSpanTracker.cs | 14 --- .../TestActiveStatementSpanTrackerFactory.cs | 30 ++++++ .../Helpers/EditingTestBase.vb | 11 ++- .../EditAndContinue/Helpers/Extensions.vb | 33 +++---- .../VisualBasicEditAndContinueTestHelpers.vb | 24 +++-- .../EditAndContinue/RudeEditStatementTests.vb | 92 +++++++------------ ...VisualBasicEditAndContinueAnalyzerTests.vb | 38 ++++---- .../CSharpEditAndContinueAnalyzer.cs | 23 ++++- .../AbstractEditAndContinueAnalyzer.cs | 29 +++--- .../IActiveStatementSpanTracker.cs | 3 +- .../IActiveStatementSpanTrackerFactory.cs | 13 +++ .../VisualBasicEditAndContinueAnalyzer.vb | 25 +++-- 22 files changed, 298 insertions(+), 269 deletions(-) delete mode 100644 src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTracker.cs create mode 100644 src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs create mode 100644 src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTrackerFactory.cs create mode 100644 src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTrackerFactory.cs diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index 571bdfcaaf581..aed75becc2d53 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -28,7 +28,7 @@ public class CSharpEditAndContinueAnalyzerTests { private static readonly IExportProviderFactory s_exportProviderFactoryWithTestActiveStatementSpanTracker = ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic - .WithPart(typeof(TestActiveStatementSpanTracker))); + .WithPart(typeof(TestActiveStatementSpanTrackerFactory))); #region Helpers @@ -266,7 +266,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); var oldSolution = workspace.CurrentSolution; @@ -287,7 +286,8 @@ public static void Main() var oldStatementSyntax = oldSyntaxRoot.FindNode(oldStatementTextSpan); var baseActiveStatements = ImmutableArray.Create(ActiveStatementsDescription.CreateActiveStatement(ActiveStatementFlags.IsLeafFrame, oldStatementSpan, DocumentId.CreateNewId(ProjectId.CreateNewId()))); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newDocument, CancellationToken.None); @@ -324,7 +324,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); var oldSolution = workspace.CurrentSolution; @@ -334,7 +333,8 @@ public static void Main() var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None); @@ -355,13 +355,13 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); var oldProject = workspace.CurrentSolution.Projects.Single(); var oldDocument = oldProject.Documents.Single(); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, oldDocument, CancellationToken.None); @@ -391,7 +391,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); @@ -403,7 +402,8 @@ public static void Main() var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None); @@ -424,7 +424,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); var experimentalFeatures = new Dictionary(); // no experimental features to enable var experimental = TestOptions.Regular.WithFeatures(experimentalFeatures); @@ -437,7 +436,8 @@ public static void Main() var documentId = oldDocument.Id; var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, oldDocument, CancellationToken.None); @@ -473,7 +473,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); var featuresToEnable = new Dictionary() { { feature, "enabled" } }; var experimental = TestOptions.Regular.WithFeatures(featuresToEnable); @@ -489,7 +488,8 @@ public static void Main() var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None); @@ -513,7 +513,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); @@ -523,7 +522,8 @@ public static void Main() var documentId = oldDocument.Id; var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, oldDocument, CancellationToken.None); @@ -555,7 +555,6 @@ public static void Main() } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); @@ -567,7 +566,8 @@ public static void Main() var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None); @@ -599,7 +599,6 @@ public static void Main(Bar x) } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); @@ -611,7 +610,8 @@ public static void Main(Bar x) var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); var result = await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None); @@ -642,7 +642,6 @@ public class D } } "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); // fork the solution to introduce a change @@ -664,7 +663,8 @@ public class D var result = new List(); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); foreach (var changedDocumentId in changedDocuments) { @@ -692,7 +692,6 @@ public static void Main() "; var source2 = @" "; - var analyzer = new CSharpEditAndContinueAnalyzer(); using var workspace = TestWorkspace.CreateCSharp(source1, exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); @@ -714,7 +713,8 @@ public static void Main() var result = new List(); var baseActiveStatements = ImmutableArray.Create(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + var analyzer = new CSharpEditAndContinueAnalyzer(spanTracker); foreach (var changedDocumentId in changedDocuments) { diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs index b5d9d8d9fa547..ae1e3df8e6f9d 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs @@ -19,20 +19,20 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue { internal sealed class CSharpEditAndContinueTestHelpers : EditAndContinueTestHelpers { + private readonly CSharpEditAndContinueAnalyzer _analyzer = new CSharpEditAndContinueAnalyzer(new TestActiveStatementSpanTracker()); + private readonly ImmutableArray _fxReferences; - internal static CSharpEditAndContinueTestHelpers Instance + internal static CSharpEditAndContinueTestHelpers CreateInstance() => new CSharpEditAndContinueTestHelpers(TargetFramework.Mscorlib46Extended); - internal static CSharpEditAndContinueTestHelpers Instance40 + internal static CSharpEditAndContinueTestHelpers CreateInstance40() => new CSharpEditAndContinueTestHelpers(TargetFramework.Mscorlib40AndSystemCore); - private static readonly CSharpEditAndContinueAnalyzer s_analyzer = new CSharpEditAndContinueAnalyzer(); - public CSharpEditAndContinueTestHelpers(TargetFramework targetFramework) => _fxReferences = TargetFrameworkUtil.GetReferences(targetFramework); - public override AbstractEditAndContinueAnalyzer Analyzer => s_analyzer; + public override AbstractEditAndContinueAnalyzer Analyzer => _analyzer; public override Compilation CreateLibraryCompilation(string name, IEnumerable trees) => CSharpCompilation.Create("New", trees, _fxReferences, TestOptions.UnsafeReleaseDll); diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs index a9806a74c59ae..90bc7d8d3dbc6 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs @@ -11,16 +11,17 @@ using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue; using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { - [UseExportProvider] public abstract class EditingTestBase : CSharpTestBase { - internal static readonly CSharpEditAndContinueAnalyzer Analyzer = new CSharpEditAndContinueAnalyzer(); + internal static CSharpEditAndContinueAnalyzer CreateAnalyzer() + { + return new CSharpEditAndContinueAnalyzer(new TestActiveStatementSpanTracker()); + } internal enum MethodKind { @@ -45,7 +46,7 @@ internal static SemanticEditDescription SemanticEdit(SemanticEditKind kind, Func => new SemanticEditDescription(kind, symbolProvider, null, preserveLocalVariables); private static SyntaxTree ParseSource(string source) - => CSharpEditAndContinueTestHelpers.Instance.ParseText(ActiveStatementsDescription.ClearTags(source)); + => CSharpEditAndContinueTestHelpers.CreateInstance().ParseText(ActiveStatementsDescription.ClearTags(source)); internal static EditScript GetTopEdits(string src1, string src2) { @@ -74,7 +75,7 @@ internal static Match GetMethodMatch(string src1, string src2, Metho var m2 = MakeMethodBody(src2, kind); var diagnostics = new List(); - var match = Analyzer.GetTestAccessor().ComputeBodyMatch(m1, m2, Array.Empty(), diagnostics, out var oldHasStateMachineSuspensionPoint, out var newHasStateMachineSuspensionPoint); + var match = CreateAnalyzer().GetTestAccessor().ComputeBodyMatch(m1, m2, Array.Empty(), diagnostics, out var oldHasStateMachineSuspensionPoint, out var newHasStateMachineSuspensionPoint); var needsSyntaxMap = oldHasStateMachineSuspensionPoint && newHasStateMachineSuspensionPoint; Assert.Equal(kind != MethodKind.Regular && kind != MethodKind.ConstructorWithParameters, needsSyntaxMap); @@ -90,7 +91,7 @@ internal static Match GetMethodMatch(string src1, string src2, Metho internal static IEnumerable> GetMethodMatches(string src1, string src2, MethodKind kind = MethodKind.Regular) { var methodMatch = GetMethodMatch(src1, src2, kind); - return EditAndContinueTestHelpers.GetMethodMatches(Analyzer, methodMatch); + return EditAndContinueTestHelpers.GetMethodMatches(CreateAnalyzer(), methodMatch); } public static MatchingPairs ToMatchingPairs(Match match) @@ -143,7 +144,7 @@ internal static void VerifyPreserveLocalVariables(EditScript edits, var body2 = ((MethodDeclarationSyntax)SyntaxFactory.SyntaxTree(decl2).GetRoot()).Body; var diagnostics = new List(); - var match = Analyzer.GetTestAccessor().ComputeBodyMatch(body1, body2, Array.Empty(), diagnostics, out var oldHasStateMachineSuspensionPoint, out var newHasStateMachineSuspensionPoint); + var match = CreateAnalyzer().GetTestAccessor().ComputeBodyMatch(body1, body2, Array.Empty(), diagnostics, out var oldHasStateMachineSuspensionPoint, out var newHasStateMachineSuspensionPoint); var needsSyntaxMap = oldHasStateMachineSuspensionPoint && newHasStateMachineSuspensionPoint; // Active methods are detected to preserve local variables for variable mapping and diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs index 3abf7cac61826..a97062e54ae2c 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs @@ -9,25 +9,18 @@ using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue; -using Microsoft.CodeAnalysis.Editor.UnitTests; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.Composition; using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { internal static class Extensions { - private static readonly IExportProviderFactory s_exportProviderFactoryWithTestActiveStatementSpanTracker = - ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic - .WithPart(typeof(TestActiveStatementSpanTracker))); - internal static void VerifyUnchangedDocument( string source, ActiveStatementsDescription description) { - CSharpEditAndContinueTestHelpers.Instance.VerifyUnchangedDocument( + CSharpEditAndContinueTestHelpers.CreateInstance().VerifyUnchangedDocument( ActiveStatementsDescription.ClearTags(source), description.OldStatements, description.OldTrackingSpans, @@ -48,7 +41,7 @@ internal static void VerifyRudeDiagnostics( ActiveStatementsDescription description, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - CSharpEditAndContinueTestHelpers.Instance.VerifyRudeDiagnostics( + CSharpEditAndContinueTestHelpers.CreateInstance().VerifyRudeDiagnostics( editScript, description, expectedDiagnostics); @@ -60,7 +53,7 @@ internal static void VerifyLineEdits( IEnumerable expectedNodeUpdates, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - CSharpEditAndContinueTestHelpers.Instance.VerifyLineEdits( + CSharpEditAndContinueTestHelpers.CreateInstance().VerifyLineEdits( editScript, expectedLineEdits, expectedNodeUpdates, @@ -120,11 +113,9 @@ internal static void VerifySemantics( DiagnosticDescription? expectedDeclarationError = null, RudeEditDiagnosticDescription[]? expectedDiagnostics = null) { - using var workspace = TestWorkspace.CreateCSharp("", exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); foreach (var targetFramework in targetFrameworks ?? new[] { TargetFramework.NetStandard20, TargetFramework.NetCoreApp30 }) { new CSharpEditAndContinueTestHelpers(targetFramework).VerifySemantics( - workspace, editScript, activeStatements, additionalOldSources, diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index f9d5a57d86a00..651c0eed64209 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -6,10 +6,7 @@ using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue; -using Microsoft.CodeAnalysis.Editor.UnitTests; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.Composition; using Roslyn.Test.Utilities; using Xunit; @@ -17,10 +14,6 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { public class StatementEditingTests : EditingTestBase { - private static readonly IExportProviderFactory s_exportProviderFactoryWithTestActiveStatementSpanTracker = - ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic - .WithPart(typeof(TestActiveStatementSpanTracker))); - #region Strings [Fact] @@ -8382,12 +8375,9 @@ static IEnumerable F() } } "; - - using var workspace = TestWorkspace.CreateCSharp("", exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); var edits = GetTopEdits(src1, src2); - CSharpEditAndContinueTestHelpers.Instance40.VerifySemantics( - workspace, + CSharpEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( edits, ActiveStatementsDescription.Empty, null, @@ -8425,12 +8415,9 @@ static IEnumerable F() } } "; - - using var workspace = TestWorkspace.CreateCSharp("", exportProvider: s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()); var edits = GetTopEdits(src1, src2); - CSharpEditAndContinueTestHelpers.Instance40.VerifySemantics( - workspace, + CSharpEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( edits, ActiveStatementsDescription.Empty, null, diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTracker.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTracker.cs deleted file mode 100644 index 74978a64839e4..0000000000000 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTracker.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable enable - -using System; -using System.Composition; -using Microsoft.CodeAnalysis.EditAndContinue; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue -{ - internal sealed class ActiveStatementSpanTracker : IActiveStatementSpanTracker - { - [ExportWorkspaceServiceFactory(typeof(IActiveStatementSpanTracker), ServiceLayer.Editor), Shared] - private sealed class Factory : IWorkspaceServiceFactory - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Factory() { } - - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - public IWorkspaceService CreateService(HostWorkspaceServices services) - => new ActiveStatementSpanTracker(services); - } - - private readonly IActiveStatementTrackingService _trackingService; - - [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - public ActiveStatementSpanTracker(HostWorkspaceServices services) - { - _trackingService = services.GetRequiredService(); - } - - public bool TryGetSpan(ActiveStatementId id, SourceText source, out TextSpan span) - => _trackingService.TryGetSpan(id, source, out span); - } -} diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs new file mode 100644 index 0000000000000..d5a9a4668386a --- /dev/null +++ b/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.EditAndContinue; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue +{ + internal sealed class ActiveStatementSpanTrackerFactory : IActiveStatementSpanTrackerFactory + { + [ExportWorkspaceServiceFactory(typeof(IActiveStatementSpanTracker), ServiceLayer.Editor), Shared] + private sealed class Factory : IWorkspaceServiceFactory + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public Factory() { } + + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + public IWorkspaceService CreateService(HostWorkspaceServices services) + => new ActiveStatementSpanTrackerFactory(services); + } + + private readonly IActiveStatementSpanTracker _tracker; + + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + public ActiveStatementSpanTrackerFactory(HostWorkspaceServices services) + { + _tracker = new Tracker(services.GetRequiredService()); + } + + public IActiveStatementSpanTracker GetOrCreateActiveStatementSpanTracker() + { + return _tracker; + } + + private sealed class Tracker : IActiveStatementSpanTracker + { + private readonly IActiveStatementTrackingService _trackingService; + + [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] + public Tracker(IActiveStatementTrackingService activeStatementTrackingService) + { + _trackingService = activeStatementTrackingService; + } + + public bool TryGetSpan(ActiveStatementId id, SourceText source, out TextSpan span) + => _trackingService.TryGetSpan(id, source, out span); + } + } +} diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 14932d741213e..0c9a00eaab628 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -36,7 +36,7 @@ public sealed class EditAndContinueWorkspaceServiceTests : TestBase { private static readonly IExportProviderFactory s_exportProviderFactoryWithTestActiveStatementSpanTracker = ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic - .WithPart(typeof(TestActiveStatementSpanTracker))); + .WithPart(typeof(TestActiveStatementSpanTrackerFactory))); private static readonly ActiveStatementProvider s_noActiveStatements = cancellationToken => Task.FromResult(ImmutableArray.Empty); @@ -72,7 +72,7 @@ public EditAndContinueWorkspaceServiceTests() private EditAndContinueWorkspaceService CreateEditAndContinueService(Workspace workspace) { - Assert.IsType(workspace.Services.GetRequiredService()); + Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); return new EditAndContinueWorkspaceService( workspace, @@ -199,7 +199,7 @@ private static TextSpan GetSpan(string str, string substr) public async Task RunMode_ProjectThatDoesNotSupportEnC() { var exportProviderFactory = ExportProviderCache.GetOrCreateExportProviderFactory( - TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTracker))); + TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTrackerFactory))); using var workspace = new TestWorkspace(exportProvider: exportProviderFactory.CreateExportProvider()); var solution = workspace.CurrentSolution; @@ -508,7 +508,7 @@ public async Task RunMode_DifferentDocumentWithSameContent() public async Task BreakMode_ProjectThatDoesNotSupportEnC() { var exportProviderFactory = ExportProviderCache.GetOrCreateExportProviderFactory( - TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTracker))); + TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTrackerFactory))); using (var workspace = new TestWorkspace(exportProvider: exportProviderFactory.CreateExportProvider())) { @@ -2325,7 +2325,7 @@ public async Task ActiveStatements() var activeLineSpan21 = sourceTextV2.Lines.GetLinePositionSpan(activeSpan21); var activeLineSpan22 = sourceTextV2.Lines.GetLinePositionSpan(activeSpan22); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); spanTracker.Spans = new Dictionary { { documentId, new TextSpan?[] { activeSpan11, activeSpan12 } } @@ -2442,7 +2442,7 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy var activeLineSpan21 = sourceTextV2.Lines.GetLinePositionSpan(activeSpan21); var activeLineSpan22 = sourceTextV2.Lines.GetLinePositionSpan(activeSpan22); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + var spanTracker = Assert.IsType(workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); spanTracker.Spans = new Dictionary { { documentId, new TextSpan?[] { activeSpan11, activeSpan12 } } @@ -2510,7 +2510,7 @@ public async Task ActiveStatements_SyntaxErrorOrOutOfSyncDocument(bool isOutOfSy public async Task ActiveStatements_ForeignDocument() { var exportProviderFactory = ExportProviderCache.GetOrCreateExportProviderFactory( - TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTracker))); + TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTrackerFactory))); using var workspace = new TestWorkspace(exportProvider: exportProviderFactory.CreateExportProvider()); var solution = workspace.CurrentSolution; diff --git a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs index b9651b1fedba8..c44df407f36fd 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditSessionActiveStatementsTests.cs @@ -91,7 +91,7 @@ public Validator( CommittedSolution.DocumentState initialState = CommittedSolution.DocumentState.MatchesBuildOutput) { var exportProviderFactory = ExportProviderCache.GetOrCreateExportProviderFactory( - TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(CSharpEditAndContinueAnalyzer)).WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTracker))); + TestExportProvider.MinimumCatalogWithCSharpAndVisualBasic.WithPart(typeof(CSharpEditAndContinueAnalyzer.Factory)).WithPart(typeof(DummyLanguageService)).WithPart(typeof(TestActiveStatementSpanTrackerFactory))); var exportProvider = exportProviderFactory.CreateExportProvider(); @@ -116,7 +116,7 @@ public Validator( debuggingSession.Test_SetNonRemappableRegions(nonRemappableRegions ?? ImmutableDictionary>.Empty); - Assert.IsType(Workspace.Services.GetRequiredService()); + Assert.IsType(Workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); var telemetry = new EditSessionTelemetry(); EditSession = new EditSession(debuggingSession, telemetry, cancellationToken => Task.FromResult(activeStatements), mockDebuggeModuleProvider.Object); diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index 2355fb3414eea..aacf2d293ebda 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -44,8 +44,8 @@ internal void VerifyUnchangedDocument( var documentId = DocumentId.CreateNewId(ProjectId.CreateNewId("TestEnCProject"), "TestEnCDocument"); - var spanTracker = new TestActiveStatementSpanTracker( - (trackingSpans != null) ? new Dictionary { { documentId, trackingSpans } } : null); + var spanTracker = Assert.IsType(Analyzer.GetTestAccessor().ActiveStatementSpanTracker); + spanTracker.Spans = (trackingSpans != null) ? new Dictionary { { documentId, trackingSpans } } : null; var actualNewActiveStatements = new ActiveStatement[oldActiveStatements.Length]; var actualNewExceptionRegions = new ImmutableArray[oldActiveStatements.Length]; @@ -94,8 +94,8 @@ internal void VerifyRudeDiagnostics( var documentId = DocumentId.CreateNewId(ProjectId.CreateNewId("TestEnCProject"), "TestEnCDocument"); - var spanTracker = new TestActiveStatementSpanTracker( - (description.OldTrackingSpans != null) ? new Dictionary { { documentId, description.OldTrackingSpans } } : null); + var spanTracker = Assert.IsType(Analyzer.GetTestAccessor().ActiveStatementSpanTracker); + spanTracker.Spans = (description.OldTrackingSpans != null) ? new Dictionary { { documentId, description.OldTrackingSpans } } : null; Analyzer.GetTestAccessor().AnalyzeSyntax( editScript, @@ -103,7 +103,6 @@ internal void VerifyRudeDiagnostics( oldText, newText, documentId, - spanTracker, oldActiveStatements.AsImmutable(), actualNewActiveStatements, actualNewExceptionRegions, @@ -183,7 +182,6 @@ internal void VerifyLineEdits( } internal void VerifySemantics( - Workspace workspace, EditScript editScript, ActiveStatementsDescription? activeStatements = null, IEnumerable? additionalOldSources = null, @@ -230,7 +228,7 @@ internal void VerifySemantics( var actualLineEdits = new List(); var actualSemanticEdits = new List(); var diagnostics = new List(); - var spanTracker = Assert.IsType(workspace.Services.GetRequiredService()); + Assert.IsType(Analyzer.GetTestAccessor().ActiveStatementSpanTracker); var documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()); var actualNewActiveStatements = new ActiveStatement[activeStatements.OldStatements.Length]; @@ -242,7 +240,6 @@ internal void VerifySemantics( oldText, newText, documentId, - spanTracker, oldActiveStatements, actualNewActiveStatements, actualNewExceptionRegions, diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTracker.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTracker.cs index bec6239dead80..410890b7f6a63 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTracker.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTracker.cs @@ -4,29 +4,15 @@ #nullable enable -using System; using System.Collections.Generic; -using System.Composition; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests { - [ExportWorkspaceService(typeof(IActiveStatementSpanTracker), ServiceLayer.Host)] - [Shared] - [PartNotDiscoverable] internal class TestActiveStatementSpanTracker : IActiveStatementSpanTracker { public Dictionary? Spans; - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public TestActiveStatementSpanTracker() - { - } - - [SuppressMessage("RoslynDiagnosticsReliability", "RS0034:Exported parts should be marked with 'ImportingConstructorAttribute'", Justification = "Used incorrectly by test code")] public TestActiveStatementSpanTracker(Dictionary? spans = null) { Spans = spans; diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTrackerFactory.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTrackerFactory.cs new file mode 100644 index 0000000000000..6d85b5e0e6f77 --- /dev/null +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/TestActiveStatementSpanTrackerFactory.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests +{ + [ExportWorkspaceService(typeof(IActiveStatementSpanTrackerFactory), ServiceLayer.Host)] + [Shared] + [PartNotDiscoverable] + internal class TestActiveStatementSpanTrackerFactory : IActiveStatementSpanTrackerFactory + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public TestActiveStatementSpanTrackerFactory() + { + ActiveStatementSpanTracker = new TestActiveStatementSpanTracker(); + } + + public TestActiveStatementSpanTracker ActiveStatementSpanTracker { get; } + + IActiveStatementSpanTracker IActiveStatementSpanTrackerFactory.GetOrCreateActiveStatementSpanTracker() + => ActiveStatementSpanTracker; + } +} diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb index 930aa6bd30618..ab09731d9b290 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb @@ -12,11 +12,12 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests - Public MustInherit Class EditingTestBase Inherits BasicTestBase - Friend Shared ReadOnly Analyzer As VisualBasicEditAndContinueAnalyzer = New VisualBasicEditAndContinueAnalyzer() + Friend Shared Function CreateAnalyzer() As VisualBasicEditAndContinueAnalyzer + Return New VisualBasicEditAndContinueAnalyzer(New TestActiveStatementSpanTracker()) + End Function Public Enum StateMachineKind None @@ -38,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests End Function Private Shared Function ParseSource(source As String, Optional options As ParseOptions = Nothing) As SyntaxTree - Return VisualBasicEditAndContinueTestHelpers.Instance.ParseText(ActiveStatementsDescription.ClearTags(source)) + Return VisualBasicEditAndContinueTestHelpers.CreateInstance().ParseText(ActiveStatementsDescription.ClearTags(source)) End Function Friend Shared Function GetTopEdits(src1 As String, src2 As String) As EditScript(Of SyntaxNode) @@ -64,7 +65,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Dim diagnostics = New List(Of RudeEditDiagnostic)() Dim oldHasStateMachineSuspensionPoint = False, newHasStateMachineSuspensionPoint = False - Dim match = Analyzer.GetTestAccessor().ComputeBodyMatch(m1, m2, Array.Empty(Of AbstractEditAndContinueAnalyzer.ActiveNode)(), diagnostics, oldHasStateMachineSuspensionPoint, newHasStateMachineSuspensionPoint) + Dim match = CreateAnalyzer().GetTestAccessor().ComputeBodyMatch(m1, m2, Array.Empty(Of AbstractEditAndContinueAnalyzer.ActiveNode)(), diagnostics, oldHasStateMachineSuspensionPoint, newHasStateMachineSuspensionPoint) Dim needsSyntaxMap = oldHasStateMachineSuspensionPoint AndAlso newHasStateMachineSuspensionPoint Assert.Equal(stateMachine <> StateMachineKind.None, needsSyntaxMap) @@ -80,7 +81,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests src2 As String, Optional stateMachine As StateMachineKind = StateMachineKind.None) As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode)) Dim methodMatch = GetMethodMatch(src1, src2, stateMachine) - Return EditAndContinueTestHelpers.GetMethodMatches(Analyzer, methodMatch) + Return EditAndContinueTestHelpers.GetMethodMatches(CreateAnalyzer(), methodMatch) End Function Public Shared Function ToMatchingPairs(match As Match(Of SyntaxNode)) As MatchingPairs diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb index d4ec3cd607306..87200932d774e 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb @@ -6,24 +6,18 @@ Imports System.Runtime.CompilerServices Imports Microsoft.CodeAnalysis.Differencing Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests -Imports Microsoft.CodeAnalysis.Editor.UnitTests -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue -Imports Microsoft.VisualStudio.Composition +Imports Microsoft.CodeAnalysis.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Friend Module Extensions - Private ReadOnly s_exportProviderFactoryWithTestActiveStatementSpanTracker As IExportProviderFactory = - ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic _ - .WithPart(GetType(TestActiveStatementSpanTracker))) - Friend Sub VerifyUnchangedDocument( source As String, description As ActiveStatementsDescription) - VisualBasicEditAndContinueTestHelpers.Instance.VerifyUnchangedDocument( + VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifyUnchangedDocument( ActiveStatementsDescription.ClearTags(source), description.OldStatements, description.OldTrackingSpans, @@ -42,7 +36,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Friend Sub VerifyRudeDiagnostics(editScript As EditScript(Of SyntaxNode), description As ActiveStatementsDescription, ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VisualBasicEditAndContinueTestHelpers.Instance.VerifyRudeDiagnostics(editScript, description, expectedDiagnostics) + VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifyRudeDiagnostics(editScript, description, expectedDiagnostics) End Sub @@ -50,7 +44,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests expectedLineEdits As IEnumerable(Of LineChange), expectedNodeUpdates As IEnumerable(Of String), ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VisualBasicEditAndContinueTestHelpers.Instance.VerifyLineEdits(editScript, expectedLineEdits, expectedNodeUpdates, expectedDiagnostics) + VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifyLineEdits(editScript, expectedLineEdits, expectedNodeUpdates, expectedDiagnostics) End Sub @@ -101,17 +95,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests expectedSemanticEdits As SemanticEditDescription(), expectedDeclarationError As DiagnosticDescription, ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - Using workspace = TestWorkspace.CreateVisualBasic("", exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) - VisualBasicEditAndContinueTestHelpers.Instance.VerifySemantics( - workspace, - editScript, - activeStatements, - additionalOldSources, - additionalNewSources, - expectedSemanticEdits, - expectedDeclarationError, - expectedDiagnostics) - End Using + VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifySemantics( + editScript, + activeStatements, + additionalOldSources, + additionalNewSources, + expectedSemanticEdits, + expectedDeclarationError, + expectedDiagnostics) End Sub End Module End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb index 9c5887d08b620..b865d20451b87 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb @@ -14,18 +14,24 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue Friend NotInheritable Class VisualBasicEditAndContinueTestHelpers Inherits EditAndContinueTestHelpers - Private ReadOnly _fxReferences As ImmutableArray(Of PortableExecutableReference) + Private ReadOnly _analyzer As VisualBasicEditAndContinueAnalyzer = New VisualBasicEditAndContinueAnalyzer(New TestActiveStatementSpanTracker()) - Friend Shared ReadOnly Instance As VisualBasicEditAndContinueTestHelpers = New VisualBasicEditAndContinueTestHelpers( - ImmutableArray.Create(TestReferences.NetFx.v4_0_30319_17626.mscorlib, TestReferences.NetFx.v4_0_30319.System, TestReferences.NetFx.v4_0_30319.System_Core)) + Private ReadOnly _fxReferences As ImmutableArray(Of PortableExecutableReference) - Friend Shared ReadOnly Instance40 As VisualBasicEditAndContinueTestHelpers = New VisualBasicEditAndContinueTestHelpers( - ImmutableArray.Create(TestReferences.NetFx.v4_0_30319.mscorlib, TestReferences.NetFx.v4_0_30319.System_Core)) + Friend Shared Function CreateInstance() As VisualBasicEditAndContinueTestHelpers + Return New VisualBasicEditAndContinueTestHelpers( + ImmutableArray.Create(TestReferences.NetFx.v4_0_30319_17626.mscorlib, TestReferences.NetFx.v4_0_30319.System, TestReferences.NetFx.v4_0_30319.System_Core)) + End Function - Friend Shared ReadOnly InstanceMinAsync As VisualBasicEditAndContinueTestHelpers = New VisualBasicEditAndContinueTestHelpers( - ImmutableArray.Create(TestReferences.NetFx.Minimal.mincorlib, TestReferences.NetFx.Minimal.minasync)) + Friend Shared Function CreateInstance40() As VisualBasicEditAndContinueTestHelpers + Return New VisualBasicEditAndContinueTestHelpers( + ImmutableArray.Create(TestReferences.NetFx.v4_0_30319.mscorlib, TestReferences.NetFx.v4_0_30319.System_Core)) + End Function - Private Shared ReadOnly s_analyzer As VisualBasicEditAndContinueAnalyzer = New VisualBasicEditAndContinueAnalyzer() + Friend Shared Function CreateInstanceMinAsync() As VisualBasicEditAndContinueTestHelpers + Return New VisualBasicEditAndContinueTestHelpers( + ImmutableArray.Create(TestReferences.NetFx.Minimal.mincorlib, TestReferences.NetFx.Minimal.minasync)) + End Function Sub New(fxReferences As ImmutableArray(Of PortableExecutableReference)) _fxReferences = fxReferences @@ -33,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue Public Overrides ReadOnly Property Analyzer As AbstractEditAndContinueAnalyzer Get - Return s_analyzer + Return _analyzer End Get End Property diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb index 0dd7704673ee1..e49b9db2ea9f1 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb @@ -4,21 +4,14 @@ Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests -Imports Microsoft.CodeAnalysis.Editor.UnitTests -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Imports Microsoft.VisualStudio.Composition Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Public Class RudeEditStatementTests Inherits EditingTestBase - Private Shared ReadOnly s_exportProviderFactoryWithTestActiveStatementSpanTracker As IExportProviderFactory = - ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic _ - .WithPart(GetType(TestActiveStatementSpanTracker))) - #Region "Matching" @@ -6017,20 +6010,15 @@ Class C End Function End Class " - - Using workspace = TestWorkspace.CreateVisualBasic("", exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) - Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.Instance40.VerifySemantics( - workspace, - editScript:=edits, - activeStatements:=ActiveStatementsDescription.Empty, - additionalOldSources:=Nothing, - additionalNewSources:=Nothing, - expectedSemanticEdits:=Nothing, - expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Iterator Function F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")}, - expectedDeclarationError:=Nothing) - End Using - + Dim edits = GetTopEdits(src1, src2) + VisualBasicEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( + editScript:=edits, + activeStatements:=ActiveStatementsDescription.Empty, + additionalOldSources:=Nothing, + additionalNewSources:=Nothing, + expectedSemanticEdits:=Nothing, + expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Iterator Function F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")}, + expectedDeclarationError:=Nothing) End Sub @@ -6053,18 +6041,15 @@ Class C End Function End Class " - Using workspace = TestWorkspace.CreateVisualBasic("", exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) - Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.Instance40.VerifySemantics( - workspace, - editScript:=edits, - activeStatements:=ActiveStatementsDescription.Empty, - additionalOldSources:=Nothing, - additionalNewSources:=Nothing, - expectedSemanticEdits:=Nothing, - expectedDiagnostics:=Nothing, - expectedDeclarationError:=Nothing) - End Using + Dim edits = GetTopEdits(src1, src2) + VisualBasicEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( + editScript:=edits, + activeStatements:=ActiveStatementsDescription.Empty, + additionalOldSources:=Nothing, + additionalNewSources:=Nothing, + expectedSemanticEdits:=Nothing, + expectedDiagnostics:=Nothing, + expectedDeclarationError:=Nothing) End Sub #End Region @@ -6143,19 +6128,15 @@ Class C End Function End Class " - - Using workspace = TestWorkspace.CreateVisualBasic("", exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) - Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.InstanceMinAsync.VerifySemantics( - workspace, - editScript:=edits, - activeStatements:=ActiveStatementsDescription.Empty, - additionalOldSources:=Nothing, - additionalNewSources:=Nothing, - expectedSemanticEdits:=Nothing, - expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Async Function F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")}, - expectedDeclarationError:=Nothing) - End Using + Dim edits = GetTopEdits(src1, src2) + VisualBasicEditAndContinueTestHelpers.CreateInstanceMinAsync().VerifySemantics( + editScript:=edits, + activeStatements:=ActiveStatementsDescription.Empty, + additionalOldSources:=Nothing, + additionalNewSources:=Nothing, + expectedSemanticEdits:=Nothing, + expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Async Function F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")}, + expectedDeclarationError:=Nothing) End Sub @@ -6179,17 +6160,14 @@ Class C End Function End Class " - Using workspace = TestWorkspace.CreateVisualBasic("", exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) - Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.InstanceMinAsync.VerifySemantics( - workspace, - edits, - ActiveStatementsDescription.Empty, - Nothing, - Nothing, - Nothing, - Nothing) - End Using + Dim edits = GetTopEdits(src1, src2) + VisualBasicEditAndContinueTestHelpers.CreateInstanceMinAsync().VerifySemantics( + edits, + ActiveStatementsDescription.Empty, + Nothing, + Nothing, + Nothing, + Nothing) End Sub #End Region End Class diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index ecc7002aa987e..9b20dee4a8dea 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Private Shared ReadOnly s_exportProviderFactoryWithTestActiveStatementSpanTracker As IExportProviderFactory = ExportProviderCache.GetOrCreateExportProviderFactory(TestExportProvider.EntireAssemblyCatalogWithCSharpAndVisualBasic _ - .WithPart(GetType(TestActiveStatementSpanTracker))) + .WithPart(GetType(TestActiveStatementSpanTrackerFactory))) #Region "Helpers" Private Shared Sub TestSpans(source As String, hasLabel As Func(Of SyntaxNode, Boolean)) @@ -446,7 +446,6 @@ Class C End Sub End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source1, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) @@ -468,7 +467,8 @@ End Class Dim oldStatementSpan = oldText.Lines.GetLinePositionSpan(oldStatementTextSpan) Dim oldStatementSyntax = oldSyntaxRoot.FindNode(oldStatementTextSpan) - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim baseActiveStatements = ImmutableArray.Create(ActiveStatementsDescription.CreateActiveStatement(ActiveStatementFlags.IsLeafFrame, oldStatementSpan, DocumentId.CreateNewId(ProjectId.CreateNewId()))) Dim result = Await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newDocument, CancellationToken.None) @@ -495,12 +495,12 @@ Class C End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) Dim oldProject = workspace.CurrentSolution.Projects.Single() Dim oldDocument = oldProject.Documents.Single() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim result = Await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, oldDocument, CancellationToken.None) Assert.False(result.HasChanges) @@ -526,17 +526,17 @@ Class C End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source1, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) Dim oldProject = workspace.CurrentSolution.Projects.Single() Dim oldDocument = oldProject.Documents.Single() Dim documentId = oldDocument.Id Dim oldSolution = workspace.CurrentSolution Dim newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)) - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None) + Dim result = Await Analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None) Assert.False(result.HasChanges) Assert.False(result.HasChangesAndErrors) @@ -555,12 +555,12 @@ Class C End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) Dim oldProject = workspace.CurrentSolution.Projects.Single() Dim oldDocument = oldProject.Documents.Single() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim result = Await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, oldDocument, CancellationToken.None) Assert.False(result.HasChanges) @@ -588,17 +588,17 @@ Class C End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source1, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) Dim oldProject = workspace.CurrentSolution.Projects.Single() Dim oldDocument = oldProject.Documents.Single() Dim documentId = oldDocument.Id Dim oldSolution = workspace.CurrentSolution Dim newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)) - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None) + Dim result = Await Analyzer.AnalyzeDocumentAsync(oldDocument, baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None) ' no declaration errors (error in method body is only reported when emitting) Assert.False(result.HasChangesAndErrors) @@ -623,16 +623,16 @@ Class C End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source1, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) Dim oldSolution = workspace.CurrentSolution Dim oldProject = oldSolution.Projects.Single() Dim documentId = oldProject.Documents.Single().Id Dim newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)) - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() - Dim result = Await analyzer.AnalyzeDocumentAsync(oldSolution.GetDocument(documentId), baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None) + Dim result = Await Analyzer.AnalyzeDocumentAsync(oldSolution.GetDocument(documentId), baseActiveStatements, newSolution.GetDocument(documentId), CancellationToken.None) Assert.True(result.HasChanges) Assert.True(result.HasChangesAndErrors) @@ -656,7 +656,7 @@ Class C End Class .Value - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(New TestActiveStatementSpanTracker()) Dim root = SyntaxFactory.ParseCompilationUnit(source) Assert.Null(analyzer.FindMemberDeclaration(root, Integer.MaxValue)) @@ -675,7 +675,6 @@ End Class Private Class D End Class " - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Using workspace = TestWorkspace.CreateVisualBasic(source1, exportProvider:=s_exportProviderFactoryWithTestActiveStatementSpanTracker.CreateExportProvider()) ' fork the solution to introduce a change @@ -695,7 +694,8 @@ End Class Dim changedDocuments = changes.GetChangedDocuments().Concat(changes.GetAddedDocuments()) - Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTracker)()) + Dim activeStatementSpanTracker = Assert.IsType(Of TestActiveStatementSpanTracker)(workspace.Services.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + Dim analyzer = New VisualBasicEditAndContinueAnalyzer(activeStatementSpanTracker) Dim result = New List(Of DocumentAnalysisResults)() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() For Each changedDocumentId In changedDocuments diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 8524bca54d1e5..ccc1eecf89b38 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.EditAndContinue; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -24,12 +25,26 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue { - [ExportLanguageService(typeof(IEditAndContinueAnalyzer), LanguageNames.CSharp), Shared] internal sealed class CSharpEditAndContinueAnalyzer : AbstractEditAndContinueAnalyzer { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CSharpEditAndContinueAnalyzer() + [ExportLanguageServiceFactory(typeof(IEditAndContinueAnalyzer), LanguageNames.CSharp), Shared] + internal sealed class Factory : ILanguageServiceFactory + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public Factory() + { + } + + public ILanguageService CreateLanguageService(HostLanguageServices languageServices) + { + return new CSharpEditAndContinueAnalyzer(languageServices.WorkspaceServices.GetRequiredService().GetOrCreateActiveStatementSpanTracker()); + } + } + + // Public for testing purposes + public CSharpEditAndContinueAnalyzer(IActiveStatementSpanTracker activeStatementSpanTracker) + : base(activeStatementSpanTracker) { } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index b5547faa51406..24d544e61ee00 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -28,6 +28,13 @@ internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyz { internal const int DefaultStatementPart = 0; + private readonly IActiveStatementSpanTracker _activeStatementSpanTracker; + + protected AbstractEditAndContinueAnalyzer(IActiveStatementSpanTracker activeStatementSpanTracker) + { + _activeStatementSpanTracker = activeStatementSpanTracker; + } + internal abstract bool ExperimentalFeaturesEnabled(SyntaxTree tree); /// @@ -375,6 +382,8 @@ public async Task AnalyzeDocumentAsync( Debug.Assert(document.SupportsSyntaxTree); Debug.Assert(document.SupportsSemanticModel); + Debug.Assert(document.Project.Solution.Workspace.Services.GetRequiredService().GetOrCreateActiveStatementSpanTracker() == _activeStatementSpanTracker); + try { cancellationToken.ThrowIfCancellationRequested(); @@ -479,15 +488,12 @@ public async Task AnalyzeDocumentAsync( var syntacticEdits = topMatch.GetTreeEdits(); var editMap = BuildEditMap(syntacticEdits); - var activeStatementSpanTracker = document.Project.Solution.Workspace.Services.GetRequiredService(); - AnalyzeSyntax( syntacticEdits, editMap, oldText, newText, document.Id, - activeStatementSpanTracker, baseActiveStatements, newActiveStatements, newExceptionRegions, @@ -640,7 +646,6 @@ private void AnalyzeSyntax( SourceText oldText, SourceText newText, DocumentId documentId, - IActiveStatementSpanTracker activeStatementSpanTracker, ImmutableArray oldActiveStatements, [Out] ActiveStatement[] newActiveStatements, [Out] ImmutableArray[] newExceptionRegions, @@ -655,11 +660,11 @@ private void AnalyzeSyntax( { var edit = script.Edits[i]; - AnalyzeUpdatedActiveMethodBodies(script, i, editMap, oldText, newText, documentId, activeStatementSpanTracker, oldActiveStatements, newActiveStatements, newExceptionRegions, updatedMethods, diagnostics); + AnalyzeUpdatedActiveMethodBodies(script, i, editMap, oldText, newText, documentId, oldActiveStatements, newActiveStatements, newExceptionRegions, updatedMethods, diagnostics); ReportSyntacticRudeEdits(diagnostics, script.Match, edit, editMap); } - UpdateUneditedSpans(diagnostics, script.Match, oldText, newText, documentId, activeStatementSpanTracker, oldActiveStatements, newActiveStatements, newExceptionRegions); + UpdateUneditedSpans(diagnostics, script.Match, oldText, newText, documentId, oldActiveStatements, newActiveStatements, newExceptionRegions); Debug.Assert(newActiveStatements.All(a => a != null)); } @@ -670,7 +675,6 @@ private void UpdateUneditedSpans( SourceText oldText, SourceText newText, DocumentId documentId, - IActiveStatementSpanTracker activeStatementSpanTracker, ImmutableArray oldActiveStatements, [In, Out] ActiveStatement[] newActiveStatements, [In, Out] ImmutableArray[] newExceptionRegions) @@ -686,7 +690,7 @@ private void UpdateUneditedSpans( if (newActiveStatements[i] == null) { Contract.ThrowIfFalse(newExceptionRegions[i].IsDefault); - var isTracked = activeStatementSpanTracker.TryGetSpan(new ActiveStatementId(documentId, i), newText, out var trackedSpan); + var isTracked = _activeStatementSpanTracker.TryGetSpan(new ActiveStatementId(documentId, i), newText, out var trackedSpan); if (!TryGetTextSpan(oldText.Lines, oldActiveStatements[i].Span, out var oldStatementSpan)) { DocumentAnalysisResults.Log.Write("Invalid active statement span: [{0}..{1})", oldStatementSpan.Start, oldStatementSpan.End); @@ -916,7 +920,6 @@ private void AnalyzeUpdatedActiveMethodBodies( SourceText oldText, SourceText newText, DocumentId documentId, - IActiveStatementSpanTracker activeStatementSpanTracker, ImmutableArray oldActiveStatements, [Out] ActiveStatement[] newActiveStatements, [Out] ImmutableArray[] newExceptionRegions, @@ -1018,7 +1021,7 @@ private void AnalyzeUpdatedActiveMethodBodies( // Tracking spans corresponding to the active statements from the tracking service. // We seed the method body matching algorithm with tracking spans (unless they were deleted) // to get precise matching. - var isTracked = activeStatementSpanTracker.TryGetSpan(new ActiveStatementId(documentId, ordinal), newText, out var trackedSpan); + var isTracked = _activeStatementSpanTracker.TryGetSpan(new ActiveStatementId(documentId, ordinal), newText, out var trackedSpan); if (isTracked) { @@ -3854,20 +3857,22 @@ internal readonly struct TestAccessor public TestAccessor(AbstractEditAndContinueAnalyzer abstractEditAndContinueAnalyzer) => _abstractEditAndContinueAnalyzer = abstractEditAndContinueAnalyzer; + internal ref readonly IActiveStatementSpanTracker ActiveStatementSpanTracker + => ref _abstractEditAndContinueAnalyzer._activeStatementSpanTracker; + internal void AnalyzeSyntax( EditScript script, Dictionary editMap, SourceText oldText, SourceText newText, DocumentId documentId, - IActiveStatementSpanTracker activeStatementSpanTracker, ImmutableArray oldActiveStatements, [Out] ActiveStatement[] newActiveStatements, [Out] ImmutableArray[] newExceptionRegions, [Out] List updatedMethods, [Out] List diagnostics) { - _abstractEditAndContinueAnalyzer.AnalyzeSyntax(script, editMap, oldText, newText, documentId, activeStatementSpanTracker, oldActiveStatements, newActiveStatements, newExceptionRegions, updatedMethods, diagnostics); + _abstractEditAndContinueAnalyzer.AnalyzeSyntax(script, editMap, oldText, newText, documentId, oldActiveStatements, newActiveStatements, newExceptionRegions, updatedMethods, diagnostics); } internal void AnalyzeUnchangedDocument( diff --git a/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTracker.cs b/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTracker.cs index b88b4189d5018..7be687d1d16e6 100644 --- a/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTracker.cs +++ b/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTracker.cs @@ -4,12 +4,11 @@ #nullable enable -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EditAndContinue { - internal interface IActiveStatementSpanTracker : IWorkspaceService + internal interface IActiveStatementSpanTracker { bool TryGetSpan(ActiveStatementId id, SourceText source, out TextSpan span); } diff --git a/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTrackerFactory.cs b/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTrackerFactory.cs new file mode 100644 index 0000000000000..f17016c25bcee --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/IActiveStatementSpanTrackerFactory.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.EditAndContinue +{ + internal interface IActiveStatementSpanTrackerFactory : IWorkspaceService + { + IActiveStatementSpanTracker GetOrCreateActiveStatementSpanTracker(); + } +} diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 2639b67c4295c..8e89e5a9ee973 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -4,23 +4,36 @@ Imports System.Collections.Immutable Imports System.Composition -Imports System.Diagnostics.CodeAnalysis Imports System.Runtime.InteropServices Imports System.Threading Imports Microsoft.CodeAnalysis.Differencing Imports Microsoft.CodeAnalysis.EditAndContinue +Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue - Friend NotInheritable Class VisualBasicEditAndContinueAnalyzer Inherits AbstractEditAndContinueAnalyzer - - - Public Sub New() + + Private NotInheritable Class Factory + Implements ILanguageServiceFactory + + + + Public Sub New() + End Sub + + Public Function CreateLanguageService(languageServices As HostLanguageServices) As ILanguageService Implements ILanguageServiceFactory.CreateLanguageService + Return New VisualBasicEditAndContinueAnalyzer(languageServices.WorkspaceServices.GetRequiredService(Of IActiveStatementSpanTrackerFactory)().GetOrCreateActiveStatementSpanTracker()) + End Function + End Class + + ' Public for testing purposes + Public Sub New(activeStatementSpanTracker As IActiveStatementSpanTracker) + MyBase.New(activeStatementSpanTracker) End Sub #Region "Syntax Analysis" @@ -511,7 +524,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Dim rightDeclaration = DirectCast(GetSymbolSyntax(rightProperty, cancellationToken), PropertyStatementSyntax) rightInitializer = rightDeclaration.Initializer - ElseIf leftInitializer.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) + ElseIf leftInitializer.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then ' field initializer or AsNewClause Dim leftDeclarator = DirectCast(leftInitializer.Parent, VariableDeclaratorSyntax) From 00ebef4c2950bef40fbb2cd6eb35046adc1c673f Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 22 Jun 2020 17:50:21 -0700 Subject: [PATCH 17/22] Fix registration of IActiveStatementSpanTrackerFactory --- .../EditAndContinue/ActiveStatementSpanTrackerFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs index d5a9a4668386a..96f41581d66ed 100644 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs +++ b/src/EditorFeatures/Core/Implementation/EditAndContinue/ActiveStatementSpanTrackerFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue { internal sealed class ActiveStatementSpanTrackerFactory : IActiveStatementSpanTrackerFactory { - [ExportWorkspaceServiceFactory(typeof(IActiveStatementSpanTracker), ServiceLayer.Editor), Shared] + [ExportWorkspaceServiceFactory(typeof(IActiveStatementSpanTrackerFactory), ServiceLayer.Editor), Shared] private sealed class Factory : IWorkspaceServiceFactory { [ImportingConstructor] From 212fc8f225108b5f45b5c60c5a2037733427d1b9 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Mon, 22 Jun 2020 19:49:36 -0700 Subject: [PATCH 18/22] Give an example in the non-exhaustive diagnostic (#44702) * When reporting a switch is not exhaustive, give an example why. Fixes #43943 * Skip a flaky test Related to #45234 --- .../Portable/Binder/PatternExplainer.cs | 505 +++++++++++ .../Portable/Binder/SwitchExpressionBinder.cs | 8 +- .../CSharp/Portable/CSharpResources.resx | 4 +- .../FlowAnalysis/NullableWalker_Patterns.cs | 9 +- .../CSharp/Portable/Utilities/IValueSet.cs | 8 + .../Portable/Utilities/IValueSetFactory.cs | 10 + .../Utilities/ValueSetFactory.BoolValueSet.cs | 3 + .../ValueSetFactory.BoolValueSetFactory.cs | 4 + .../Utilities/ValueSetFactory.ByteTC.cs | 2 + .../Utilities/ValueSetFactory.CharTC.cs | 8 +- .../Utilities/ValueSetFactory.DecimalTC.cs | 2 + .../ValueSetFactory.DecimalValueSetFactory.cs | 4 + .../Utilities/ValueSetFactory.DoubleTC.cs | 2 + .../ValueSetFactory.EnumeratedValueSet.cs | 41 +- ...lueSetFactory.EnumeratedValueSetFactory.cs | 10 +- .../ValueSetFactory.FloatingValueSet.cs | 23 +- ...ValueSetFactory.FloatingValueSetFactory.cs | 4 + .../Utilities/ValueSetFactory.INumericTC.cs | 5 + .../Utilities/ValueSetFactory.IntTC.cs | 2 + .../Utilities/ValueSetFactory.LongTC.cs | 2 + .../Utilities/ValueSetFactory.NintValueSet.cs | 22 +- .../ValueSetFactory.NintValueSetFactory.cs | 4 + .../ValueSetFactory.NuintValueSet.cs | 22 +- .../ValueSetFactory.NuintValueSetFactory.cs | 4 + .../ValueSetFactory.NumericValueSet.cs | 21 +- .../ValueSetFactory.NumericValueSetFactory.cs | 4 + .../Utilities/ValueSetFactory.SByteTC.cs | 2 + .../Utilities/ValueSetFactory.ShortTC.cs | 2 + .../Utilities/ValueSetFactory.SingleTC.cs | 2 + .../Utilities/ValueSetFactory.StringTC.cs | 2 +- .../Utilities/ValueSetFactory.UIntTC.cs | 2 + .../Utilities/ValueSetFactory.ULongTC.cs | 2 + .../Utilities/ValueSetFactory.UShortTC.cs | 2 + .../Portable/xlf/CSharpResources.cs.xlf | 8 +- .../Portable/xlf/CSharpResources.de.xlf | 8 +- .../Portable/xlf/CSharpResources.es.xlf | 8 +- .../Portable/xlf/CSharpResources.fr.xlf | 8 +- .../Portable/xlf/CSharpResources.it.xlf | 8 +- .../Portable/xlf/CSharpResources.ja.xlf | 8 +- .../Portable/xlf/CSharpResources.ko.xlf | 8 +- .../Portable/xlf/CSharpResources.pl.xlf | 8 +- .../Portable/xlf/CSharpResources.pt-BR.xlf | 8 +- .../Portable/xlf/CSharpResources.ru.xlf | 8 +- .../Portable/xlf/CSharpResources.tr.xlf | 8 +- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 8 +- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 8 +- .../CSharp/Test/Emit/CodeGen/PatternTests.cs | 8 +- .../IOperationTests_ISwitchExpression.cs | 8 +- .../IOperationTests_ISwitchOperation.cs | 4 +- .../Semantics/NullableReferenceTypesTests.cs | 8 +- .../NullableReferenceTypesVsPatterns.cs | 44 +- .../Semantics/PatternMatchingTests2.cs | 12 +- .../Semantics/PatternMatchingTests3.cs | 833 +++++++++++++++++- .../Semantics/PatternMatchingTests4.cs | 44 +- .../Parsing/StatementAttributeParsingTests.cs | 4 +- .../VisualBasic/BasicIntelliSense.cs | 2 +- 56 files changed, 1652 insertions(+), 166 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs diff --git a/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs b/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs new file mode 100644 index 0000000000000..76fc06cf53568 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/PatternExplainer.cs @@ -0,0 +1,505 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal static class PatternExplainer + { + /// + /// Find the shortest path from the root node to the node of interest. + /// + /// The set of nodes in topological order. + /// The node of interest. + /// Whether to permit following paths that test for null. + /// The shortest path, excluding the node of interest. + private static ImmutableArray ShortestPathToNode( + ImmutableArray nodes, + BoundDecisionDagNode node, + bool nullPaths) + { + // compute the distance from each node to the endpoint. + var dist = PooledDictionary.GetInstance(); + int nodeCount = nodes.Length; + int distance(BoundDecisionDagNode x) + { + if (x == null) + return nodeCount + 2; + if (dist.TryGetValue(x, out var v)) + return v.distance; + Debug.Assert(!nodes.Contains(x)); + return nodeCount + 2; + } + + for (int i = nodeCount - 1; i >= 0; i--) + { + var n = nodes[i]; + dist.Add(n, n switch + { + BoundEvaluationDecisionDagNode e => (distance(e.Next), e.Next), + BoundTestDecisionDagNode { Test: BoundDagNonNullTest _ } t when !nullPaths => (1 + distance(t.WhenTrue), t.WhenTrue), + BoundTestDecisionDagNode { Test: BoundDagExplicitNullTest _ } t when !nullPaths => (1 + distance(t.WhenFalse), t.WhenFalse), + BoundTestDecisionDagNode t when distance(t.WhenTrue) is var trueDist && distance(t.WhenFalse) is var falseDist => + (trueDist <= falseDist) ? (1 + trueDist, t.WhenTrue) : (1 + falseDist, t.WhenFalse), + BoundWhenDecisionDagNode w when distance(w.WhenTrue) is var trueDist && distance(w.WhenFalse) is var falseDist => + (trueDist <= falseDist) ? (1 + trueDist, w.WhenTrue) : (1 + falseDist, w.WhenFalse), + // treat the endpoint as distance 1. + // treat other nodes as not on the path to the endpoint + _ => ((n == node) ? 1 : nodeCount + 2, null), + }); + } + + // trace a path from the root node to the node of interest + var result = ArrayBuilder.GetInstance(capacity: dist[nodes[0]].distance); + for (BoundDecisionDagNode n = nodes[0]; n != node;) + { + result.Add(n); + switch (n) + { + case BoundEvaluationDecisionDagNode e: + n = e.Next; + break; + case BoundTestDecisionDagNode t: + (int d, BoundDecisionDagNode next) = dist[t]; + Debug.Assert(next != null); + Debug.Assert(distance(next) == (d - 1)); + n = next; + break; + case BoundWhenDecisionDagNode w: + result.RemoveLast(); + n = w.WhenFalse; + break; + default: + throw ExceptionUtilities.Unreachable; + } + } + + dist.Free(); + return result.ToImmutableAndFree(); + } + + /// + /// Return a sample pattern that would lead to the given decision dag node. + /// + /// A topologically sorted list of nodes in the decision dag. + /// A node of interest (typically, the default node for a non-exhaustive switch). + /// Permit the use of "null" paths on tests which check for null. + /// + internal static string SamplePatternForPathToDagNode( + BoundDagTemp rootIdentifier, + ImmutableArray nodes, + BoundDecisionDagNode targetNode, + bool nullPaths) + { + // Compute the path to the node, excluding the node itself. + var pathToNode = ShortestPathToNode(nodes, targetNode, nullPaths); + + var constraints = new Dictionary>(); + var evaluations = new Dictionary>(); + for (int i = 0, n = pathToNode.Length; i < n; i++) + { + BoundDecisionDagNode node = pathToNode[i]; + switch (node) + { + case BoundTestDecisionDagNode t: + { + BoundDecisionDagNode nextNode = (i < n - 1) ? pathToNode[i + 1] : targetNode; + bool sense = t.WhenTrue == nextNode || (t.WhenFalse != nextNode && t.WhenTrue is BoundWhenDecisionDagNode); + BoundDagTest test = t.Test; + BoundDagTemp temp = test.Input; + if (test is BoundDagTypeTest && sense == false) + { + // A failed type test is not very useful in constructing a counterexample, + // at least not without discriminated unions, so we just drop them. + } + else + { + if (!constraints.TryGetValue(temp, out var constraintBuilder)) + { + constraints.Add(temp, constraintBuilder = new ArrayBuilder<(BoundDagTest, bool)>()); + } + constraintBuilder.Add((test, sense)); + } + } + break; + case BoundEvaluationDecisionDagNode e: + { + BoundDagTemp temp = e.Evaluation.Input; + if (!evaluations.TryGetValue(temp, out var evaluationBuilder)) + { + evaluations.Add(temp, evaluationBuilder = new ArrayBuilder()); + } + evaluationBuilder.Add(e.Evaluation); + } + break; + } + } + + return SamplePatternForTemp(rootIdentifier, constraints, evaluations, requireExactType: false); + } + + private static string SamplePatternForTemp( + BoundDagTemp input, + Dictionary> constraintMap, + Dictionary> evaluationMap, + bool requireExactType) + { + var constraints = getArray(constraintMap, input); + var evaluations = getArray(evaluationMap, input); + + return + tryHandleSingleTest() ?? + tryHandleTypeTestAndTypeEvaluation() ?? + tryHandleUnboxNullableValueType() ?? + tryHandleTuplePattern() ?? + tryHandleNumericLimits() ?? + tryHandleRecursivePattern() ?? + produceFallbackPattern(); + + static ImmutableArray getArray(Dictionary> map, BoundDagTemp temp) + { + return map.TryGetValue(temp, out var builder) ? builder.ToImmutable() : ImmutableArray.Empty; + } + + // Handle the special case of a single test that is not handled. + string tryHandleSingleTest() + { + if (evaluations.IsEmpty && constraints.Length == 1) + { + switch (constraints[0]) + { + case (test: BoundDagNonNullTest _, sense: var sense): + return !sense ? "null" : requireExactType ? input.Type.ToDisplayString() : "not null"; + case (test: BoundDagExplicitNullTest _, sense: var sense): + return sense ? "null" : requireExactType ? input.Type.ToDisplayString() : "not null"; + case (test: BoundDagTypeTest { Type: var testedType }, sense: var sense): + Debug.Assert(sense); // we have dropped failing type tests + return testedType.ToDisplayString(); + } + } + + return null; + } + + // Handle the special case of a type test and a type evaluation. + string tryHandleTypeTestAndTypeEvaluation() + { + if (evaluations.Length == 1 && constraints.Length == 1 && + constraints[0] is (BoundDagTypeTest { Type: var constraintType }, true) && + evaluations[0] is BoundDagTypeEvaluation { Type: var evaluationType } te && + constraintType.Equals(evaluationType, TypeCompareKind.AllIgnoreOptions)) + { + var typedTemp = new BoundDagTemp(te.Syntax, te.Type, te); + return SamplePatternForTemp(typedTemp, constraintMap, evaluationMap, requireExactType: true); + } + + return null; + } + + // Handle the special case of a null test and a type evaluation to unbox a nullable value type + string tryHandleUnboxNullableValueType() + { + if (evaluations.Length == 1 && constraints.Length == 1 && + constraints[0] is (BoundDagNonNullTest _, true) && + evaluations[0] is BoundDagTypeEvaluation { Type: var evaluationType } te && + input.Type.IsNullableType() && input.Type.GetNullableUnderlyingType().Equals(evaluationType, TypeCompareKind.AllIgnoreOptions)) + { + var typedTemp = new BoundDagTemp(te.Syntax, te.Type, te); + var result = SamplePatternForTemp(typedTemp, constraintMap, evaluationMap, requireExactType: false); + // We need a null check. If not included in the result, add it. + return (result == "_") ? "not null" : result; + } + + return null; + } + + // Handle the special case of a tuple pattern + string tryHandleTuplePattern() + { + if (input.Type.IsTupleType && + constraints.IsEmpty && + evaluations.All(e => e is BoundDagFieldEvaluation { Field: var field } && field.IsTupleElement())) + { + var elements = input.Type.TupleElements; + int cardinality = elements.Length; + var subpatterns = new ArrayBuilder(cardinality); + subpatterns.AddMany("_", cardinality); + foreach (BoundDagFieldEvaluation e in evaluations) + { + var elementTemp = new BoundDagTemp(e.Syntax, e.Field.Type, e); + var index = e.Field.TupleElementIndex; + if (index < 0 || index >= cardinality) + return null; + var oldPattern = subpatterns[index]; + var newPattern = SamplePatternForTemp(elementTemp, constraintMap, evaluationMap, requireExactType: false); + subpatterns[index] = makeConjunct(oldPattern, newPattern); + } + + return "(" + string.Join(", ", subpatterns) + ")" + (subpatterns.Count == 1 ? " { }" : null); + } + + return null; + + static string makeConjunct(string oldPattern, string newPattern) => (oldPattern, newPattern) switch + { + ("_", var x) => x, + (var x, "_") => x, + (var x, var y) => x + " and " + y + }; + } + + // Handle the special case of numeric limits + string tryHandleNumericLimits() + { + if (evaluations.IsEmpty && + constraints.All(t => t switch + { + (BoundDagValueTest _, _) => true, + (BoundDagRelationalTest _, _) => true, + (BoundDagExplicitNullTest _, false) => true, + (BoundDagNonNullTest _, true) => true, + _ => false + }) && + ValueSetFactory.ForType(input.Type) is { } fac) + { + // All we have are numeric constraints. Process them to compute a value not covered. + var remainingValues = fac.AllValues; + foreach (var constraint in constraints) + { + var (test, sense) = constraint; + switch (test) + { + case BoundDagValueTest v: + addRelation(BinaryOperatorKind.Equal, v.Value); + break; + case BoundDagRelationalTest r: + addRelation(r.Relation, r.Value); + break; + } + void addRelation(BinaryOperatorKind relation, ConstantValue value) + { + var filtered = fac.Related(relation, value); + if (!sense) + filtered = filtered.Complement(); + remainingValues = remainingValues.Intersect(filtered); + } + } + + if (remainingValues.Complement().IsEmpty) + return "_"; + + return SampleValueString(remainingValues, input.Type, requireExactType: requireExactType); + } + + return null; + } + + // Handle the special case of a recursive pattern + string tryHandleRecursivePattern() + { + if (constraints.IsEmpty && evaluations.IsEmpty) + return null; + + if (!constraints.All(c => c switch + { + // not-null tests are implicitly incorporated into a recursive pattern + (test: BoundDagNonNullTest _, sense: true) => true, + (test: BoundDagExplicitNullTest _, sense: false) => true, + _ => false, + })) + { + return null; + } + + string deconstruction = null; + var properties = new Dictionary(); + bool needsPropertyString = false; + + foreach (var eval in evaluations) + { + switch (eval) + { + case BoundDagDeconstructEvaluation e: + var method = e.DeconstructMethod; + int extensionExtra = method.RequiresInstanceReceiver ? 0 : 1; + int count = method.Parameters.Length - extensionExtra; + var subpatternBuilder = new StringBuilder("("); + for (int j = 0; j < count; j++) + { + var elementTemp = new BoundDagTemp(e.Syntax, method.Parameters[j + extensionExtra].Type, e, j); + var newPattern = SamplePatternForTemp(elementTemp, constraintMap, evaluationMap, requireExactType: false); + if (j != 0) + subpatternBuilder.Append(", "); + subpatternBuilder.Append(newPattern); + } + subpatternBuilder.Append(")"); + var result = subpatternBuilder.ToString(); + if (deconstruction != null && needsPropertyString) + { + deconstruction = deconstruction + " { }"; + needsPropertyString = properties.Count != 0; + } + + deconstruction = (deconstruction is null) ? result : deconstruction + " and " + result; + needsPropertyString |= count == 1; + break; + case BoundDagFieldEvaluation e: + { + var subInput = new BoundDagTemp(e.Syntax, e.Field.Type, e); + var subPattern = SamplePatternForTemp(subInput, constraintMap, evaluationMap, false); + properties.Add(e.Field, subPattern); + } + break; + case BoundDagPropertyEvaluation e: + { + var subInput = new BoundDagTemp(e.Syntax, e.Property.Type, e); + var subPattern = SamplePatternForTemp(subInput, constraintMap, evaluationMap, false); + properties.Add(e.Property, subPattern); + } + break; + default: + return null; + } + } + + string typeName = requireExactType ? input.Type.ToDisplayString() : null; + needsPropertyString |= deconstruction == null && typeName == null || properties.Count != 0; + var propertyString = needsPropertyString ? (deconstruction != null ? " {" : "{") + string.Join(", ", properties.Select(kvp => $" {kvp.Key.Name}: {kvp.Value}")) + " }" : null; + Debug.Assert(typeName != null || deconstruction != null || propertyString != null); + return typeName + deconstruction + propertyString; + } + + // Produce a fallback pattern when we were not able to produce a more specific pattern. + string produceFallbackPattern() + { + return requireExactType ? input.Type.ToDisplayString() : "_"; + } + } + + private static string SampleValueString(IValueSet remainingValues, TypeSymbol type, bool requireExactType) + { + // We would not have been asked to produce an example of a missing pattern if no values are missing + Debug.Assert(!remainingValues.IsEmpty); + + // If the input is an enumeration type, see if any declared enumeration constant values are in the set. + // If so, that is what to report. + if (type is NamedTypeSymbol { TypeKind: TypeKind.Enum } e) + { + foreach (var declaredMember in e.GetMembers()) + { + if (declaredMember is FieldSymbol { IsConst: true, IsStatic: true, DeclaredAccessibility: Accessibility.Public } field && + field.GetConstantValue(ConstantFieldsInProgress.Empty, false) is ConstantValue constantValue && + remainingValues.Any(BinaryOperatorKind.Equal, constantValue)) + { + return field.ToDisplayString(); + } + } + } + + var sample = remainingValues.Sample; + if (sample != null) + return ValueString(sample, type, requireExactType); + + // IValueSet.Sample cannot produce a sample of type `nint` or `nuint` outside the range + // of values of `int` and `uint`. So if we get here we need to produce a pattern indicating + // such an out-of-range value. + + var underlyingType = type.EnumUnderlyingTypeOrSelf(); + Debug.Assert(underlyingType.IsNativeIntegerType); + if (underlyingType.SpecialType == SpecialType.System_IntPtr) + { + if (remainingValues.Any(BinaryOperatorKind.GreaterThan, ConstantValue.Create(int.MaxValue))) + return $"> ({type.ToDisplayString()})int.MaxValue"; + + if (remainingValues.Any(BinaryOperatorKind.LessThan, ConstantValue.Create(int.MinValue))) + return $"< ({type.ToDisplayString()})int.MinValue"; + } + else if (underlyingType.SpecialType == SpecialType.System_UIntPtr) + { + if (remainingValues.Any(BinaryOperatorKind.GreaterThan, ConstantValue.Create(uint.MaxValue))) + return $"> ({type.ToDisplayString()})uint.MaxValue"; + } + + throw ExceptionUtilities.Unreachable; + } + + private static string ValueString(ConstantValue value, TypeSymbol type, bool requireExactType) + { + bool requiresCast = (type.IsEnumType() || requireExactType || type.IsNativeIntegerType) && + !(typeHasExactTypeLiteral(type) && !value.IsNull); + string valueString = PrimitiveValueString(value, type.EnumUnderlyingTypeOrSelf()); + return requiresCast ? $"({type.ToDisplayString()}){valueString}" : valueString; + + static bool typeHasExactTypeLiteral(TypeSymbol type) => type.SpecialType switch + { + SpecialType.System_Int32 => true, + SpecialType.System_Int64 => true, + SpecialType.System_UInt32 => true, + SpecialType.System_UInt64 => true, + SpecialType.System_String => true, + SpecialType.System_Decimal => true, + SpecialType.System_Single => true, + SpecialType.System_Double => true, + SpecialType.System_Boolean => true, + SpecialType.System_Char => true, + _ => false, + }; + } + + private static string PrimitiveValueString(ConstantValue value, TypeSymbol type) + { + if (value.IsNull) + return "null"; + + switch (type.SpecialType) + { + case SpecialType.System_Boolean: + case SpecialType.System_Byte: + case SpecialType.System_SByte: + case SpecialType.System_UInt16: + case SpecialType.System_Int16: + case SpecialType.System_Int32: + case SpecialType.System_UInt32: + case SpecialType.System_UInt64: + case SpecialType.System_Int64: + case SpecialType.System_IntPtr when type.IsNativeIntegerType: + case SpecialType.System_UIntPtr when type.IsNativeIntegerType: + case SpecialType.System_Decimal: + case SpecialType.System_Char: + case SpecialType.System_String: + return ObjectDisplay.FormatPrimitive(value.Value, ObjectDisplayOptions.EscapeNonPrintableCharacters | ObjectDisplayOptions.IncludeTypeSuffix | ObjectDisplayOptions.UseQuotes); + + case SpecialType.System_Single: + return value.SingleValue switch + { + float.NaN => "float.NaN", + float.NegativeInfinity => "float.NegativeInfinity", + float.PositiveInfinity => "float.PositiveInfinity", + var x => ObjectDisplay.FormatPrimitive(x, ObjectDisplayOptions.IncludeTypeSuffix) + }; + + case SpecialType.System_Double: + return value.DoubleValue switch + { + double.NaN => "double.NaN", + double.NegativeInfinity => "double.NegativeInfinity", + double.PositiveInfinity => "double.PositiveInfinity", + var x => ObjectDisplay.FormatPrimitive(x, ObjectDisplayOptions.IncludeTypeSuffix) + }; + + default: + return "_"; + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs index 56f3ee9dd7c96..82e5c228e853a 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -93,11 +93,15 @@ private bool CheckSwitchExpressionExhaustive( // nulls is the job of the nullable walker. if (!hasErrors) { - foreach (var n in TopologicalSort.IterativeSort(new[] { decisionDag.RootNode }, nonNullSuccessors)) + var nodes = TopologicalSort.IterativeSort(new[] { decisionDag.RootNode }, nonNullSuccessors); + foreach (var n in nodes) { if (n is BoundLeafDecisionDagNode leaf && leaf.Label == defaultLabel) { - diagnostics.Add(ErrorCode.WRN_SwitchExpressionNotExhaustive, node.SwitchKeyword.GetLocation()); + diagnostics.Add( + ErrorCode.WRN_SwitchExpressionNotExhaustive, + node.SwitchKeyword.GetLocation(), + PatternExplainer.SamplePatternForPathToDagNode(BoundDagTemp.ForOriginalInput(boundInputExpression), nodes, n, nullPaths: false)); return true; } } diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index bb0de1a1e7493..6bf06fd5cde2f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5750,7 +5750,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The syntax 'var' for a pattern is not permitted to refer to a type, but '{0}' is in scope here. - The switch expression does not handle all possible values of its input type (it is not exhaustive). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. The switch expression does not handle all possible values of its input type (it is not exhaustive). @@ -5981,7 +5981,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Type '{0}' cannot be embedded because it has a non-abstract member. Consider setting the 'Embed Interop Types' property to false. - The switch expression does not handle some null inputs (it is not exhaustive). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. The switch expression does not handle some null inputs. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index 875ec5ac04452..ac75cd825cde0 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -590,7 +591,13 @@ private void VisitSwitchExpressionCore(BoundSwitchExpression node, bool inferTyp labelStateMap.TryGetValue(node.DefaultLabel, out var defaultLabelState) && defaultLabelState.believedReachable) { SetState(defaultLabelState.state); - ReportDiagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, ((SwitchExpressionSyntax)node.Syntax).SwitchKeyword.GetLocation()); + var nodes = node.DecisionDag.TopologicallySortedNodes; + var leaf = nodes.Where(n => n is BoundLeafDecisionDagNode leaf && leaf.Label == node.DefaultLabel).First(); + ReportDiagnostic( + ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, + ((SwitchExpressionSyntax)node.Syntax).SwitchKeyword.GetLocation(), + PatternExplainer.SamplePatternForPathToDagNode(BoundDagTemp.ForOriginalInput(node.Expression), nodes, leaf, nullPaths: true) + ); } // collect expressions, conversions and result types diff --git a/src/Compilers/CSharp/Portable/Utilities/IValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/IValueSet.cs index 74c17c283450f..47efd70f8bc20 100644 --- a/src/Compilers/CSharp/Portable/Utilities/IValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/IValueSet.cs @@ -4,6 +4,8 @@ #nullable enable +using System; + namespace Microsoft.CodeAnalysis.CSharp { /// @@ -51,6 +53,12 @@ internal interface IValueSet /// Does this value set contain no values? /// bool IsEmpty { get; } + + /// + /// Produce a sample value contained in the set. Throws if the set is empty. If the set + /// contains values but we cannot produce a particular value (e.g. for the set `nint > int.MaxValue`), returns null. + /// + ConstantValue? Sample { get; } } /// diff --git a/src/Compilers/CSharp/Portable/Utilities/IValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/IValueSetFactory.cs index 42e4928a8a897..22883c0ed413e 100644 --- a/src/Compilers/CSharp/Portable/Utilities/IValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/IValueSetFactory.cs @@ -33,6 +33,16 @@ internal interface IValueSetFactory /// Produce a random value for testing. /// ConstantValue RandomValue(Random random); + + /// + /// The set containing all values of the type. + /// + IValueSet AllValues { get; } + + /// + /// The empty set of values. + /// + IValueSet NoValues { get; } } /// diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs index 170ec193d01a0..2811dd5157c06 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSet.cs @@ -40,6 +40,9 @@ public static BoolValueSet Create(bool hasFalse, bool hasTrue) bool IValueSet.IsEmpty => !_hasFalse && !_hasTrue; + ConstantValue IValueSet.Sample => ConstantValue.Create(_hasTrue ? true : _hasFalse ? false : throw new ArgumentException()); + + public bool Any(BinaryOperatorKind relation, bool value) { switch (relation, value) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs index 66b20d0a28268..225a56964f890 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.BoolValueSetFactory.cs @@ -23,6 +23,10 @@ private sealed class BoolValueSetFactory : IValueSetFactory private BoolValueSetFactory() { } + IValueSet IValueSetFactory.AllValues => BoolValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => BoolValueSet.None; + public IValueSet Related(BinaryOperatorKind relation, bool value) { switch (relation, value) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs index 355cf93d14761..7e11209dfb8e8 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ByteTC.cs @@ -19,6 +19,8 @@ private struct ByteTC : INumericTC byte INumericTC.MaxValue => byte.MaxValue; + byte INumericTC.Zero => 0; + bool INumericTC.Related(BinaryOperatorKind relation, byte left, byte right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs index 71e8833e3955f..f192c8be6bd00 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.CharTC.cs @@ -20,6 +20,8 @@ private struct CharTC : INumericTC char INumericTC.MaxValue => char.MaxValue; + char INumericTC.Zero => (char)0; + bool INumericTC.Related(BinaryOperatorKind relation, char left, char right) { switch (relation) @@ -49,11 +51,7 @@ char INumericTC.Next(char value) string INumericTC.ToString(char c) { - var isPrintable = char.IsWhiteSpace(c) || - // exclude the Unicode character categories containing non-rendering, - // unknown, or incomplete characters. - char.GetUnicodeCategory(c) switch { UnicodeCategory.Control => false, UnicodeCategory.OtherNotAssigned => false, UnicodeCategory.Surrogate => false, _ => true }; - return isPrintable ? $"'{c}'" : $"\\u{(int)c:X4}"; + return ObjectDisplay.FormatPrimitive(c, ObjectDisplayOptions.EscapeNonPrintableCharacters | ObjectDisplayOptions.UseQuotes); } char INumericTC.Prev(char value) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs index e755e3d5a2ecc..78e235f599f42 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs @@ -30,6 +30,8 @@ private struct DecimalTC : INumericTC decimal INumericTC.MaxValue => decimal.MaxValue; + decimal INumericTC.Zero => 0M; + public decimal FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0m : constantValue.DecimalValue; public ConstantValue ToConstantValue(decimal value) => ConstantValue.Create(value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalValueSetFactory.cs index 2e8abee831943..1853b554c5a96 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalValueSetFactory.cs @@ -16,6 +16,10 @@ private sealed class DecimalValueSetFactory : IValueSetFactory, IValueS private readonly IValueSetFactory _underlying = NumericValueSetFactory.Instance; + IValueSet IValueSetFactory.AllValues => NumericValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => NumericValueSet.NoValues; + public IValueSet Related(BinaryOperatorKind relation, decimal value) => _underlying.Related(relation, DecimalTC.Normalize(value)); IValueSet IValueSetFactory.Random(int expectedSize, Random random) => _underlying.Random(expectedSize, random); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs index 6ff9c00be961b..ab05ea50b5cf7 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DoubleTC.cs @@ -21,6 +21,8 @@ private struct DoubleTC : FloatingTC, INumericTC double FloatingTC.NaN => double.NaN; + double INumericTC.Zero => 0.0; + /// /// The implementation of Next depends critically on the internal representation of an IEEE floating-point /// number. Every bit sequence between the representation of 0 and MaxValue represents a distinct diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs index cf0d85a433498..1a6405efeb8ad 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSet.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using System.Collections.Immutable; using System.Linq; using Roslyn.Utilities; @@ -24,18 +25,50 @@ private sealed class EnumeratedValueSet : IValueSet where TTC : struc /// In , then members are listed by inclusion. Otherwise all members /// are assumed to be contained in the set unless excluded. /// - private bool _included; + private readonly bool _included; - private ImmutableHashSet _membersIncludedOrExcluded; + private readonly ImmutableHashSet _membersIncludedOrExcluded; private EnumeratedValueSet(bool included, ImmutableHashSet membersIncludedOrExcluded) => (this._included, this._membersIncludedOrExcluded) = (included, membersIncludedOrExcluded); - public static EnumeratedValueSet AllValues = new EnumeratedValueSet(included: false, ImmutableHashSet.Empty); + public static readonly EnumeratedValueSet AllValues = new EnumeratedValueSet(included: false, ImmutableHashSet.Empty); + + public static readonly EnumeratedValueSet NoValues = new EnumeratedValueSet(included: true, ImmutableHashSet.Empty); internal static EnumeratedValueSet Including(T value) => new EnumeratedValueSet(included: true, ImmutableHashSet.Empty.Add(value)); - bool IValueSet.IsEmpty => _included && _membersIncludedOrExcluded.IsEmpty; + public bool IsEmpty => _included && _membersIncludedOrExcluded.IsEmpty; + + ConstantValue IValueSet.Sample + { + get + { + if (IsEmpty) throw new ArgumentException(); + var tc = default(TTC); + if (_included) + return tc.ToConstantValue(_membersIncludedOrExcluded.OrderBy(k => k).First()); + if (typeof(T) == typeof(string)) + { + // try some simple strings. + if (this.Any(BinaryOperatorKind.Equal, (T)(object)"")) + return tc.ToConstantValue((T)(object)""); + for (char c = 'A'; c <= 'z'; c++) + if (this.Any(BinaryOperatorKind.Equal, (T)(object)c.ToString())) + return tc.ToConstantValue((T)(object)c.ToString()); + } + // If that doesn't work, choose from a sufficiently large random selection of values. + // Since this is an excluded set, they cannot all be excluded + var candidates = tc.RandomValues(_membersIncludedOrExcluded.Count + 1, new Random(0), _membersIncludedOrExcluded.Count + 1); + foreach (var value in candidates) + { + if (this.Any(BinaryOperatorKind.Equal, value)) + return tc.ToConstantValue(value); + } + + throw ExceptionUtilities.Unreachable; + } + } public bool Any(BinaryOperatorKind relation, T value) { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSetFactory.cs index 4d3ee6c5b0d91..931380d28bd6a 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.EnumeratedValueSetFactory.cs @@ -19,7 +19,11 @@ internal static partial class ValueSetFactory /// private sealed class EnumeratedValueSetFactory : IValueSetFactory where TTC : struct, IEquatableValueTC where T : notnull { - public static EnumeratedValueSetFactory Instance = new EnumeratedValueSetFactory(); + public static readonly EnumeratedValueSetFactory Instance = new EnumeratedValueSetFactory(); + + IValueSet IValueSetFactory.AllValues => EnumeratedValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => EnumeratedValueSet.NoValues; private EnumeratedValueSetFactory() { } @@ -44,11 +48,11 @@ bool IValueSetFactory.Related(BinaryOperatorKind relation, ConstantValue left, C return tc.FromConstantValue(left).Equals(tc.FromConstantValue(right)); } - IValueSet IValueSetFactory.Random(int expectedSize, Random random) + public IValueSet Random(int expectedSize, Random random) { TTC tc = default; T[] values = tc.RandomValues(expectedSize, random, expectedSize * 2); - IValueSet result = EnumeratedValueSet.AllValues.Complement(); + IValueSet result = EnumeratedValueSet.NoValues; Debug.Assert(result.IsEmpty); foreach (T value in values) result = result.Union(Related(Equal, value)); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSet.cs index 268d39f0b70be..bb545dab8caca 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSet.cs @@ -5,6 +5,7 @@ #nullable enable using System; +using System.Diagnostics; using System.Text; using Roslyn.Utilities; @@ -47,7 +48,27 @@ internal static IValueSet Random(int expectedSize, Random random) numbers: (IValueSet)NumericValueSetFactory.Instance.Random(expectedSize, random), hasNaN: hasNan); } - bool IValueSet.IsEmpty => !_hasNaN && _numbers.IsEmpty; + public bool IsEmpty => !_hasNaN && _numbers.IsEmpty; + + ConstantValue IValueSet.Sample + { + get + { + if (IsEmpty) + throw new ArgumentException(); + + if (!_numbers.IsEmpty) + { + var sample = _numbers.Sample; + Debug.Assert(sample is { }); + return sample; + } + + Debug.Assert(_hasNaN); + var tc = default(TFloatingTC); + return tc.ToConstantValue(tc.NaN); + } + } public static IValueSet Related(BinaryOperatorKind relation, TFloating value) { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSetFactory.cs index f01ffd022afc0..67bc0f9c22e40 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.FloatingValueSetFactory.cs @@ -16,6 +16,10 @@ private sealed class FloatingValueSetFactory : IValueSet private FloatingValueSetFactory() { } + IValueSet IValueSetFactory.AllValues => FloatingValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => FloatingValueSet.NoValues; + public IValueSet Related(BinaryOperatorKind relation, TFloating value) => FloatingValueSet.Related(relation, value); diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.INumericTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.INumericTC.cs index 7c9a250d489c7..df0281adc8e27 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.INumericTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.INumericTC.cs @@ -59,6 +59,11 @@ private interface INumericTC /// T Random(Random random); + /// + /// Produce the zero value for the type. + /// + T Zero { get; } + /// /// A formatter for values of type . This is needed for testing because /// the default ToString output for float and double changed between desktop and .net core, diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs index 96652fab749fc..47e721a0d3662 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.IntTC.cs @@ -19,6 +19,8 @@ private struct IntTC : INumericTC int INumericTC.MaxValue => int.MaxValue; + int INumericTC.Zero => 0; + public bool Related(BinaryOperatorKind relation, int left, int right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs index 5a35e264b25f1..b0d4c183eec35 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.LongTC.cs @@ -19,6 +19,8 @@ private struct LongTC : INumericTC long INumericTC.MaxValue => long.MaxValue; + long INumericTC.Zero => 0; + bool INumericTC.Related(BinaryOperatorKind relation, long left, long right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs index 7cc48dfd0a3f8..1f1d65c1b44d7 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSet.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -17,6 +18,8 @@ private sealed class NintValueSet : IValueSet, IValueSet { public readonly static NintValueSet AllValues = new NintValueSet(hasSmall: true, values: NumericValueSet.AllValues, hasLarge: true); + public readonly static NintValueSet NoValues = new NintValueSet(hasSmall: false, values: NumericValueSet.NoValues, hasLarge: false); + private readonly IValueSet _values; /// @@ -42,7 +45,24 @@ internal NintValueSet(bool hasSmall, IValueSet values, bool hasLarge) _hasLarge = hasLarge; } - bool IValueSet.IsEmpty => !_hasSmall && !_hasLarge && _values.IsEmpty; + public bool IsEmpty => !_hasSmall && !_hasLarge && _values.IsEmpty; + + ConstantValue? IValueSet.Sample + { + get + { + if (IsEmpty) + throw new ArgumentException(); + + if (!_values.IsEmpty) + return _values.Sample; + + // We do not support sampling from a nint value set without a specific value. The caller + // must arrange another way to get a sample, since we can return no specific value. This + // occurs when the value set was constructed from a pattern like `> (nint)int.MaxValue`. + return null; + } + } public bool All(BinaryOperatorKind relation, int value) { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSetFactory.cs index 22bb7c5a6b4df..4c2774fe00f44 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NintValueSetFactory.cs @@ -18,6 +18,10 @@ private sealed class NintValueSetFactory : IValueSetFactory, IValueSetFacto private NintValueSetFactory() { } + IValueSet IValueSetFactory.AllValues => NintValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => NintValueSet.NoValues; + public IValueSet Related(BinaryOperatorKind relation, int value) { return new NintValueSet( diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs index 74c8381f2b166..2b7ace45bdbcd 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSet.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -17,6 +18,8 @@ private sealed class NuintValueSet : IValueSet, IValueSet { public readonly static NuintValueSet AllValues = new NuintValueSet(values: NumericValueSet.AllValues, hasLarge: true); + public readonly static NuintValueSet NoValues = new NuintValueSet(values: NumericValueSet.NoValues, hasLarge: false); + private readonly IValueSet _values; /// @@ -33,7 +36,24 @@ internal NuintValueSet(IValueSet values, bool hasLarge) _hasLarge = hasLarge; } - bool IValueSet.IsEmpty => !_hasLarge && _values.IsEmpty; + public bool IsEmpty => !_hasLarge && _values.IsEmpty; + + ConstantValue? IValueSet.Sample + { + get + { + if (IsEmpty) + throw new ArgumentException(); + + if (!_values.IsEmpty) + return _values.Sample; + + // We do not support sampling from a nuint value set without a specific value. The caller + // must arrange another way to get a sample, since we can return no specific value. This + // occurs when the value set was constructed from a pattern like `> (nuint)uint.MaxValue`. + return null; + } + } public bool All(BinaryOperatorKind relation, uint value) { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSetFactory.cs index b4606ce7a2d53..06f5aa7c7d53c 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NuintValueSetFactory.cs @@ -18,6 +18,10 @@ private sealed class NuintValueSetFactory : IValueSetFactory, IValueSetFac private NuintValueSetFactory() { } + IValueSet IValueSetFactory.AllValues => NuintValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => NuintValueSet.NoValues; + public IValueSet Related(BinaryOperatorKind relation, uint value) { return new NuintValueSet( diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSet.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSet.cs index 9b4da121c21e0..421fd9fb49d2f 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSet.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSet.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -25,6 +26,7 @@ private sealed class NumericValueSet : IValueSet where TTC : struct, private readonly ImmutableArray<(T first, T last)> _intervals; public readonly static NumericValueSet AllValues = new NumericValueSet(default(TTC).MinValue, default(TTC).MaxValue); + public readonly static NumericValueSet NoValues = new NumericValueSet(ImmutableArray<(T first, T last)>.Empty); internal NumericValueSet(T first, T last) : this(ImmutableArray.Create((first, last))) @@ -49,7 +51,24 @@ internal NumericValueSet(ImmutableArray<(T first, T last)> intervals) _intervals = intervals; } - bool IValueSet.IsEmpty => _intervals.Length == 0; + public bool IsEmpty => _intervals.Length == 0; + + ConstantValue IValueSet.Sample + { + get + { + if (IsEmpty) + throw new ArgumentException(); + + // Prefer a value near zero. + var tc = default(TTC); + var gz = NumericValueSetFactory.Instance.Related(BinaryOperatorKind.GreaterThanOrEqual, tc.Zero); + var t = (NumericValueSet)this.Intersect(gz); + if (!t.IsEmpty) + return tc.ToConstantValue(t._intervals[0].first); + return tc.ToConstantValue(this._intervals[this._intervals.Length - 1].last); + } + } public bool Any(BinaryOperatorKind relation, T value) { diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSetFactory.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSetFactory.cs index 58f9e09a8cf31..12d48f24625c4 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSetFactory.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.NumericValueSetFactory.cs @@ -22,6 +22,10 @@ private sealed class NumericValueSetFactory : IValueSetFactory where { public static readonly NumericValueSetFactory Instance = new NumericValueSetFactory(); + IValueSet IValueSetFactory.AllValues => NumericValueSet.AllValues; + + IValueSet IValueSetFactory.NoValues => NumericValueSet.NoValues; + private NumericValueSetFactory() { } public IValueSet Related(BinaryOperatorKind relation, T value) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs index c3486ca3023c9..7e745dd05b157 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SByteTC.cs @@ -19,6 +19,8 @@ private struct SByteTC : INumericTC sbyte INumericTC.MaxValue => sbyte.MaxValue; + sbyte INumericTC.Zero => 0; + bool INumericTC.Related(BinaryOperatorKind relation, sbyte left, sbyte right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs index 1c07ce50fadcb..6e8fc986f9412 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ShortTC.cs @@ -19,6 +19,8 @@ private struct ShortTC : INumericTC short INumericTC.MaxValue => short.MaxValue; + short INumericTC.Zero => 0; + bool INumericTC.Related(BinaryOperatorKind relation, short left, short right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs index 52e21a673f7bd..371a534dafc58 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.SingleTC.cs @@ -21,6 +21,8 @@ private struct SingleTC : FloatingTC, INumericTC float FloatingTC.NaN => float.NaN; + float INumericTC.Zero => 0; + /// /// The implementation of Next depends critically on the internal representation of an IEEE floating-point /// number. Every bit sequence between the representation of 0 and MaxValue represents a distinct diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs index 44f2fe5f0280c..f6d5c2a2ae6a3 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.StringTC.cs @@ -23,7 +23,7 @@ string IEquatableValueTC.FromConstantValue(ConstantValue constantValue) string[] IEquatableValueTC.RandomValues(int count, Random random, int scope) { Debug.Assert(count > 0); - Debug.Assert(scope > count); + Debug.Assert(scope >= count); string[] result = new string[count]; int next = 0; for (int i = 0; i < scope; i++) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs index 069f69e05154e..24fceb6700e84 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UIntTC.cs @@ -19,6 +19,8 @@ private struct UIntTC : INumericTC uint INumericTC.MaxValue => uint.MaxValue; + uint INumericTC.Zero => 0; + public bool Related(BinaryOperatorKind relation, uint left, uint right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs index ce7558c5967e0..02d3c7b77e868 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.ULongTC.cs @@ -19,6 +19,8 @@ private struct ULongTC : INumericTC ulong INumericTC.MaxValue => ulong.MaxValue; + ulong INumericTC.Zero => 0; + bool INumericTC.Related(BinaryOperatorKind relation, ulong left, ulong right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs index 19440d0288c66..af44f257e758a 100644 --- a/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs +++ b/src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.UShortTC.cs @@ -19,6 +19,8 @@ private struct UShortTC : INumericTC ushort INumericTC.MaxValue => ushort.MaxValue; + ushort INumericTC.Zero => 0; + bool INumericTC.Related(BinaryOperatorKind relation, ushort left, ushort right) { switch (relation) diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index b29c49b51361d..11321d0f1f607 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - Výraz switch nezpracovává všechny možné hodnoty svého vstupního typu (není úplný). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Výraz switch nezpracovává některé vstupy s hodnotou null (není vyčerpávající). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 935515d0a8674..06512493acfa0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - Der switch-Ausdruck verarbeitet nicht alle möglichen Werte des zugehörigen Eingabetyps (nicht umfassend). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Der switch-Ausdruck verarbeitet einige NULL-Eingaben nicht (nicht umfassend). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 87f343ed6efdf..d951fffe51264 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - La expresión switch no controla todos los valores posibles de su tipo de entrada (no es exhaustiva). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - La expresión switch no controla algunas entradas de tipo NULL (no es exhaustiva). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 1186574e2cbd1..be5d17fe01552 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - L'expression switch ne prend pas en charge toutes les valeurs possibles de son type d'entrée (elle n'est pas exhaustive). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - L'expression switch ne prend pas en charge certaines entrées ayant une valeur null (elle n'est pas exhaustive). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index b30f6939144b8..0c39aea3033d4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - L'espressione switch non gestisce tutti i possibili valori del relativo tipo di input (non è esaustiva). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - L'espressione switch non gestisce alcuni input Null (non è esaustiva). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index da5f28027d5d9..83ca6b556423b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - switch 式が入力の種類で可能なすべての値を処理していません (すべてを網羅していません)。 + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - switch 式が一部の null 入力を処理しません (すべてを網羅していません)。 + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 9ff84d130fe38..7c64d29d60080 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - switch 식에서 입력 형식의 가능한 값을 모두 처리하지는 않습니다(전체 아님). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - switch 식은 일부 null 입력을 처리하지 않습니다(전체 아님). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index a90cfb2a9545f..025b4b94771d1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - Wyrażenie switch nie obsługuje wszystkich możliwych wartości jego typu danych wejściowych (nie jest kompletne). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Wyrażenie switch nie obsługuje niektórych danych wejściowych o wartości null (nie jest kompletne). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 9ce099fe06cb7..a66efba099ab8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -2278,13 +2278,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - A expressão switch não manipula todos os valores possíveis de seu tipo de entrada (não é exaustiva). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - A expressão switch não manipula algumas entradas nulas (não é exaustiva). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 2487721e91dc2..f77f0fa45eb4e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - Выражение switch обрабатывает не все возможные значения своего типа входных данных (оно не полное). + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Выражение switch не обрабатывает некоторые входные данные NULL (оно не полное). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index e7fae4b9f9620..98644e909656f 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - Switch ifadesi giriş türünün tüm olası değerlerini işlemiyor. (İfade tam kapsamlı değil.) + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Switch ifadesi bazı null girişleri işlemiyor (tam kapsamlı değil). + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 14469ea631e81..033f7b228d986 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - switch 表达式不会处理属于其输入类型的所有可能值(它并非详尽无遗)。 + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Switch 表达式不会处理某些为 null 的输入(它并非详尽无遗)。 + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ef97a010d4afb..04fc47746bd1a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -2280,13 +2280,13 @@ - The switch expression does not handle all possible values of its input type (it is not exhaustive). - switch 運算式未處理其輸入類型可能的值 (並非全部)。 + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{0}' is not covered. - The switch expression does not handle some null inputs (it is not exhaustive). - Switch 運算式未處理某些 null 輸入 (並不徹底)。 + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. + The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '{0}' is not covered. diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs index 6cec7b87bf41f..9af3c03850154 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/PatternTests.cs @@ -3199,9 +3199,9 @@ static class C { "; var compilation = CreateEmptyCompilation(source, options: TestOptions.ReleaseDll); compilation.GetDiagnostics().Verify( - // (9,38): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (9,38): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // public static bool M(int i) => i switch { 1 => true }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(9, 38) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(9, 38) ); compilation.GetEmitDiagnostics().Verify( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. @@ -3209,9 +3209,9 @@ static class C { // (9,36): error CS0656: Missing compiler required member 'System.InvalidOperationException..ctor' // public static bool M(int i) => i switch { 1 => true }; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "i switch { 1 => true }").WithArguments("System.InvalidOperationException", ".ctor").WithLocation(9, 36), - // (9,38): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (9,38): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // public static bool M(int i) => i switch { 1 => true }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(9, 38) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(9, 38) ); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchExpression.cs index b90ed82441dfc..98790da7d6fe9 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchExpression.cs @@ -77,9 +77,9 @@ void M(int? x, object y) Arms(0) "; var expectedDiagnostics = new[] { - // file.cs(7,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // file.cs(7,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // y = /**/x switch { }/**/; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(7, 25) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(7, 25) }; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } @@ -509,9 +509,9 @@ void M(int? x, object y) ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5) (Syntax: '5') "; var expectedDiagnostics = new[] { - // file.cs(7,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // file.cs(7,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // y = /**/x switch { 1 => 2, _ when false => 5 }/**/; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(7, 25) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(7, 25) }; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs index 5ba16efabc223..9ccab4d2b78fa 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs @@ -3326,9 +3326,9 @@ public static void Main() } "; var expectedDiagnostics = new[] { - // file.cs(6,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // file.cs(6,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // var r = 1 switch { }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(6, 19), + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(6, 19), // file.cs(6,19): error CS8506: No best type was found for the switch expression. // var r = 1 switch { }; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(6, 19) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 8455d11226e6b..7b329cc0ca67a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -129399,12 +129399,12 @@ public void M7(C? a, bool b) "; var comp = CreateCompilation(source); comp.VerifyDiagnostics( - // (4,30): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + // (4,30): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern 'null' is not covered. // public int M1(C? a) => a switch { C _ => 0 }; // warns - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(4, 30), - // (8,35): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("null").WithLocation(4, 30), + // (8,35): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(_, null)' is not covered. // public int M4(C? a) => (1, a) switch { (_, C _) => 0 }; // warns - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(8, 35), + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(_, null)").WithLocation(8, 35), // (36,9): warning CS8602: Dereference of a possibly null reference. // a.ToString(); // warns Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(36, 9) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesVsPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesVsPatterns.cs index 2f6a11192c05d..ad8e2f2118223 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesVsPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesVsPatterns.cs @@ -1319,27 +1319,27 @@ void M8(object o, bool b) "; var comp = CreateNullableCompilation(source); comp.VerifyDiagnostics( - // (18,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + // (18,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // _ = t switch // 1 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(18, 15), - // (27,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(18, 15), + // (27,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // _ = t switch // 2 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(27, 15), - // (36,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(27, 15), + // (36,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(not null, null)' is not covered. // _ = t switch // 3 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(36, 15), - // (46,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(not null, null)").WithLocation(36, 15), + // (46,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // _ = t switch // 4 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(46, 15), - // (55,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(46, 15), + // (55,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // _ = t switch // 5 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(55, 15), - // (64,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(55, 15), + // (64,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(not null, null)' is not covered. // _ = t switch // 6 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(64, 15), - // (73,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(not null, null)").WithLocation(64, 15), + // (73,15): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern 'null' is not covered. // _ = o switch // 7 not exhaustive - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(73, 15)); + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("null").WithLocation(73, 15)); } [Fact] @@ -1384,18 +1384,18 @@ int M5(string s1, string s2) }"; var comp = CreateNullableCompilation(source); comp.VerifyDiagnostics( - // (12,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + // (12,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // return (s1, s2) switch { // 1 - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(12, 25), - // (18,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(12, 25), + // (18,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // return (s1, s2) switch { // 2 - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(18, 25), - // (24,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(18, 25), + // (24,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, _)' is not covered. // return (s1, s2) switch { // 3 - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(24, 25), - // (30,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, _)").WithLocation(24, 25), + // (30,25): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, "")' is not covered. // return (s1, s2) switch { // 4 - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithLocation(30, 25)); + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, \"\")").WithLocation(30, 25)); } [Fact, WorkItem(31881, "https://github.com/dotnet/roslyn/issues/31881")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs index bcd136036de16..187d4b65adea0 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs @@ -494,9 +494,9 @@ public static void Main() } }"; CreatePatternCompilation(source).VerifyDiagnostics( - // (5,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (5,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // var r = 1 switch { }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(5, 19), + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(5, 19), // (5,19): error CS8506: No best type was found for the switch expression. // var r = 1 switch { }; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(5, 19)); @@ -518,9 +518,9 @@ public static void M() {} public delegate void D(); }"; CreatePatternCompilation(source).VerifyDiagnostics( - // (5,19): warning CS8409: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (5,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '3' is not covered. // var x = 1 switch { 0 => M, 1 => new D(M), 2 => M }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(5, 19) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("3").WithLocation(5, 19) ); } @@ -609,9 +609,9 @@ public static void Main() }"; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (7,19): warning CS8409: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (7,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // var c = a switch { var x2 when x2 is var x3 => x3 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(7, 19) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(7, 19) ); var names = new[] { "x1", "x2", "x3", "x4", "x5" }; var tree = compilation.SyntaxTrees[0]; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs index 37c64e5ad9d73..cfde836b41983 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests3.cs @@ -1612,12 +1612,12 @@ static void Main() { } }"; CreateCompilation(source).VerifyDiagnostics( - // (5,41): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). - // var x = ((Func)(0 switch { 0 => _ => {}}))(0); - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(5, 41), - // (5,57): error CS1643: Not all code paths return a value in lambda expression of type 'Func' - // var x = ((Func)(0 switch { 0 => _ => {}}))(0); - Diagnostic(ErrorCode.ERR_AnonymousReturnExpected, "=>").WithArguments("lambda expression", "System.Func").WithLocation(5, 57) + // (5,41): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '1' is not covered. + // var x = ((Func)(0 switch { 0 => _ => {}}))(0); + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("1").WithLocation(5, 41), + // (5,57): error CS1643: Not all code paths return a value in lambda expression of type 'Func' + // var x = ((Func)(0 switch { 0 => _ => {}}))(0); + Diagnostic(ErrorCode.ERR_AnonymousReturnExpected, "=>").WithArguments("lambda expression", "System.Func").WithLocation(5, 57) ); } @@ -1633,12 +1633,12 @@ static void Main() { static void M(int x) {} }"; CreateCompilation(source).VerifyDiagnostics( - // (5,41): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). - // var x = ((Func)(0 switch { 0 => M }))(0); - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(5, 41), - // (5,55): error CS0407: 'void C.M(int)' has the wrong return type - // var x = ((Func)(0 switch { 0 => M }))(0); - Diagnostic(ErrorCode.ERR_BadRetType, "M").WithArguments("C.M(int)", "void").WithLocation(5, 55) + // (5,41): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '1' is not covered. + // var x = ((Func)(0 switch { 0 => M }))(0); + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("1").WithLocation(5, 41), + // (5,55): error CS0407: 'void C.M(int)' has the wrong return type + // var x = ((Func)(0 switch { 0 => M }))(0); + Diagnostic(ErrorCode.ERR_BadRetType, "M").WithArguments("C.M(int)", "void").WithLocation(5, 55) ); } @@ -2460,9 +2460,9 @@ static void M2(object o) // (9,18): error CS8120: The switch case is unreachable. It has already been handled by a previous case or it is impossible to match. // case 1L or 2L: Console.Write(2); break; Diagnostic(ErrorCode.ERR_SwitchCaseSubsumed, "1L or 2L").WithLocation(9, 18), - // (14,15): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (14,15): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. // _ = o switch - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(14, 15), + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(14, 15), // (17,13): error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match. // 1L or 2L => 2, Diagnostic(ErrorCode.ERR_SwitchArmSubsumed, "1L or 2L").WithLocation(17, 13) @@ -2908,9 +2908,9 @@ class C else { compilation.VerifyDiagnostics( - // (15,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (15,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'E.Five' is not covered. // static int M(E c) => c switch - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(15, 28) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("E.Five").WithLocation(15, 28) ); } } @@ -2964,9 +2964,9 @@ class C else { compilation.VerifyEmitDiagnostics( - // (15,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (15,28): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'E.Five' is not covered. // static int M(E c) => c switch - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(15, 28) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("E.Five").WithLocation(15, 28) ); } } @@ -4857,7 +4857,7 @@ public void RelationalFuzz_01(int seed, int numCases, char kind, string point) 'x' => "nint", 'y' => "nuint", 'z' => "int", - _ => throw new ArgumentException(nameof(kind)), + _ => throw new ArgumentException("unexpected", nameof(kind)), }; if (kind is 'x' || kind is 'y' || kind is 'z') { @@ -5488,16 +5488,254 @@ static void Main() string expectedOutput = "bb"; var compilation = CreateCompilation(source, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularWithPatternCombinators); compilation.VerifyDiagnostics( - // (7,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). - // var str = x switch // does not handle zero - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(7, 21), - // (15,17): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). - // str = x switch // does not handle zero - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(15, 17) + // (7,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. + // var str = x switch // does not handle zero + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(7, 21), + // (15,17): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered. + // str = x switch // does not handle zero + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("0").WithLocation(15, 17) ); var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); } + [Fact] + public void NonexhaustiveEnumDiagnostic_01() + { + var source = +@" +class C +{ + int M(Color color) => color switch + { + Color.Red => 0, + Color.Blue => 2, + }; +} +enum Color { Red, Greed, Blue } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,33): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Color.Greed' is not covered. + // int M(Color color) => color switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Color.Greed").WithLocation(4, 33) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_02() + { + var source = +@" +class C +{ + int M(Color color) => color switch + { + <= Color.Greed => 0, + }; +} +enum Color { Red, Greed, Blue } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,33): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Color.Blue' is not covered. + // int M(Color color) => color switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Color.Blue").WithLocation(4, 33) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_03() + { + var source = +@" +class C +{ + int M(Color color) => color switch + { + <= Color.Greed => 0, + > Color.Greed => 1, + }; +} +enum Color { Red, Greed, Blue } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_04() + { + var source = +@" +class C +{ + int M(Color color) => color switch + { + Color.Red => 0, + Color.Greed => 1, + Color.Blue => 2, + }; +} +enum Color { Red, Greed, Blue } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,33): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(Color)3' is not covered. + // int M(Color color) => color switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(Color)3").WithLocation(4, 33) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_05() + { + var source = +@"#nullable enable +class C +{ + int M(bool a, bool b) => (a, b) switch + { + (true, _) => 1, + (_, true) => 2, + }; +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,37): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(false, false)' is not covered. + // int M(bool a, bool b) => (a, b) switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(false, false)").WithLocation(4, 37) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_06() + { + var source = +@"#nullable enable +class C +{ + int M(string? a, string? b) => (a, b) switch + { + (string, _) => 1, + (_, string) => 2, + }; + void M2(object? a, object? b) + { + _ = (a, b) switch { (null, _) => 1 }; + _ = (a, b) switch { (null, _) => 1, (_, null) => 2 }; + _ = (a, b) switch { (_, not null) => 1 }; + _ = (a, b) switch { (_, not null) => 1, (not null, _) => 2 }; + } +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,43): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, null)' is not covered. + // int M(string? a, string? b) => (a, b) switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, null)").WithLocation(4, 43), + // (11,20): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(not null, _)' is not covered. + // _ = (a, b) switch { (null, _) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(not null, _)").WithLocation(11, 20), + // (12,20): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(not null, not null)' is not covered. + // _ = (a, b) switch { (null, _) => 1, (_, null) => 2 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(not null, not null)").WithLocation(12, 20), + // (13,20): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(_, null)' is not covered. + // _ = (a, b) switch { (_, not null) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(_, null)").WithLocation(13, 20), + // (14,20): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern '(null, null)' is not covered. + // _ = (a, b) switch { (_, not null) => 1, (not null, _) => 2 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("(null, null)").WithLocation(14, 20) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_07() + { + var source = +@"#nullable enable +class C +{ + int M1(nint a) => a switch { <= (nint)int.MaxValue => 1 }; + int M2(nint a) => a switch { >= (nint)int.MinValue => 1 }; + int M3(nuint a) => a switch { <= (nuint)uint.MaxValue => 1 }; + // Cannot test these cases due to https://github.com/dotnet/roslyn/issues/44651 + //int M4(Enint a) => a switch { <= (Enint)int.MaxValue => 1 }; + //int M5(Enint a) => a switch { >= (Enint)int.MinValue => 1 }; + //int M6(Enuint a) => a switch { <= (Enuint)uint.MaxValue => 1 }; +} +//enum Enint : nint { } +//enum Enuint : nuint { } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '> (nint)int.MaxValue' is not covered. + // int M1(nint a) => a switch { <= (nint)int.MaxValue => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("> (nint)int.MaxValue").WithLocation(4, 25), + // (5,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '< (nint)int.MinValue' is not covered. + // int M2(nint a) => a switch { >= (nint)int.MinValue => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("< (nint)int.MinValue").WithLocation(5, 25), + // (6,26): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '> (nuint)uint.MaxValue' is not covered. + // int M3(nuint a) => a switch { <= (nuint)uint.MaxValue => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("> (nuint)uint.MaxValue").WithLocation(6, 26) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_08() + { + var source = +@" +class C +{ + int M1(float f) => f switch { < 0 => 1, >= 0 => 2 }; // float.NaN + int M2(float f) => f switch { > float.NegativeInfinity => 1, float.NaN => 2 }; // float.NegativeInfinity + int M3(float f) => f switch { < float.PositiveInfinity => 1, float.NaN => 2 }; // float.PositiveInfinity + int M1(double f) => f switch { < 0 => 1, >= 0 => 2 }; // double.NaN + int M2(double f) => f switch { > double.NegativeInfinity => 1, double.NaN => 2 }; // double.NegativeInfinity + int M3(double f) => f switch { < double.PositiveInfinity => 1, double.NaN => 2 }; // double.PositiveInfinity +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,26): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'float.NaN' is not covered. + // int M1(float f) => f switch { < 0 => 1, >= 0 => 2 }; // float.NaN + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("float.NaN").WithLocation(4, 26), + // (5,26): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'float.NegativeInfinity' is not covered. + // int M2(float f) => f switch { > float.NegativeInfinity => 1, float.NaN => 2 }; // float.NegativeInfinity + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("float.NegativeInfinity").WithLocation(5, 26), + // (6,26): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'float.PositiveInfinity' is not covered. + // int M3(float f) => f switch { < float.PositiveInfinity => 1, float.NaN => 2 }; // float.PositiveInfinity + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("float.PositiveInfinity").WithLocation(6, 26), + // (7,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'double.NaN' is not covered. + // int M1(double f) => f switch { < 0 => 1, >= 0 => 2 }; // double.NaN + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("double.NaN").WithLocation(7, 27), + // (8,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'double.NegativeInfinity' is not covered. + // int M2(double f) => f switch { > double.NegativeInfinity => 1, double.NaN => 2 }; // double.NegativeInfinity + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("double.NegativeInfinity").WithLocation(8, 27), + // (9,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'double.PositiveInfinity' is not covered. + // int M3(double f) => f switch { < double.PositiveInfinity => 1, double.NaN => 2 }; // double.PositiveInfinity + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("double.PositiveInfinity").WithLocation(9, 27) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_09() + { + var source = +@" +class C +{ + int M1(string s) => s switch { """" => 1, ""1"" => 2, ""A"" => 3, }; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '"B"' is not covered. + // int M1(string s) => s switch { "" => 1, "1" => 2, "A" => 3, }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("\"B\"").WithLocation(4, 27) + ); + } + [Fact, WorkItem(44398, "https://github.com/dotnet/roslyn/issues/44398")] public void MismatchedExpressionPattern() { @@ -5833,5 +6071,550 @@ static void Main() Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(26, 21) ); } + + [Fact] + public void NonexhaustiveEnumDiagnostic_10() + { + var source = +@"#nullable enable +class C +{ + int M1(string? s) => s switch { string => 1 }; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,28): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern 'null' is not covered. + // int M1(string? s) => s switch { string => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("null").WithLocation(4, 28) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_11() + { + var source = +@"#nullable enable +class C +{ + int M1(object o) => o switch { not Q(1, 2.5) => 1 }; + int M2(object o) => o switch { not Q(1L, 2.5F) => 1 }; + int M3(object o) => o switch { not Q((byte)1, (short)2) => 1 }; + int M4(object o) => o switch { not Q((uint)1, (ulong)2) => 1 }; + int M5(object o) => o switch { not Q((long)1, (sbyte)2) => 1 }; +} +class Q +{ + public void Deconstruct(out object o1, out object o2) => throw null!; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q(1, 2.5D)' is not covered. + // int M1(object o) => o switch { not Q(1, 2.5) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q(1, 2.5D)").WithLocation(4, 27), + // (5,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q(1L, 2.5F)' is not covered. + // int M2(object o) => o switch { not Q(1L, 2.5F) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q(1L, 2.5F)").WithLocation(5, 27), + // (6,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q((byte)1, (short)2)' is not covered. + // int M3(object o) => o switch { not Q((byte)1, (short)2) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q((byte)1, (short)2)").WithLocation(6, 27), + // (7,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q(1U, 2UL)' is not covered. + // int M4(object o) => o switch { not Q((uint)1, (ulong)2) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q(1U, 2UL)").WithLocation(7, 27), + // (8,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q(1L, (sbyte)2)' is not covered. + // int M5(object o) => o switch { not Q((long)1, (sbyte)2) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q(1L, (sbyte)2)").WithLocation(8, 27) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_12() + { + var source = +@"#nullable enable +class C +{ + int M1(object o) => o switch { not (Q(1, 2.5) or I) => 1 }; + int M2(object o) => o switch { not (Q(1, 2.5) { P1: 1 } and Q(3, 4, 5) { P2: 2 }) => 1 }; +} +class Q +{ + public void Deconstruct(out object o1, out object o2) => throw null!; + public void Deconstruct(out object o1, out object o2, out object o3) => throw null!; + public int P1 = 5; + public int P2 = 6; +} +interface I +{ +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'I' is not covered. + // int M1(object o) => o switch { not (Q(1, 2.5) or I) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("I").WithLocation(4, 27), + // (5,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q(1, 2.5D) and (3, 4, 5) { P1: 1, P2: 2 }' is not covered. + // int M2(object o) => o switch { not (Q(1, 2.5) { P1: 1 } and Q(3, 4, 5) { P2: 2 }) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q(1, 2.5D) and (3, 4, 5) { P1: 1, P2: 2 }").WithLocation(5, 27) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_13() + { + var source = +@"#nullable enable +class C +{ + int M1(Q? q) => q switch { not null => 1 }; + int M2(Q? q) => q switch { null => 1 }; +} +class Q +{ +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,23): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern 'null' is not covered. + // int M1(Q? q) => q switch { not null => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("null").WithLocation(4, 23), + // (5,23): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'not null' is not covered. + // int M2(Q? q) => q switch { null => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("not null").WithLocation(5, 23) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_14() + { + var source = +@"#nullable enable +class C +{ + int M1((int x, int y) t) => t switch { not ((1, _) { y: 2 }) => 1 }; + int M2((int x, int y) t) => t switch { not ((1, _) { Item2: 2 }) => 1 }; + int M3((int x, int y)? t) => t switch { (_, _) => 1 }; + int M4((int x, int y)? t) => t switch { not (_, _) => 1 }; +} +class Q +{ +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,35): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(1, 2)' is not covered. + // int M1((int x, int y) t) => t switch { not ((1, _) { y: 2 }) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(1, 2)").WithLocation(4, 35), + // (5,35): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(1, 2)' is not covered. + // int M2((int x, int y) t) => t switch { not ((1, _) { Item2: 2 }) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(1, 2)").WithLocation(5, 35), + // (6,36): warning CS8655: The switch expression does not handle some null inputs (it is not exhaustive). For example, the pattern 'null' is not covered. + // int M3((int x, int y)? t) => t switch { (_, _) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustiveForNull, "switch").WithArguments("null").WithLocation(6, 36), + // (7,36): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'not null' is not covered. + // int M4((int x, int y)? t) => t switch { not (_, _) => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("not null").WithLocation(7, 36) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_15() + { + var source = +@"#nullable enable +class C +{ + int M1(Q q) => q switch { { P1: < 10 } => 1 }; + int M2(Q q) => q switch { { P1: > -100, P2: < int.MaxValue } => 1 }; +} +class Q +{ + public int P1 = 5; + public int P2 = 6; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ P1: 10 }' is not covered. + // int M1(Q q) => q switch { { P1: < 10 } => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ P1: 10 }").WithLocation(4, 22), + // (5,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ P1: -100 }' is not covered. + // int M2(Q q) => q switch { { P1: > -100, P2: < int.MaxValue } => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ P1: -100 }").WithLocation(5, 22) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_16() + { + var source = +@"#nullable enable +class C +{ + int M1(Q q) => q switch { { P1: < 10 } => 1, null => 2 }; + int M2(Q q) => q switch { { P1: > -100, P2: < int.MaxValue } => 1, null => 2 }; +} +class Q +{ + public int P1 = 5; + public int P2 = 6; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ P1: 10 }' is not covered. + // int M1(Q q) => q switch { { P1: < 10 } => 1, null => 2 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ P1: 10 }").WithLocation(4, 22), + // (5,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ P1: -100 }' is not covered. + // int M2(Q q) => q switch { { P1: > -100, P2: < int.MaxValue } => 1, null => 2 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ P1: -100 }").WithLocation(5, 22) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_17() + { + var source = +@"#nullable enable +class C +{ + int M1(object o) => o switch { not Q => 1 }; + int M2(object o) => o switch { not Q and not W => 1 }; + int M3(object o) => o switch { not W and not Q => 1 }; +} +class Q { } +class W { } +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q' is not covered. + // int M1(object o) => o switch { not Q => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q").WithLocation(4, 27), + // (5,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'Q' is not covered. + // int M2(object o) => o switch { not Q and not W => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("Q").WithLocation(5, 27), + // (6,27): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'W' is not covered. + // int M3(object o) => o switch { not W and not Q => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("W").WithLocation(6, 27) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_18() + { + var source = +@"#nullable enable +class C +{ + int M1(Q q) => q switch { not (1) { } => 1 }; + int M2(System.ValueTuple q) => q switch { not (1) { } => 1 }; +} +class Q +{ + public void Deconstruct(out int X) => throw null!; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(1) { }' is not covered. + // int M1(Q q) => q switch { not (1) { } => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(1) { }").WithLocation(4, 22), + // (5,43): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(1) { }' is not covered. + // int M2(System.ValueTuple q) => q switch { not (1) { } => 1 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(1) { }").WithLocation(5, 43) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_19() + { + var source = +@"#nullable enable +class C +{ + int M((bool, bool, bool, bool) t) => t switch + { + (false, true, true, true) => 3, + // (false, true, false, true) => 4, + (false, false, false, true) => 5, + (false, false, true, true) => 6, + (true, _, false, _) => 1, + (true, _, true, _) => 2, + (false, _, _, false) => 7, + }; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,44): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(false, true, false, true)' is not covered. + // int M((bool, bool, bool, bool) t) => t switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(false, true, false, true)").WithLocation(4, 44) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_20() + { + // The is an example of a situation which is too complicated for our implementation to produce a precise + // example pattern that is not covered. We fall back to suggesting the pattern `_` in that case. + // For situations such as this, that is probably better than producing a precise pattern. + var source = +@"#nullable enable +class C +{ + int M(string s) => s switch + { + """" => 0, + ""A"" => 1, + ""BB"" => 2, + { Length: 1 } => 3, + }; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,26): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. + // int M(string s) => s switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(4, 26) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_21() + { + var source = +@"#nullable enable +class C +{ + int M(Q t) => t switch + { + { A: false, B: true, C: true, D: true } => 3, + // { A: false, B: true, C: false, D: true } => 4, + { A: false, B: false, C: false, D: true } => 5, + { A: false, B: false, C: true, D: true } => 6, + { A: true, C: false } => 1, + { A: true, C: true } => 2, + { A: false, D: false } => 7, + }; +} +class Q +{ + public bool A = true; + public bool B = true; + public bool C = true; + public bool D = true; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ A: false, B: true, C: false, D: true }' is not covered. + // int M(Q t) => t switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ A: false, B: true, C: false, D: true }").WithLocation(4, 21) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_22() + { + var source = +@"#nullable enable +class C +{ + int M(Q t) => t switch + { + { A: true, C: true } => 2, + { A: false, D: false } => 7, + { A: false, B: true, C: true, D: true } => 3, + // { A: false, B: true, C: false, D: true } => 4, + { A: false, B: false, C: false, D: true } => 5, + { A: false, B: false, C: true, D: true } => 6, + { A: true, C: false } => 1, + }; +} +class Q +{ + public bool A = true; + public bool B = true; + public bool C = true; + public bool D = true; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ A: false, D: true, B: true, C: false }' is not covered. + // int M(Q t) => t switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ A: false, D: true, B: true, C: false }").WithLocation(4, 21) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_23() + { + var source = +@"#nullable enable +class C +{ + int M(Q t) => t switch + { + (0, """") => 0, + (1, ""A"") => 1, + }; +} +class Q +{ +} +static class Extensions +{ + public static void Deconstruct(this Q q, out int X, out string Y) + { + X = 2; + Y = ""Y""; + } +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,21): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, "A")' is not covered. + // int M(Q t) => t switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments(@"(0, ""A"")").WithLocation(4, 21) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_24() + { + var source = +@"#nullable enable +class C +{ + int M1(Q o) => o switch { not ((1) { } and (2, 3)) => 0 }; + int M2(Q o) => o switch { not ((2, 3) and (1) { }) => 0 }; +} +class Q +{ + public void Deconstruct(out object o1) => throw null!; + public void Deconstruct(out object o1, out object o2) => throw null!; +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (4,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(1) { } and (2, 3)' is not covered. + // int M1(Q o) => o switch { not ((1) { } and (2, 3)) => 0 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(1) { } and (2, 3)").WithLocation(4, 22), + // (5,22): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(2, 3) and (1) { }' is not covered. + // int M2(Q o) => o switch { not ((2, 3) and (1) { }) => 0 }; + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(2, 3) and (1) { }").WithLocation(5, 22) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_25() + { + var source = +@$"class C +{{ + static int M(string s) => s switch + {{ + """" => 0, + {Enumerable.Range((int)'A', (int)('z' - 'A') + 1).Select(x => (char)x) + .Aggregate("", (s, c) => s + $"{ObjectDisplay.FormatPrimitive(c.ToString(), ObjectDisplayOptions.EscapeNonPrintableCharacters | ObjectDisplayOptions.UseQuotes)} => 0, ")} + }}; +}}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (3,33): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '"0"' is not covered. + // static int M(string s) => s switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments(@"""0""").WithLocation(3, 33) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_26() + { + var source = +@$"class C +{{ + static int M(string s) => s switch + {{ + """" => 0, + {Enumerable.Range((int)'A', (int)('z' - 'A') + 1).Select(x => (char)x) + .Aggregate("", (s, c) => s + $"{ObjectDisplay.FormatPrimitive(c.ToString(), ObjectDisplayOptions.EscapeNonPrintableCharacters | ObjectDisplayOptions.UseQuotes)} => 0, ")} + {Enumerable.Range(0, 20) + .Aggregate("", (s, i) => s + $"{ObjectDisplay.FormatPrimitive(i.ToString(), ObjectDisplayOptions.EscapeNonPrintableCharacters | ObjectDisplayOptions.UseQuotes)} => 0, ")} + }}; +}}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (3,33): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '"20"' is not covered. + // static int M(string s) => s switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments(@"""20""").WithLocation(3, 33) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_27() + { + var source = +@"class C +{ + static int M(string s) => s switch + { + not { Length: 1, Length: 1 } => 0 + }; +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (3,33): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ Length: 1 }' is not covered. + // static int M(string s) => s switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ Length: 1 }").WithLocation(3, 33) + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_28() + { + // Note that "not" of an impossible pattern handles everything. + var source = +@"class C +{ + static int M(string s) => s switch + { + not { Length: 1, Length: 2 } => 0 + }; +}"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + ); + } + + [Fact] + public void NonexhaustiveEnumDiagnostic_29() + { + var source = +@"class C +{ + static int M((int x, int y) s) => s switch + { + not (1, 2) { Extra: 3 } => 0 + }; +} +namespace System +{ + public struct ValueTuple + { + public T1 Item1; + public T2 Item2; + public int Extra; + } +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithPatternCombinators); + compilation.VerifyDiagnostics( + // (3,41): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '{ x: 1, y: 2, Extra: 3 }' is not covered. + // static int M((int x, int y) s) => s switch + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("{ x: 1, y: 2, Extra: 3 }").WithLocation(3, 41) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs index a081c9a3aa235..12e5e3e0b8984 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs @@ -1778,9 +1778,9 @@ static int M1(bool? b1, bool? b2) "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (6,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (6,25): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(true, false)' is not covered. // return (b1, b2) switch { - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(6, 25) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(true, false)").WithLocation(6, 25) ); } @@ -2050,9 +2050,9 @@ static void Main() "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _)' is not covered. // _ = t switch { (3, 4) => 1 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(9, 19) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _)").WithLocation(9, 19) ); CompileAndVerify(compilation, expectedOutput: "InvalidOperationException"); } @@ -2093,9 +2093,9 @@ public SwitchExpressionException() {} "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _)' is not covered. // _ = t switch { (3, 4) => 1 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(9, 19) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _)").WithLocation(9, 19) ); CompileAndVerify(compilation, expectedOutput: "SwitchExpressionException()"); } @@ -2136,9 +2136,9 @@ public class SwitchExpressionException : InvalidOperationException "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _)' is not covered. // _ = t switch { (3, 4) => 1 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(9, 19) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _)").WithLocation(9, 19) ); CompileAndVerify(compilation, expectedOutput: "SwitchExpressionException((1, 2))"); } @@ -2178,9 +2178,9 @@ public class SwitchExpressionException : InvalidOperationException "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (8,24): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (8,24): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _)' is not covered. // _ = (1, 2) switch { (3, 4) => 1 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(8, 24) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _)").WithLocation(8, 24) ); CompileAndVerify(compilation, expectedOutput: "SwitchExpressionException((1, 2))"); } @@ -2225,9 +2225,9 @@ public SwitchExpressionException() {} "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (9,19): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _)' is not covered. // _ = r switch { (3, 4) => 1 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(9, 19) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _)").WithLocation(9, 19) ); CompileAndVerify(compilation, expectedOutput: "SwitchExpressionException()"); } @@ -2817,9 +2817,9 @@ public static void Main() "; var compilation = CreatePatternCompilation(source, options: TestOptions.ReleaseExe); compilation.VerifyDiagnostics( - // (7,32): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (7,32): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '((0, _), _)' is not covered. // Console.Write((x, 300) switch { ((1, int x2), int y) => x2+y }); - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(7, 32) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("((0, _), _)").WithLocation(7, 32) ); CompileAndVerify(compilation, expectedOutput: "320"); } @@ -2893,9 +2893,9 @@ public static void Main() "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (6,15): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (6,15): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'not null' is not covered. // _ = o switch { null => 1 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(6, 15) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("not null").WithLocation(6, 15) ); } @@ -2932,9 +2932,9 @@ public int M(bool b) "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (22,18): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (22,18): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'false' is not covered. // return b switch - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(22, 18) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("false").WithLocation(22, 18) ); CompileAndVerify(compilation, expectedOutput: "1 throw"); } @@ -3084,9 +3084,9 @@ public class SwitchExpressionException : InvalidOperationException "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (17,23): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (17,23): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _)' is not covered. // return (x, y) switch { (1, 2) => 3 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(17, 23) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _)").WithLocation(17, 23) ); CompileAndVerify(compilation, expectedOutput: @"3 SwitchExpressionException((1, 3))"); @@ -3220,9 +3220,9 @@ public class SwitchExpressionException : InvalidOperationException "; var compilation = CreatePatternCompilation(source); compilation.VerifyDiagnostics( - // (17,44): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (17,44): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '(0, _, _, _, _, _, _, _, _)' is not covered. // return (x, y, a, b, c, d, e, f, g) switch { (1, 2, _, _, _, _, _, _, _) => 3 }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(17, 44) + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("(0, _, _, _, _, _, _, _, _)").WithLocation(17, 44) ); CompileAndVerify(compilation, expectedOutput: @"3 SwitchExpressionException((1, 3, 3, 4, 5, 6, 7, 8, 9))"); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementAttributeParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementAttributeParsingTests.cs index 25f5c3137b85e..4cda7e70f87a7 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementAttributeParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementAttributeParsingTests.cs @@ -6230,9 +6230,9 @@ void Goo(int a) // (6,12): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // [A]a switch { }; Diagnostic(ErrorCode.ERR_IllegalStatement, "a switch { }").WithLocation(6, 12), - // (6,14): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). + // (6,14): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '_' is not covered. // [A]a switch { }; - Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithLocation(6, 14), + Diagnostic(ErrorCode.WRN_SwitchExpressionNotExhaustive, "switch").WithArguments("_").WithLocation(6, 14), // (6,14): error CS8506: No best type was found for the switch expression. // [A]a switch { }; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(6, 14)); diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicIntelliSense.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicIntelliSense.cs index aa4228142eeff..b39b95567d02c 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicIntelliSense.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicIntelliSense.cs @@ -140,7 +140,7 @@ End Sub assertCaretPosition: true); } - [WpfFact, Trait(Traits.Feature, Traits.Features.Completion)] + [WpfFact(Skip = "https://github.com/dotnet/roslyn/issues/45234"), Trait(Traits.Feature, Traits.Features.Completion)] public void TypeAVariableDeclaration() { SetUpEditor(@" From 35b67f8afd8c8313ad68bc155dc6b38c7bc8bd22 Mon Sep 17 00:00:00 2001 From: Kevin Sun Date: Tue, 23 Jun 2020 11:33:53 -0700 Subject: [PATCH 19/22] Binder Check for Unbound Generics in Methods (#45033) * Binder Check for Unbound Generics in Methods * Edited Tests to Match Changes * Update Binder_Expressions.cs * Restricted Error Scope, allowed other binding errors to occur * Update Binder_Expressions.cs * Modified location and error reporting * Quick PR Fixes - Part 1 of 2 * Quick PR Fixes - Part 2 of 2 Added new error, broadened error throwing to include all instances of OmittedTypeArgument in method binding. * Update GenericConstraintTests.cs * Added VB tests, extended C# tests --- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Test/Semantic/Semantics/BindingTests.cs | 150 ++++---- .../Test/Semantic/Semantics/NameOfTests.cs | 4 +- .../Semantic/Semantics/SemanticErrorTests.cs | 160 ++++++--- .../Symbol/Symbols/GenericConstraintTests.cs | 254 ++++++++++++++ .../Symbol/SymbolsTests/UnboundGenericType.vb | 325 ++++++++++++++++++ 21 files changed, 838 insertions(+), 128 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index d2cc52a1bef5d..276a331f9a7b9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -6241,9 +6241,9 @@ private BoundExpression BindInstanceMemberAccess( lookupResult, flags); - if (!boundMethodGroup.HasErrors && boundMethodGroup.ResultKind == LookupResultKind.Empty && typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) + if (!boundMethodGroup.HasErrors && typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) { - Error(diagnostics, ErrorCode.ERR_BadArity, node, rightName, MessageID.IDS_MethodGroup.Localize(), typeArgumentsSyntax.Count); + Error(diagnostics, ErrorCode.ERR_OmittedTypeArgument, node); } return boundMethodGroup; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 6bf06fd5cde2f..00b6d833826b8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -471,6 +471,9 @@ Keyword 'this' is not available in the current context + + Omitting the type argument is not allowed in the current context + '{0}' has the wrong signature to be an entry point diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 43ce114a6e3fa..eaf44f30d12dc 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1581,6 +1581,7 @@ internal enum ErrorCode WRN_TypeParameterSameAsOuterMethodTypeParameter = 8387, ERR_OutVariableCannotBeByRef = 8388, + ERR_OmittedTypeArgument = 8389, #region diagnostics introduced for C# 8.0 ERR_FeatureNotAvailableInVersion8 = 8400, diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 11321d0f1f607..aec3bace55ded 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -537,6 +537,11 @@ Parametr typu s možnou hodnotou null musí být známou hodnotou nebo musí jít o typ odkazu, který nemůže mít hodnotu null. Zvažte přidání 'class', 'struct' nebo omezení typu. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Výstupní proměnná nemůže být deklarovaná jako lokální proměnná podle odkazu. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 06512493acfa0..562cd01b51be1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -537,6 +537,11 @@ Ein Nullable-Typparameter muss als Werttyp oder als Non-Nullable-Verweistyp bekannt sein. Sie sollten eine class-, struct- oder eine Typeinschränkung hinzufügen. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Eine out-Variable kann nicht als lokales ref-Element deklariert werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index d951fffe51264..0575ff86b014a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -537,6 +537,11 @@ Debe saberse si un parámetro de tipo que acepta valores NULL es un tipo de valor o un tipo de referencia que no admite valores NULL. Considere agregar "class", "struct" o una restricción de tipo. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Una variable out no se puede declarar como ref local diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index be5d17fe01552..de461331c35ed 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -537,6 +537,11 @@ Un paramètre de type nullable doit être connu pour pouvoir être un type valeur ou un type référence non-nullable. Ajoutez une contrainte 'class', 'struct' ou une contrainte de type. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Impossible de déclarer une variable out en tant que variable locale ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 0c39aea3033d4..67a09daff5b16 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -537,6 +537,11 @@ Un parametro di tipo che ammette i valori Null deve essere noto per essere un tipo valore o un tipo riferimento che non ammette i valori Null. Provare ad aggiungere un vincolo di tipo, 'class' o 'struct'. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Non è possibile dichiarare una variabile out come variabile locale ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 83ca6b556423b..fae66adc636f4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -537,6 +537,11 @@ Null 許容型パラメーターは、値の型または Null 非許容参照型として既知である必要があります。'class'、'struct'、型制約を追加することをご検討ください。 + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local out 変数を ref ローカルと宣言することはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 7c64d29d60080..4d7001e452953 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -537,6 +537,11 @@ 값 형식 또는 null을 허용하지 않는 참조 형식이 되려면 nullable 형식 매개 변수를 알려야 합니다. 'class', 'struct' 또는 형식 제약 조건 추가를 고려하세요. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local 출력 변수는 참조 로컬로 선언할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 025b4b94771d1..5deb92cbbada3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -537,6 +537,11 @@ Nullowalny parametr typumusi być typem wartości lub nienullowalnym typem referencyjnym. Rozważ dodanie elementu „class”, „struct” lub ograniczenia typu. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Zmiennej out nie można zadeklarować jako lokalnej zmiennej ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index a66efba099ab8..ecd5b7330c54d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -537,6 +537,11 @@ Um parâmetro de tipo que permite valor nulo precisa ser um tipo de valor ou um tipo de referência não anulável. Considere a possibilidade de adicionar 'class', 'struct' ou restrição de tipo. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Uma variável out não pode ser declarada como uma referência local diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index f77f0fa45eb4e..16a6eb0a263b2 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -537,6 +537,11 @@ Параметр типа, допускающего значение NULL, должен быть известен как тип значения или ссылочный тип, не допускающий значение NULL. Рекомендуется добавить "class", "struct" или ограничение типа. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Выходная переменная не может быть объявлена как локальная переменная ref diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 98644e909656f..21832f0499aaa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -537,6 +537,11 @@ Boş değer atanabilir bir tür parametresi bilinen bir değer türü veya boş değer atanamayan bir başvuru türü olmalıdır. Bir 'class', 'struct' veya tür kısıtlaması eklemeyi düşünün. + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local Bir out değişkeni ref yerel değeri olarak bildirilemez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 033f7b228d986..1a25e509ecccc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -537,6 +537,11 @@ 可为 null 的类型参数必须已知为值类型或不可为 null 引用类型。请考虑添加一个 “class”、“struct” 或类型约束。 + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local out 变量无法声明为 ref 局部变量 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 04fc47746bd1a..b4c911a3ad2e8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -537,6 +537,11 @@ 可為 Null 的型別參數必須已知為實值型別或不可為 Null 的參考型別。建議新增 'class'、'struct' 或型別條件約束。 + + Omitting the type argument is not allowed in the current context + Omitting the type argument is not allowed in the current context + + An out variable cannot be declared as a ref local out 變數不可宣告為 ref local diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs index 367e5b24d1d6b..2f33afac51fba 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingTests.cs @@ -3516,81 +3516,81 @@ public void Test() var compilation = CreateCompilationWithMscorlib40AndSystemCore(source); compilation.VerifyDiagnostics( - // (13,27): error CS0305: Using the generic method group 'ExtensionMethod0' requires 1 type arguments - // var omittedArg0 = "string literal".ExtensionMethod0<>(); - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod0<>").WithArguments("ExtensionMethod0", "method group", "1").WithLocation(13, 27), - // (13,44): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var omittedArg0 = "string literal".ExtensionMethod0<>(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0<>").WithArguments("string", "ExtensionMethod0").WithLocation(13, 44), - // (14,27): error CS0305: Using the generic method group 'ExtensionMethod1' requires 1 type arguments - // var omittedArg1 = "string literal".ExtensionMethod1<>(); - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod1<>").WithArguments("ExtensionMethod1", "method group", "1").WithLocation(14, 27), - // (15,27): error CS0305: Using the generic method group 'ExtensionMethod2' requires 1 type arguments - // var omittedArg2 = "string literal".ExtensionMethod2<>(); - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod2<>").WithArguments("ExtensionMethod2", "method group", "1").WithLocation(15, 27), - // (15,44): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var omittedArg2 = "string literal".ExtensionMethod2<>(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(15, 44), - // (17,31): error CS0305: Using the generic method group 'ExtensionMethod0' requires 1 type arguments - // var omittedArgFunc0 = "string literal".ExtensionMethod0<>; - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod0<>").WithArguments("ExtensionMethod0", "method group", "1").WithLocation(17, 31), - // (17,48): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var omittedArgFunc0 = "string literal".ExtensionMethod0<>; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0<>").WithArguments("string", "ExtensionMethod0").WithLocation(17, 48), - // (18,31): error CS0305: Using the generic method group 'ExtensionMethod1' requires 1 type arguments - // var omittedArgFunc1 = "string literal".ExtensionMethod1<>; - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod1<>").WithArguments("ExtensionMethod1", "method group", "1").WithLocation(18, 31), - // (18,13): error CS0815: Cannot assign method group to an implicitly-typed variable - // var omittedArgFunc1 = "string literal".ExtensionMethod1<>; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, @"omittedArgFunc1 = ""string literal"".ExtensionMethod1<>").WithArguments("method group").WithLocation(18, 13), - // (19,31): error CS0305: Using the generic method group 'ExtensionMethod2' requires 1 type arguments - // var omittedArgFunc2 = "string literal".ExtensionMethod2<>; - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod2<>").WithArguments("ExtensionMethod2", "method group", "1").WithLocation(19, 31), - // (19,48): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var omittedArgFunc2 = "string literal".ExtensionMethod2<>; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(19, 48), - // (21,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var moreArgs0 = "string literal".ExtensionMethod0(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0").WithArguments("string", "ExtensionMethod0").WithLocation(21, 42), - // (22,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod1' and no extension method 'ExtensionMethod1' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var moreArgs1 = "string literal".ExtensionMethod1(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod1").WithArguments("string", "ExtensionMethod1").WithLocation(22, 42), - // (23,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var moreArgs2 = "string literal".ExtensionMethod2(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2").WithArguments("string", "ExtensionMethod2").WithLocation(23, 42), - // (25,42): error CS0411: The type arguments for method 'FooExtensions.ExtensionMethod1(object)' cannot be inferred from the usage. Try specifying the type arguments explicitly. - // var lessArgs1 = "string literal".ExtensionMethod1(); - Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "ExtensionMethod1").WithArguments("FooExtensions.ExtensionMethod1(object)").WithLocation(25, 42), - // (26,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var lessArgs2 = "string literal".ExtensionMethod2(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2").WithArguments("string", "ExtensionMethod2").WithLocation(26, 42), - // (28,51): error CS1061: 'string' does not contain a definition for 'ExtensionMethodNotFound0' and no extension method 'ExtensionMethodNotFound0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var nonExistingMethod0 = "string literal".ExtensionMethodNotFound0(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethodNotFound0").WithArguments("string", "ExtensionMethodNotFound0").WithLocation(28, 51), - // (29,51): error CS1061: 'string' does not contain a definition for 'ExtensionMethodNotFound1' and no extension method 'ExtensionMethodNotFound1' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var nonExistingMethod1 = "string literal".ExtensionMethodNotFound1(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethodNotFound1").WithArguments("string", "ExtensionMethodNotFound1").WithLocation(29, 51), - // (30,51): error CS1061: 'string' does not contain a definition for 'ExtensionMethodNotFound2' and no extension method 'ExtensionMethodNotFound2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // var nonExistingMethod2 = "string literal".ExtensionMethodNotFound2(); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethodNotFound2").WithArguments("string", "ExtensionMethodNotFound2").WithLocation(30, 51), - // (32,51): error CS0305: Using the generic method group 'ExtensionMethod0' requires 1 type arguments - // System.Func delegateConversion0 = "string literal".ExtensionMethod0<>; - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod0<>").WithArguments("ExtensionMethod0", "method group", "1").WithLocation(32, 51), - // (32,68): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // System.Func delegateConversion0 = "string literal".ExtensionMethod0<>; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0<>").WithArguments("string", "ExtensionMethod0").WithLocation(32, 68), - // (33,51): error CS0305: Using the generic method group 'ExtensionMethod1' requires 1 type arguments - // System.Func delegateConversion1 = "string literal".ExtensionMethod1<>; - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod1<>").WithArguments("ExtensionMethod1", "method group", "1").WithLocation(33, 51), - // (33,51): error CS0407: '? FooExtensions.ExtensionMethod1(object)' has the wrong return type - // System.Func delegateConversion1 = "string literal".ExtensionMethod1<>; - Diagnostic(ErrorCode.ERR_BadRetType, @"""string literal"".ExtensionMethod1<>").WithArguments("FooExtensions.ExtensionMethod1(object)", "?").WithLocation(33, 51), - // (34,51): error CS0305: Using the generic method group 'ExtensionMethod2' requires 1 type arguments - // System.Func delegateConversion2 = "string literal".ExtensionMethod2<>; - Diagnostic(ErrorCode.ERR_BadArity, @"""string literal"".ExtensionMethod2<>").WithArguments("ExtensionMethod2", "method group", "1").WithLocation(34, 51), - // (34,68): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) - // System.Func delegateConversion2 = "string literal".ExtensionMethod2<>; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(34, 68)); + // (13,27): error CS8389: Omitting the type argument is not allowed in the current context + // var omittedArg0 = "string literal".ExtensionMethod0<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod0<>").WithLocation(13, 27), + // (13,44): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no accessible extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var omittedArg0 = "string literal".ExtensionMethod0<>(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0<>").WithArguments("string", "ExtensionMethod0").WithLocation(13, 44), + // (14,27): error CS8389: Omitting the type argument is not allowed in the current context + // var omittedArg1 = "string literal".ExtensionMethod1<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod1<>").WithLocation(14, 27), + // (15,27): error CS8389: Omitting the type argument is not allowed in the current context + // var omittedArg2 = "string literal".ExtensionMethod2<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod2<>").WithLocation(15, 27), + // (15,44): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no accessible extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var omittedArg2 = "string literal".ExtensionMethod2<>(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(15, 44), + // (17,31): error CS8389: Omitting the type argument is not allowed in the current context + // var omittedArgFunc0 = "string literal".ExtensionMethod0<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod0<>").WithLocation(17, 31), + // (17,48): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no accessible extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var omittedArgFunc0 = "string literal".ExtensionMethod0<>; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0<>").WithArguments("string", "ExtensionMethod0").WithLocation(17, 48), + // (18,31): error CS8389: Omitting the type argument is not allowed in the current context + // var omittedArgFunc1 = "string literal".ExtensionMethod1<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod1<>").WithLocation(18, 31), + // (18,13): error CS0815: Cannot assign method group to an implicitly-typed variable + // var omittedArgFunc1 = "string literal".ExtensionMethod1<>; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, @"omittedArgFunc1 = ""string literal"".ExtensionMethod1<>").WithArguments("method group").WithLocation(18, 13), + // (19,31): error CS8389: Omitting the type argument is not allowed in the current context + // var omittedArgFunc2 = "string literal".ExtensionMethod2<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod2<>").WithLocation(19, 31), + // (19,48): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no accessible extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var omittedArgFunc2 = "string literal".ExtensionMethod2<>; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(19, 48), + // (21,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no accessible extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var moreArgs0 = "string literal".ExtensionMethod0(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0").WithArguments("string", "ExtensionMethod0").WithLocation(21, 42), + // (22,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod1' and no accessible extension method 'ExtensionMethod1' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var moreArgs1 = "string literal".ExtensionMethod1(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod1").WithArguments("string", "ExtensionMethod1").WithLocation(22, 42), + // (23,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no accessible extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var moreArgs2 = "string literal".ExtensionMethod2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2").WithArguments("string", "ExtensionMethod2").WithLocation(23, 42), + // (25,42): error CS0411: The type arguments for method 'FooExtensions.ExtensionMethod1(object)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // var lessArgs1 = "string literal".ExtensionMethod1(); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "ExtensionMethod1").WithArguments("FooExtensions.ExtensionMethod1(object)").WithLocation(25, 42), + // (26,42): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no accessible extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var lessArgs2 = "string literal".ExtensionMethod2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2").WithArguments("string", "ExtensionMethod2").WithLocation(26, 42), + // (28,51): error CS1061: 'string' does not contain a definition for 'ExtensionMethodNotFound0' and no accessible extension method 'ExtensionMethodNotFound0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var nonExistingMethod0 = "string literal".ExtensionMethodNotFound0(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethodNotFound0").WithArguments("string", "ExtensionMethodNotFound0").WithLocation(28, 51), + // (29,51): error CS1061: 'string' does not contain a definition for 'ExtensionMethodNotFound1' and no accessible extension method 'ExtensionMethodNotFound1' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var nonExistingMethod1 = "string literal".ExtensionMethodNotFound1(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethodNotFound1").WithArguments("string", "ExtensionMethodNotFound1").WithLocation(29, 51), + // (30,51): error CS1061: 'string' does not contain a definition for 'ExtensionMethodNotFound2' and no accessible extension method 'ExtensionMethodNotFound2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // var nonExistingMethod2 = "string literal".ExtensionMethodNotFound2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethodNotFound2").WithArguments("string", "ExtensionMethodNotFound2").WithLocation(30, 51), + // (32,51): error CS8389: Omitting the type argument is not allowed in the current context + // System.Func delegateConversion0 = "string literal".ExtensionMethod0<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod0<>").WithLocation(32, 51), + // (32,68): error CS1061: 'string' does not contain a definition for 'ExtensionMethod0' and no accessible extension method 'ExtensionMethod0' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // System.Func delegateConversion0 = "string literal".ExtensionMethod0<>; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod0<>").WithArguments("string", "ExtensionMethod0").WithLocation(32, 68), + // (33,51): error CS8389: Omitting the type argument is not allowed in the current context + // System.Func delegateConversion1 = "string literal".ExtensionMethod1<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod1<>").WithLocation(33, 51), + // (33,51): error CS0407: '? FooExtensions.ExtensionMethod1(object)' has the wrong return type + // System.Func delegateConversion1 = "string literal".ExtensionMethod1<>; + Diagnostic(ErrorCode.ERR_BadRetType, @"""string literal"".ExtensionMethod1<>").WithArguments("FooExtensions.ExtensionMethod1(object)", "?").WithLocation(33, 51), + // (34,51): error CS8389: Omitting the type argument is not allowed in the current context + // System.Func delegateConversion2 = "string literal".ExtensionMethod2<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, @"""string literal"".ExtensionMethod2<>").WithLocation(34, 51), + // (34,68): error CS1061: 'string' does not contain a definition for 'ExtensionMethod2' and no accessible extension method 'ExtensionMethod2' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // System.Func delegateConversion2 = "string literal".ExtensionMethod2<>; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "ExtensionMethod2<>").WithArguments("string", "ExtensionMethod2").WithLocation(34, 68)); } [WorkItem(22757, "https://github.com/dotnet/roslyn/issues/22757")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index cd828e08d82c6..b81f7d7e7f04f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -295,7 +295,7 @@ class Test // (28,20): error CS0103: The name 'List2' does not exist in the current context // s = nameof(List2<>.Add); Diagnostic(ErrorCode.ERR_NameNotInContext, "List2<>").WithArguments("List2").WithLocation(28, 20), - // (31,20): error CS8149: An alias-qualified name is not an expression. + // (31,20): error CS8083: An alias-qualified name is not an expression. // s = nameof(global::Program); // not an expression Diagnostic(ErrorCode.ERR_AliasQualifiedNameNotAnExpression, "global::Program").WithLocation(31, 20), // (32,20): error CS0305: Using the generic type 'Test' requires 1 type arguments @@ -307,7 +307,7 @@ class Test // (33,20): error CS0841: Cannot use local variable 'b' before it is declared // s = nameof(b); // cannot use before declaration Diagnostic(ErrorCode.ERR_VariableUsedBeforeDeclaration, "b").WithArguments("b").WithLocation(33, 20), - // (35,20): error CS8150: Type parameters are not allowed on a method group as an argument to 'nameof'. + // (35,20): error CS8084: Type parameters are not allowed on a method group as an argument to 'nameof'. // s = nameof(System.Linq.Enumerable.Select); // type parameters not allowed on method group in nameof Diagnostic(ErrorCode.ERR_NameofMethodGroupWithTypeParameters, "System.Linq.Enumerable.Select").WithLocation(35, 20), // (43,13): error CS0103: The name 'nameof' does not exist in the current context diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs index a2778d5abe676..ae559e9ecf2e8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticErrorTests.cs @@ -22799,76 +22799,123 @@ void Test(object p) "; CreateCompilation(text).VerifyDiagnostics( // (16,14): error CS0307: The variable 'l' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "l<>").WithArguments("l", "variable"), + // Test(l<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "l<>").WithArguments("l", "variable").WithLocation(16, 14), // (17,14): error CS0307: The variable 'object' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "p<>").WithArguments("object", "variable"), + // Test(p<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "p<>").WithArguments("object", "variable").WithLocation(17, 14), // (19,14): error CS0307: The field 'Program.f' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "f<>").WithArguments("Program.f", "field"), + // Test(f<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "f<>").WithArguments("Program.f", "field").WithLocation(19, 14), // (20,14): error CS0307: The property 'Program.P' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "P<>").WithArguments("Program.P", "property"), + // Test(P<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "P<>").WithArguments("Program.P", "property").WithLocation(20, 14), // (21,14): error CS0308: The non-generic method 'Program.M()' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method"), + // Test(M<>()); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method").WithLocation(21, 14), + // (23,14): error CS8389: Omitting the type argument is not allowed in the current context + // Test(this.f<>); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "this.f<>").WithLocation(23, 14), // (23,19): error CS0307: The field 'Program.f' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "f<>").WithArguments("Program.f", "field"), + // Test(this.f<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "f<>").WithArguments("Program.f", "field").WithLocation(23, 19), + // (24,14): error CS8389: Omitting the type argument is not allowed in the current context + // Test(this.P<>); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "this.P<>").WithLocation(24, 14), // (24,19): error CS0307: The property 'Program.P' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "P<>").WithArguments("Program.P", "property"), + // Test(this.P<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "P<>").WithArguments("Program.P", "property").WithLocation(24, 19), + // (25,14): error CS8389: Omitting the type argument is not allowed in the current context + // Test(this.M<>()); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "this.M<>").WithLocation(25, 14), // (25,19): error CS0308: The non-generic method 'Program.M()' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method"), + // Test(this.M<>()); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method").WithLocation(25, 19), // (29,13): error CS0308: The non-generic method 'Program.M()' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method"), + // m = M<>; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method").WithLocation(29, 13), + // (30,13): error CS8389: Omitting the type argument is not allowed in the current context + // m = this.M<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "this.M<>").WithLocation(30, 13), // (30,18): error CS0308: The non-generic method 'Program.M()' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method"), + // m = this.M<>; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "M<>").WithArguments("Program.M()", "method").WithLocation(30, 18), // (32,9): error CS0308: The non-generic type 'Program.I' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type"), + // I<> i1 = null; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type").WithLocation(32, 9), // (33,9): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // C<> c1 = new C(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(33, 9), // (34,20): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // C c2 = new C<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(34, 20), // (35,9): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // S<> s1 = new S(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(35, 9), // (36,20): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // S s2 = new S<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(36, 20), // (37,9): error CS0308: The non-generic type 'Program.D' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "D<>").WithArguments("Program.D", "type"), + // D<> d1 = null; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "D<>").WithArguments("Program.D", "type").WithLocation(37, 9), // (39,17): error CS0308: The non-generic type 'Program.I' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type"), + // Program.I<> i2 = null; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type").WithLocation(39, 17), // (40,17): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // Program.C<> c3 = new Program.C(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(40, 17), // (41,36): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // Program.C c4 = new Program.C<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(41, 36), // (42,17): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // Program.S<> s3 = new Program.S(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(42, 17), // (43,36): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // Program.S s4 = new Program.S<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(43, 36), // (44,17): error CS0308: The non-generic type 'Program.D' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "D<>").WithArguments("Program.D", "type"), + // Program.D<> d2 = null; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "D<>").WithArguments("Program.D", "type").WithLocation(44, 17), // (46,22): error CS0308: The non-generic type 'Program.I' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type"), + // Test(default(I<>)); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type").WithLocation(46, 22), // (47,22): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // Test(default(C<>)); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(47, 22), // (48,22): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // Test(default(S<>)); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(48, 22), // (50,30): error CS0308: The non-generic type 'Program.I' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type"), + // Test(default(Program.I<>)); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type").WithLocation(50, 30), // (51,30): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // Test(default(Program.C<>)); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(51, 30), // (52,30): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // Test(default(Program.S<>)); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(52, 30), // (56,20): error CS0308: The non-generic type 'Program.I' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type"), + // s = typeof(I<>).Name; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type").WithLocation(56, 20), // (57,20): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // s = typeof(C<>).Name; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(57, 20), // (58,20): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // s = typeof(S<>).Name; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(58, 20), // (60,28): error CS0308: The non-generic type 'Program.I' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type"), + // s = typeof(Program.I<>).Name; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "I<>").WithArguments("Program.I", "type").WithLocation(60, 28), // (61,28): error CS0308: The non-generic type 'Program.C' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type"), + // s = typeof(Program.C<>).Name; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "C<>").WithArguments("Program.C", "type").WithLocation(61, 28), // (62,28): error CS0308: The non-generic type 'Program.S' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type"), + // s = typeof(Program.S<>).Name; + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "S<>").WithArguments("Program.S", "type").WithLocation(62, 28), // (4,9): warning CS0649: Field 'Program.f' is never assigned to, and will always have its default value 0 - Diagnostic(ErrorCode.WRN_UnassignedInternalField, "f").WithArguments("Program.f", "0") - ); + // int f; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "f").WithArguments("Program.f", "0").WithLocation(4, 9) + ); } [WorkItem(542419, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542419")] @@ -22898,32 +22945,47 @@ void Test(T p) // Parser // (12,11): error CS1525: Invalid expression term '>' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">"), + // E<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">").WithLocation(12, 11), // (12,13): error CS1525: Invalid expression term '+=' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+="), + // E<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+=").WithLocation(12, 13), // (13,11): error CS1525: Invalid expression term '>' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">"), + // F<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">").WithLocation(13, 11), // (13,13): error CS1525: Invalid expression term '+=' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+="), + // F<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+=").WithLocation(13, 13), // (15,16): error CS1525: Invalid expression term '>' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">"), + // this.E<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">").WithLocation(15, 16), // (15,18): error CS1525: Invalid expression term '+=' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+="), + // this.E<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+=").WithLocation(15, 18), // (16,16): error CS1525: Invalid expression term '>' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">"), + // this.F<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ">").WithArguments(">").WithLocation(16, 16), + // (16,18): error CS1525: Invalid expression term '+=' + // this.F<> += null; //parse error + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+=").WithLocation(16, 18), // Binder - // (16,18): error CS1525: Invalid expression term '+=' - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "+=").WithArguments("+="), // (9,14): error CS0307: The event 'Program.E' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "E<>").WithArguments("Program.E", "event"), + // Test(E<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "E<>").WithArguments("Program.E", "event").WithLocation(9, 14), + // (10,14): error CS8389: Omitting the type argument is not allowed in the current context + // Test(this.E<>); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "this.E<>").WithLocation(10, 14), // (10,19): error CS0307: The event 'Program.E' cannot be used with type arguments - Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "E<>").WithArguments("Program.E", "event"), + // Test(this.E<>); + Diagnostic(ErrorCode.ERR_TypeArgsNotAllowed, "E<>").WithArguments("Program.E", "event").WithLocation(10, 19), // (13,9): error CS0079: The event 'Program.F' can only appear on the left hand side of += or -= - Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "F").WithArguments("Program.F"), + // F<> += null; //parse error + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "F").WithArguments("Program.F").WithLocation(13, 9), // (16,14): error CS0079: The event 'Program.F' can only appear on the left hand side of += or -= - Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "F").WithArguments("Program.F")); + // this.F<> += null; //parse error + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "F").WithArguments("Program.F").WithLocation(16, 14)); } [WorkItem(542419, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542419")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/GenericConstraintTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/GenericConstraintTests.cs index 5bb735d504ec4..4cc6dd6043220 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/GenericConstraintTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/GenericConstraintTests.cs @@ -6870,5 +6870,259 @@ static void Main(string[] args) Diagnostic(ErrorCode.ERR_BadTypeArgument, "R2").WithArguments("int*").WithLocation(6, 7) ); } + + [Fact] + [WorkItem(41779, "https://github.com/dotnet/roslyn/issues/41779")] + public void Bug41779_Original() + { + var source = +@"interface I +{ + object GetService(); +} + +static class Program +{ + static T GetService(this I obj) => default; + + static void M(I provider) + { + provider.GetService<>(); + provider.GetService<>().ToString(); + provider.GetService<>(); + } +}"; + CreateCompilation(source).VerifyDiagnostics( + // (12,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(12, 9), + // (13,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(13, 9), + // (14,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(14, 9) + ); + } + + [Fact] + [WorkItem(41779, "https://github.com/dotnet/roslyn/issues/41779")] + public void Bug41779_DoubleTypeArg() + { + var source = +@"interface I +{ + object GetService(); +} + +static class Program +{ + static T1 GetService(this I obj) => default; + + static void M(I provider) + { + provider.GetService<>(); + provider.GetService<>().ToString(); + provider.GetService<>(); + } +}"; + CreateCompilation(source).VerifyDiagnostics( + // (12,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(12, 9), + // (12,18): error CS0308: The non-generic method 'I.GetService()' cannot be used with type arguments + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetService<>").WithArguments("I.GetService()", "method").WithLocation(12, 18), + // (13,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(13, 9), + // (13,18): error CS0308: The non-generic method 'I.GetService()' cannot be used with type arguments + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetService<>").WithArguments("I.GetService()", "method").WithLocation(13, 18), + // (14,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(14, 9), + // (14,18): error CS0308: The non-generic method 'I.GetService()' cannot be used with type arguments + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetService<>").WithArguments("I.GetService()", "method").WithLocation(14, 18) + ); + } + + [Fact] + [WorkItem(41779, "https://github.com/dotnet/roslyn/issues/41779")] + public void Bug41779_Instance() + { + var source = +@"interface I +{ + object GetService(); +} + +interface J +{ + object GetService(); +} + +interface K +{ + object GetService(); +} + +static class Program +{ + static void M(I provider) + { + provider.GetService<>(); + provider.GetService<>().ToString(); + } + + static void M(J provider) + { + provider.GetService<>(); + provider.GetService<>().ToString(); + } + + static void M(K provider) + { + provider.GetService<>(); + provider.GetService<>().ToString(); + } +}"; + CreateCompilation(source).VerifyDiagnostics( + // (20,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(20, 9), + // (20,18): error CS0308: The non-generic method 'I.GetService()' cannot be used with type arguments + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetService<>").WithArguments("I.GetService()", "method").WithLocation(20, 18), + // (21,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(21, 9), + // (21,18): error CS0308: The non-generic method 'I.GetService()' cannot be used with type arguments + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetService<>").WithArguments("I.GetService()", "method").WithLocation(21, 18), + // (26,9): error CS0305: Using the generic method group 'GetService' requires 1 type arguments + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_BadArity, "provider.GetService<>").WithArguments("GetService", "method group", "1").WithLocation(26, 9), + // (27,9): error CS0305: Using the generic method group 'GetService' requires 1 type arguments + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_BadArity, "provider.GetService<>").WithArguments("GetService", "method group", "1").WithLocation(27, 9), + // (32,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(32, 9), + // (32,18): error CS0305: Using the generic method 'K.GetService()' requires 2 type arguments + // provider.GetService<>(); + Diagnostic(ErrorCode.ERR_BadArity, "GetService<>").WithArguments("K.GetService()", "method", "2").WithLocation(32, 18), + // (33,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetService<>").WithLocation(33, 9), + // (33,18): error CS0305: Using the generic method 'K.GetService()' requires 2 type arguments + // provider.GetService<>().ToString(); + Diagnostic(ErrorCode.ERR_BadArity, "GetService<>").WithArguments("K.GetService()", "method", "2").WithLocation(33, 18) + ); + } + + + [Fact] + [WorkItem(41779, "https://github.com/dotnet/roslyn/issues/41779")] + public void Bug41779_Extension() + { + var source = +@"interface I{} + +static class Program +{ + static void GetServiceA(this I obj){} + static T GetServiceB(this I obj) => default; + static T1 GetServiceC(this I obj) => default; + + static void M(I provider) + { + provider.GetServiceA<>(); + provider.GetServiceA<>().ToString(); + + provider.GetServiceB<>(); + provider.GetServiceB<>().ToString(); + + provider.GetServiceC<>(); + provider.GetServiceC<>().ToString(); + } +}"; + CreateCompilation(source).VerifyDiagnostics( + // (11,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetServiceA<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetServiceA<>").WithLocation(11, 9), + // (11,18): error CS1061: 'I' does not contain a definition for 'GetServiceA' and no accessible extension method 'GetServiceA' accepting a first argument of type 'I' could be found (are you missing a using directive or an assembly reference?) + // provider.GetServiceA<>(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "GetServiceA<>").WithArguments("I", "GetServiceA").WithLocation(11, 18), + // (12,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetServiceA<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetServiceA<>").WithLocation(12, 9), + // (12,18): error CS1061: 'I' does not contain a definition for 'GetServiceA' and no accessible extension method 'GetServiceA' accepting a first argument of type 'I' could be found (are you missing a using directive or an assembly reference?) + // provider.GetServiceA<>().ToString(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "GetServiceA<>").WithArguments("I", "GetServiceA").WithLocation(12, 18), + // (14,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetServiceB<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetServiceB<>").WithLocation(14, 9), + // (15,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetServiceB<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetServiceB<>").WithLocation(15, 9), + // (17,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetServiceC<>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetServiceC<>").WithLocation(17, 9), + // (17,18): error CS1061: 'I' does not contain a definition for 'GetServiceC' and no accessible extension method 'GetServiceC' accepting a first argument of type 'I' could be found (are you missing a using directive or an assembly reference?) + // provider.GetServiceC<>(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "GetServiceC<>").WithArguments("I", "GetServiceC").WithLocation(17, 18), + // (18,9): error CS8389: Omitting the type argument is not allowed in the current context + // provider.GetServiceC<>().ToString(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "provider.GetServiceC<>").WithLocation(18, 9), + // (18,18): error CS1061: 'I' does not contain a definition for 'GetServiceC' and no accessible extension method 'GetServiceC' accepting a first argument of type 'I' could be found (are you missing a using directive or an assembly reference?) + // provider.GetServiceC<>().ToString(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "GetServiceC<>").WithArguments("I", "GetServiceC").WithLocation(18, 18) + ); + } + + [Fact] + [WorkItem(41779, "https://github.com/dotnet/roslyn/issues/41779")] + public void Bug41779_Static() + { + var source = +@"static class Program +{ + static object GetServiceA(){ return null; } + static T GetServiceB() => default; + static T1 GetServiceC() => default; + + static void M() + { + GetServiceA<>(); + GetServiceA<>().ToString(); + GetServiceB<>(); + GetServiceB<>().ToString(); + GetServiceC<>(); + GetServiceC<>().ToString(); + } +}"; + CreateCompilation(source).VerifyDiagnostics( + // (9,9): error CS0308: The non-generic method 'Program.GetServiceA()' cannot be used with type arguments + // GetServiceA<>(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetServiceA<>").WithArguments("Program.GetServiceA()", "method").WithLocation(9, 9), + // (10,9): error CS0308: The non-generic method 'Program.GetServiceA()' cannot be used with type arguments + // GetServiceA<>().ToString(); + Diagnostic(ErrorCode.ERR_HasNoTypeVars, "GetServiceA<>").WithArguments("Program.GetServiceA()", "method").WithLocation(10, 9), + // (11,9): error CS0305: Using the generic method group 'GetServiceB' requires 1 type arguments + // GetServiceB<>(); + Diagnostic(ErrorCode.ERR_BadArity, "GetServiceB<>").WithArguments("GetServiceB", "method group", "1").WithLocation(11, 9), + // (12,9): error CS0305: Using the generic method group 'GetServiceB' requires 1 type arguments + // GetServiceB<>().ToString(); + Diagnostic(ErrorCode.ERR_BadArity, "GetServiceB<>").WithArguments("GetServiceB", "method group", "1").WithLocation(12, 9), + // (13,9): error CS0305: Using the generic method 'Program.GetServiceC()' requires 2 type arguments + // GetServiceC<>(); + Diagnostic(ErrorCode.ERR_BadArity, "GetServiceC<>").WithArguments("Program.GetServiceC()", "method", "2").WithLocation(13, 9), + // (14,9): error CS0305: Using the generic method 'Program.GetServiceC()' requires 2 type arguments + // GetServiceC<>().ToString(); + Diagnostic(ErrorCode.ERR_BadArity, "GetServiceC<>").WithArguments("Program.GetServiceC()", "method", "2").WithLocation(14, 9) + ); + } } } diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnboundGenericType.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnboundGenericType.vb index 7e1667093c2db..871a53255c9d3 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnboundGenericType.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/UnboundGenericType.vb @@ -289,6 +289,331 @@ End Class Assert.True(DirectCast(c3c6s, INamedTypeSymbol).IsSerializable) End Sub + + + Public Sub UnboundGenericType_Bug41779_Original() + + Dim compilation = CompilationUtils.CreateCompilation( + + + Private Function GetService(Of T)(ByVal obj As I) As T + Return "default" + End Function + + Private Sub M(ByVal provider As I) + provider.GetService(Of)() + provider.GetService(Of)().ToString() + provider.GetService(Of)() + End Sub +End Module + ]]> +) + + compilation.AssertTheseDiagnostics( +BC30311: Value of type 'String' cannot be converted to 'T'. + Return "default" + ~~~~~~~~~ +BC30182: Type expected. + provider.GetService(Of)() + ~ +BC30182: Type expected. + provider.GetService(Of)().ToString() + ~ +BC30182: Type expected. + provider.GetService(Of)() + ~ + ) + + End Sub + + + + Public Sub UnboundGenericType_Bug41779_DoubleArgs() + + Dim compilation = CompilationUtils.CreateCompilation( + + + Private Function GetService(Of T1, T2)(ByVal obj As I) As T1 + Return "default" + End Function + + Private Sub M(ByVal provider As I) + provider.GetService(Of)() + provider.GetService(Of)().ToString() + provider.GetService(Of)() + End Sub +End Module + ]]> +) + + compilation.AssertTheseDiagnostics( +BC30311: Value of type 'String' cannot be converted to 'T1'. + Return "default" + ~~~~~~~~~ +BC32087: Overload resolution failed because no accessible 'GetService' accepts this number of type arguments. + provider.GetService(Of)() + ~~~~~~~~~~~~~~ +BC30182: Type expected. + provider.GetService(Of)() + ~ +BC32087: Overload resolution failed because no accessible 'GetService' accepts this number of type arguments. + provider.GetService(Of)().ToString() + ~~~~~~~~~~~~~~ +BC30182: Type expected. + provider.GetService(Of)().ToString() + ~ +BC32087: Overload resolution failed because no accessible 'GetService' accepts this number of type arguments. + provider.GetService(Of)() + ~~~~~~~~~~~~~~ +BC30182: Type expected. + provider.GetService(Of)() + ~ + ) + + End Sub + + + + Public Sub UnboundGenericType_Bug41779_Instance() + + Dim compilation = CompilationUtils.CreateCompilation( + + +) + + compilation.AssertTheseDiagnostics( +BC32045: 'Function GetService() As Object' has no type parameters and so cannot have type arguments. + provider.GetService(Of)() + ~~~~ +BC30182: Type expected. + provider.GetService(Of)() + ~ +BC32045: 'Function GetService() As Object' has no type parameters and so cannot have type arguments. + provider.GetService(Of)().ToString() + ~~~~ +BC30182: Type expected. + provider.GetService(Of)().ToString() + ~ +BC30182: Type expected. + provider.GetService(Of)() + ~ +BC30182: Type expected. + provider.GetService(Of)().ToString() + ~ +BC32042: Too few type arguments to 'Function GetService(Of T1, T2)() As Object'. + provider.GetService(Of)() + ~~~~ +BC30182: Type expected. + provider.GetService(Of)() + ~ +BC32042: Too few type arguments to 'Function GetService(Of T1, T2)() As Object'. + provider.GetService(Of)().ToString() + ~~~~ +BC30182: Type expected. + provider.GetService(Of)().ToString() + ~ + ) + + End Sub + + + + Public Sub UnboundGenericType_Bug41779_Extension() + + Dim compilation = CompilationUtils.CreateCompilation( + + + Private Sub GetServiceA(ByVal obj As I) + End Sub + + + Private Function GetServiceB(Of T)(ByVal obj As I) As T + Return "default" + End Function + + + Private Function GetServiceC(Of T1, T2)(ByVal obj As I) As T1 + Return "default" + End Function + + Private Sub M(ByVal provider As I) + provider.GetServiceA(Of)() + provider.GetServiceA(Of)().ToString() + provider.GetServiceB(Of)() + provider.GetServiceB(Of)().ToString() + provider.GetServiceC(Of)() + provider.GetServiceC(Of)().ToString() + End Sub +End Module + ]]> +) + + compilation.AssertTheseDiagnostics( +BC30311: Value of type 'String' cannot be converted to 'T'. + Return "default" + ~~~~~~~~~ +BC30311: Value of type 'String' cannot be converted to 'T1'. + Return "default" + ~~~~~~~~~ +BC36907: Extension method 'Private Sub GetServiceA()' defined in 'Program' is not generic (or has no free type parameters) and so cannot have type arguments. + provider.GetServiceA(Of)() + ~~~~ +BC30182: Type expected. + provider.GetServiceA(Of)() + ~ +BC36907: Extension method 'Private Sub GetServiceA()' defined in 'Program' is not generic (or has no free type parameters) and so cannot have type arguments. + provider.GetServiceA(Of)().ToString() + ~~~~ +BC30182: Type expected. + provider.GetServiceA(Of)().ToString() + ~ +BC30182: Type expected. + provider.GetServiceB(Of)() + ~ +BC30182: Type expected. + provider.GetServiceB(Of)().ToString() + ~ +BC36590: Too few type arguments to extension method 'Private Function GetServiceC(Of T1, T2)() As T1' defined in 'Program'. + provider.GetServiceC(Of)() + ~~~~ +BC30182: Type expected. + provider.GetServiceC(Of)() + ~ +BC36590: Too few type arguments to extension method 'Private Function GetServiceC(Of T1, T2)() As T1' defined in 'Program'. + provider.GetServiceC(Of)().ToString() + ~~~~ +BC30182: Type expected. + provider.GetServiceC(Of)().ToString() + ~ + ) + + End Sub + + + + Public Sub UnboundGenericType_Bug41779_Function() + + Dim compilation = CompilationUtils.CreateCompilation( + + +) + + compilation.AssertTheseDiagnostics( +BC30311: Value of type 'String' cannot be converted to 'T'. + Return "default" + ~~~~~~~~~ +BC30311: Value of type 'String' cannot be converted to 'T1'. + Return "default" + ~~~~~~~~~ +BC32045: 'Private Function GetServiceA() As Object' has no type parameters and so cannot have type arguments. + GetServiceA(Of)() + ~~~~ +BC30182: Type expected. + GetServiceA(Of)() + ~ +BC32045: 'Private Function GetServiceA() As Object' has no type parameters and so cannot have type arguments. + GetServiceA(Of)().ToString() + ~~~~ +BC30182: Type expected. + GetServiceA(Of)().ToString() + ~ +BC30182: Type expected. + GetServiceB(Of)() + ~ +BC30182: Type expected. + GetServiceB(Of)().ToString() + ~ +BC32042: Too few type arguments to 'Private Function GetServiceC(Of T1, T2)() As T1'. + GetServiceC(Of)() + ~~~~ +BC30182: Type expected. + GetServiceC(Of)() + ~ +BC32042: Too few type arguments to 'Private Function GetServiceC(Of T1, T2)() As T1'. + GetServiceC(Of)().ToString() + ~~~~ +BC30182: Type expected. + GetServiceC(Of)().ToString() + ~ + ) + + End Sub + End Class End Namespace From 430056a6bbd1263f85945db4fb4e7ecd388545e8 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Tue, 23 Jun 2020 11:43:02 -0700 Subject: [PATCH 20/22] Do not permit default literals in relational patterns (#45375) Fixes #45292 --- .../CSharp/Portable/Binder/Binder_Patterns.cs | 26 ++++++- .../Portable/Binder/SwitchBinder_Patterns.cs | 1 - .../Semantics/PatternMatchingTests2.cs | 78 ++++++++++++++++++- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index f425958c6f4bf..0d14e5253e038 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -180,7 +180,7 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( bool hasErrors, DiagnosticBag diagnostics) { - ExpressionSyntax innerExpression = expression.SkipParens(); + ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(expression); if (innerExpression.Kind() == SyntaxKind.DefaultLiteralExpression) { diagnostics.Add(ErrorCode.ERR_DefaultPattern, innerExpression.Location); @@ -204,6 +204,24 @@ internal BoundPattern BindConstantPatternWithFallbackToTypePattern( } } + private ExpressionSyntax SkipParensAndNullSuppressions(ExpressionSyntax e) + { + while (true) + { + switch (e) + { + case ParenthesizedExpressionSyntax p: + e = p.Expression; + break; + case PostfixUnaryExpressionSyntax { RawKind: (int)SyntaxKind.SuppressNullableWarningExpression } p: + e = p.Operand; + break; + default: + return e; + } + } + } + /// /// Binds the expression for a pattern. Sets if it was a type rather than an expression, /// and in that case it returns a . @@ -1239,6 +1257,12 @@ private BoundPattern BindRelationalPattern( DiagnosticBag diagnostics) { BoundExpression value = BindExpressionForPattern(inputType, node.Expression, ref hasErrors, diagnostics, out var constantValueOpt, out _); + ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(node.Expression); + if (innerExpression.Kind() == SyntaxKind.DefaultLiteralExpression) + { + diagnostics.Add(ErrorCode.ERR_DefaultPattern, innerExpression.Location); + hasErrors = true; + } RoslynDebug.Assert(value.Type is { }); BinaryOperatorKind operation = tokenKindToBinaryOperatorKind(node.OperatorToken.Kind()); if (operation == BinaryOperatorKind.Equal) diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs index 8a5090ddf7d69..14095889a81a9 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs @@ -237,7 +237,6 @@ private BoundSwitchLabel BindSwitchSectionLabel( case SyntaxKind.CaseSwitchLabel: { var caseLabelSyntax = (CaseSwitchLabelSyntax)node; - SyntaxNode innerExpression = caseLabelSyntax.Value.SkipParens(); bool hasErrors = node.HasErrors; BoundPattern pattern = sectionBinder.BindConstantPatternWithFallbackToTypePattern( caseLabelSyntax.Value, caseLabelSyntax.Value, SwitchGoverningType, hasErrors, diagnostics); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs index 187d4b65adea0..f5baad8ca0e21 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests2.cs @@ -347,6 +347,22 @@ public static void Main() switch (i) { case default: break; } // error 3 switch (i) { case default when true: break; } // error 4 switch ((1, 2)) { case (1, default): break; } // error 5 + + if (i is < default) {} // error 6 + switch (i) { case < default: break; } // error 7 + if (i is < ((default))) {} // error 8 + switch (i) { case < ((default)): break; } // error 9 + + if (i is default!) {} // error 10 + if (i is (default!)) {} // error 11 + if (i is < ((default)!)) {} // error 12 + if (i is default!!) {} // error 13 + if (i is (default!!)) {} // error 14 + if (i is < ((default)!!)) {} // error 15 + + // These are not accepted by the parser. See https://github.com/dotnet/roslyn/issues/45387 + if (i is (default)!) {} // error 16 + if (i is ((default)!)) {} // error 17 } }"; var compilation = CreatePatternCompilation(source); @@ -365,7 +381,67 @@ public static void Main() Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(9, 27), // (10,36): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. // switch ((1, 2)) { case (1, default): break; } // error 5 - Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(10, 36) + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(10, 36), + // (12,20): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is < default) {} // error 6 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(12, 20), + // (13,29): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // switch (i) { case < default: break; } // error 7 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(13, 29), + // (14,22): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is < ((default))) {} // error 8 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(14, 22), + // (15,31): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // switch (i) { case < ((default)): break; } // error 9 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(15, 31), + // (17,18): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is default!) {} // error 10 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(17, 18), + // (18,19): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is (default!)) {} // error 11 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(18, 19), + // (19,22): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is < ((default)!)) {} // error 12 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(19, 22), + // (20,18): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is default!!) {} // error 13 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(20, 18), + // (21,19): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is (default!!)) {} // error 14 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(21, 19), + // (22,22): error CS8715: Duplicate null suppression operator ('!') + // if (i is < ((default)!!)) {} // error 15 + Diagnostic(ErrorCode.ERR_DuplicateNullSuppression, "default").WithLocation(22, 22), + // (22,22): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is < ((default)!!)) {} // error 15 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(22, 22), + // (25,19): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(25, 19), + // (25,27): error CS1026: ) expected + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_CloseParenExpected, "!").WithLocation(25, 27), + // (25,28): error CS1525: Invalid expression term ')' + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(25, 28), + // (25,28): error CS1002: ; expected + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(25, 28), + // (25,28): error CS1513: } expected + // if (i is (default)!) {} // error 16 + Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(25, 28), + // (26,18): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'int', with 2 out parameters and a void return type. + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "((default)!)").WithArguments("int", "2").WithLocation(26, 18), + // (26,20): error CS8505: A default literal 'default' is not valid as a pattern. Use another literal (e.g. '0' or 'null') as appropriate. To match everything, use a discard pattern '_'. + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_DefaultPattern, "default").WithLocation(26, 20), + // (26,28): error CS1003: Syntax error, ',' expected + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_SyntaxError, "!").WithArguments(",", "!").WithLocation(26, 28), + // (26,29): error CS1525: Invalid expression term ')' + // if (i is ((default)!)) {} // error 17 + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(26, 29) ); } From 09db48d5817b397778115c45aaa1dbb933d9ddcd Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 23 Jun 2020 15:42:07 -0700 Subject: [PATCH 21/22] Finalize the design for the shape of the BaseTypeSyntax for records and implement SemanticModel APIs around it. (#45351) Finalize the design for the shape of the BaseTypeSyntax for records and implement SemanticModel APIs around it. Closes #44795. --- .../Portable/Binder/Binder_Expressions.cs | 10 +- .../Portable/Binder/Binder_Statements.cs | 6 +- .../Binder/ExpressionVariableFinder.cs | 4 +- .../Portable/Binder/LocalBinderFactory.cs | 25 +- .../CSharp/Portable/CSharpExtensions.cs | 58 +++ .../Compilation/AttributeSemanticModel.cs | 6 + .../Compilation/CSharpSemanticModel.cs | 126 +++++- .../Compilation/InitializerSemanticModel.cs | 18 +- ...ticModel.SpeculativeMemberSemanticModel.cs | 5 + .../Compilation/MemberSemanticModel.cs | 9 +- .../Compilation/MethodBodySemanticModel.cs | 40 +- .../Compilation/SyntaxTreeSemanticModel.cs | 36 +- .../Portable/Generated/CSharp.Generated.g4 | 9 +- .../Syntax.xml.Internal.Generated.cs | 199 ++++++++-- .../Generated/Syntax.xml.Main.Generated.cs | 30 +- .../Generated/Syntax.xml.Syntax.Generated.cs | 59 ++- .../CSharp/Portable/GlobalSuppressions.cs | 2 + .../LocalRewriter/LocalRewriter_Call.cs | 2 + .../CSharp/Portable/Parser/LanguageParser.cs | 6 +- .../CSharp/Portable/PublicAPI.Unshipped.txt | 23 +- .../Symbols/Source/SourceLocalSymbol.cs | 25 +- .../Records/SynthesizedRecordConstructor.cs | 9 +- .../CSharp/Portable/Syntax/LambdaUtilities.cs | 2 - .../Syntax/RecordDeclarationSyntax.cs | 9 +- .../Portable/Syntax/SimpleBaseTypeSyntax.cs | 14 - .../CSharp/Portable/Syntax/Syntax.xml | 7 +- .../Portable/Syntax/SyntaxExtensions.cs | 1 + .../CSharp/Portable/Syntax/SyntaxFacts.cs | 4 + .../CSharp/Portable/Syntax/SyntaxKind.cs | 4 +- .../Portable/Syntax/SyntaxNodeExtensions.cs | 5 +- .../Test/Semantic/Semantics/RecordTests.cs | 364 +++++++++++++++++- .../Compilation/SemanticModelAPITests.cs | 2 +- .../Generated/Syntax.Test.xml.Generated.cs | 86 ++++- .../Test/Syntax/Parsing/RecordParsing.cs | 4 +- 34 files changed, 1038 insertions(+), 171 deletions(-) delete mode 100644 src/Compilers/CSharp/Portable/Syntax/SimpleBaseTypeSyntax.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 276a331f9a7b9..b26e85c09bb00 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -3777,9 +3777,8 @@ private static bool IsNegativeConstantForArraySize(BoundExpression expression) /// /// /// Null for implicit, - /// BaseConstructorInitializerSyntax.ArgumentList, or - /// ThisConstructorInitializerSyntax.ArgumentList, or - /// SimpleBaseTypeSyntax.ArgumentList for explicit. + /// , or + /// for explicit. /// Constructor containing the initializer. /// Accumulates errors (e.g. unable to find constructor to invoke). /// A bound expression for the constructor initializer call. @@ -3921,9 +3920,8 @@ private BoundExpression BindConstructorInitializerCore( errorLocation = initializerSyntax.ThisOrBaseKeyword.GetLocation(); break; - case SimpleBaseTypeSyntax baseWithArguments: - Debug.Assert(baseWithArguments.Parent?.Parent is RecordDeclarationSyntax recordDecl && recordDecl.BaseList.Types.FirstOrDefault() == baseWithArguments); - nonNullSyntax = initializerArgumentListOpt; + case PrimaryConstructorBaseTypeSyntax baseWithArguments: + nonNullSyntax = baseWithArguments; errorLocation = initializerArgumentListOpt.GetLocation(); break; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index be2802ac4751e..ea2cf793cc199 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3322,7 +3322,7 @@ private BoundNode BindRecordConstructorBody(RecordDeclarationSyntax recordDecl, Debug.Assert(bodyBinder != null); BoundExpressionStatement initializer = null; - if (recordDecl.BaseWithArguments is SimpleBaseTypeSyntax baseWithArguments) + if (recordDecl.PrimaryConstructorBaseType is PrimaryConstructorBaseTypeSyntax baseWithArguments) { initializer = bodyBinder.BindConstructorInitializer(baseWithArguments, diagnostics); } @@ -3334,10 +3334,8 @@ private BoundNode BindRecordConstructorBody(RecordDeclarationSyntax recordDecl, expressionBody: null); } - internal BoundExpressionStatement BindConstructorInitializer(SimpleBaseTypeSyntax initializer, DiagnosticBag diagnostics) + internal virtual BoundExpressionStatement BindConstructorInitializer(PrimaryConstructorBaseTypeSyntax initializer, DiagnosticBag diagnostics) { - Debug.Assert(initializer.Parent?.Parent is RecordDeclarationSyntax recordDecl && recordDecl.ParameterList is object && recordDecl.BaseWithArguments == initializer); - BoundExpression initializerInvocation = GetBinder(initializer).BindConstructorInitializer(initializer.ArgumentList, (MethodSymbol)this.ContainingMember(), diagnostics); var constructorInitializer = new BoundExpressionStatement(initializer, initializerInvocation); Debug.Assert(initializerInvocation.HasAnyErrors || constructorInitializer.IsConstructorInitializer(), "Please keep this bound node in sync with BoundNodeExtensions.IsConstructorInitializer."); diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs index 48bd938480ade..a2a2eb2767d3b 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs @@ -49,7 +49,7 @@ protected void FindExpressionVariables( case SyntaxKind.GotoCaseStatement: break; case SyntaxKind.ArgumentList: - Debug.Assert(node.Parent is ConstructorInitializerSyntax); + Debug.Assert(node.Parent is ConstructorInitializerSyntax || node.Parent is PrimaryConstructorBaseTypeSyntax); break; case SyntaxKind.RecordDeclaration: Debug.Assert(((RecordDeclarationSyntax)node).ParameterList is object); @@ -397,7 +397,7 @@ public override void VisitRecordDeclaration(RecordDeclarationSyntax node) { Debug.Assert(node.ParameterList is object); - if (node.BaseWithArguments is SimpleBaseTypeSyntax baseWithArguments) + if (node.PrimaryConstructorBaseType is PrimaryConstructorBaseTypeSyntax baseWithArguments) { VisitNodeToBind(baseWithArguments); } diff --git a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs index ef27e8dcdd1cf..7a025be2810c6 100644 --- a/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/LocalBinderFactory.cs @@ -162,13 +162,14 @@ public override void VisitRecordDeclaration(RecordDeclarationSyntax node) Binder enclosing = new ExpressionVariableBinder(node, _enclosing); AddToMap(node, enclosing); + Visit(node.PrimaryConstructorBaseType, enclosing); + } - if (node.BaseWithArguments is SimpleBaseTypeSyntax baseWithArguments) - { - enclosing = enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer); - AddToMap(baseWithArguments, enclosing); - Visit(baseWithArguments.ArgumentList, enclosing); - } + public override void VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) + { + Binder enclosing = _enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer); + AddToMap(node, enclosing); + VisitConstructorInitializerArgumentList(node, node.ArgumentList, enclosing); } public override void VisitDestructorDeclaration(DestructorDeclarationSyntax node) @@ -317,16 +318,20 @@ public override void VisitConstructorInitializer(ConstructorInitializerSyntax no { var binder = _enclosing.WithAdditionalFlags(BinderFlags.ConstructorInitializer); AddToMap(node, binder); + VisitConstructorInitializerArgumentList(node, node.ArgumentList, binder); + } - if (node.ArgumentList != null) + private void VisitConstructorInitializerArgumentList(CSharpSyntaxNode node, ArgumentListSyntax argumentList, Binder binder) + { + if (argumentList != null) { if (_root == node) { - binder = new ExpressionVariableBinder(node.ArgumentList, binder); - AddToMap(node.ArgumentList, binder); + binder = new ExpressionVariableBinder(argumentList, binder); + AddToMap(argumentList, binder); } - Visit(node.ArgumentList, binder); + Visit(argumentList, binder); } } diff --git a/src/Compilers/CSharp/Portable/CSharpExtensions.cs b/src/Compilers/CSharp/Portable/CSharpExtensions.cs index 1875ac2f36bf3..8e7998236412d 100644 --- a/src/Compilers/CSharp/Portable/CSharpExtensions.cs +++ b/src/Compilers/CSharp/Portable/CSharpExtensions.cs @@ -537,6 +537,22 @@ public static Conversion ClassifyConversion(this Compilation? compilation, IType } } + /// + /// Returns what symbol(s), if any, the given constructor initializer syntax bound to in the program. + /// + public static SymbolInfo GetSymbolInfo(this SemanticModel? semanticModel, PrimaryConstructorBaseTypeSyntax constructorInitializer, CancellationToken cancellationToken = default(CancellationToken)) + { + var csmodel = semanticModel as CSharpSemanticModel; + if (csmodel != null) + { + return csmodel.GetSymbolInfo(constructorInitializer, cancellationToken); + } + else + { + return SymbolInfo.None; + } + } + /// /// Returns what symbol(s), if any, the given attribute syntax bound to in the program. /// @@ -643,6 +659,27 @@ public static SymbolInfo GetSpeculativeSymbolInfo(this SemanticModel? semanticMo } } + /// + /// Bind the constructor initializer in the context of the specified location and get semantic information + /// about symbols. This method is used to get semantic information about a constructor + /// initializer that did not actually appear in the source code. + /// + /// NOTE: This will only work in locations where there is already a constructor initializer. + /// . + /// + public static SymbolInfo GetSpeculativeSymbolInfo(this SemanticModel? semanticModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer) + { + var csmodel = semanticModel as CSharpSemanticModel; + if (csmodel != null) + { + return csmodel.GetSpeculativeSymbolInfo(position, constructorInitializer); + } + else + { + return SymbolInfo.None; + } + } + /// /// Gets type information about a constructor initializer. /// @@ -1178,6 +1215,27 @@ public static bool TryGetSpeculativeSemanticModel([NotNullWhen(true)] this Seman } } + /// + /// Get a SemanticModel object that is associated with a constructor initializer that did not appear in + /// this source code. This can be used to get detailed semantic information about sub-parts + /// of a constructor initializer that did not appear in source code. + /// + /// NOTE: This will only work in locations where there is already a constructor initializer. + /// + public static bool TryGetSpeculativeSemanticModel([NotNullWhen(true)] this SemanticModel? semanticModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, [NotNullWhen(true)] out SemanticModel? speculativeModel) + { + var csmodel = semanticModel as CSharpSemanticModel; + if (csmodel != null) + { + return csmodel.TryGetSpeculativeSemanticModel(position, constructorInitializer, out speculativeModel); + } + else + { + speculativeModel = null; + return false; + } + } + /// /// Get a SemanticModel object that is associated with an attribute that did not appear in /// this source code. This can be used to get detailed semantic information about sub-parts diff --git a/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs index cd1a135d60701..c5a400824172a 100644 --- a/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/AttributeSemanticModel.cs @@ -118,6 +118,12 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + { + speculativeModel = null; + return false; + } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) { speculativeModel = null; diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 41f8b3b4f4eb5..07fb552aeeb41 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -120,6 +120,7 @@ internal static bool CanGetSemanticInfo(CSharpSyntaxNode node, bool allowNamedAr return (node is ExpressionSyntax && (isSpeculative || allowNamedArgumentName || !SyntaxFacts.IsNamedArgumentName(node))) || (node is ConstructorInitializerSyntax) || + (node is PrimaryConstructorBaseTypeSyntax) || (node is AttributeSyntax) || (node is CrefSyntax); } @@ -636,7 +637,6 @@ private static SymbolInfo GetSymbolInfoFromSymbolOrNone(ITypeSymbol type) return SymbolInfo.None; } - /// /// Returns what symbol(s), if any, the given constructor initializer syntax bound to in the program. /// @@ -651,6 +651,20 @@ private static SymbolInfo GetSymbolInfoFromSymbolOrNone(ITypeSymbol type) : SymbolInfo.None; } + /// + /// Returns what symbol(s), if any, the given constructor initializer syntax bound to in the program. + /// + /// The syntax node to get semantic information for. + /// The cancellation token. + public SymbolInfo GetSymbolInfo(PrimaryConstructorBaseTypeSyntax constructorInitializer, CancellationToken cancellationToken = default(CancellationToken)) + { + CheckSyntaxNode(constructorInitializer); + + return CanGetSemanticInfo(constructorInitializer) + ? GetSymbolInfoWorker(constructorInitializer, SymbolInfoOptions.DefaultOptions, cancellationToken) + : SymbolInfo.None; + } + /// /// Returns what symbol(s), if any, the given attribute syntax bound to in the program. /// @@ -798,7 +812,82 @@ public SymbolInfo GetSpeculativeSymbolInfo(int position, ConstructorInitializerS binder = new ExecutableCodeBinder(constructorInitializer, binder.ContainingMemberOrLambda, binder); BoundExpressionStatement bnode = binder.BindConstructorInitializer(constructorInitializer, diagnostics); - var binfo = memberModel.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, bnode.Expression, bnode.Expression, boundNodeForSyntacticParent: null, binderOpt: binder); + var binfo = GetSymbolInfoFromBoundConstructorInitializer(memberModel, binder, bnode); + diagnostics.Free(); + return binfo; + } + else + { + return SymbolInfo.None; + } + } + + private static SymbolInfo GetSymbolInfoFromBoundConstructorInitializer(MemberSemanticModel memberModel, Binder binder, BoundExpressionStatement bnode) + { + BoundExpression expression = bnode.Expression; + + while (expression is BoundSequence sequence) + { + expression = sequence.Value; + } + + return memberModel.GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, expression, expression, boundNodeForSyntacticParent: null, binderOpt: binder); + } + + /// + /// Bind the constructor initializer in the context of the specified location and get semantic information + /// about symbols. This method is used to get semantic information about a constructor + /// initializer that did not actually appear in the source code. + /// + /// NOTE: This will only work in locations where there is already a constructor initializer. + /// + /// A character position used to identify a declaration scope and accessibility. This + /// character position must be within the span of an existing constructor initializer. + /// + /// A syntax node that represents a parsed constructor initializer. This syntax node + /// need not and typically does not appear in the source code referred to SemanticModel instance. + /// The semantic information for the topmost node of the constructor initializer. + public SymbolInfo GetSpeculativeSymbolInfo(int position, PrimaryConstructorBaseTypeSyntax constructorInitializer) + { + Debug.Assert(CanGetSemanticInfo(constructorInitializer, isSpeculative: true)); + + position = CheckAndAdjustPosition(position); + + if (constructorInitializer == null) + { + throw new ArgumentNullException(nameof(constructorInitializer)); + } + + // NOTE: since we're going to be depending on a MemberModel to do the binding for us, + // we need to find a constructor initializer in the tree of this semantic model. + // NOTE: This approach will not allow speculative binding of a constructor initializer + // on a constructor that didn't formerly have one. + // TODO: Should we support positions that are not in existing constructor initializers? + // If so, we will need to build up the context that would otherwise be built up by + // InitializerMemberModel. + var existingConstructorInitializer = this.Root.FindToken(position).Parent.AncestorsAndSelf().OfType().FirstOrDefault(); + + if (existingConstructorInitializer == null) + { + return SymbolInfo.None; + } + + MemberSemanticModel memberModel = GetMemberModel(existingConstructorInitializer); + + if (memberModel == null) + { + return SymbolInfo.None; + } + + var argumentList = existingConstructorInitializer.ArgumentList; + var binder = memberModel.GetEnclosingBinder(LookupPosition.IsBetweenTokens(position, argumentList.OpenParenToken, argumentList.CloseParenToken) ? position : argumentList.OpenParenToken.SpanStart); + if (binder != null) + { + var diagnostics = DiagnosticBag.GetInstance(); + binder = new ExecutableCodeBinder(constructorInitializer, binder.ContainingMemberOrLambda, binder); + + BoundExpressionStatement bnode = binder.BindConstructorInitializer(constructorInitializer, diagnostics); + SymbolInfo binfo = GetSymbolInfoFromBoundConstructorInitializer(memberModel, binder, bnode); diagnostics.Free(); return binfo; } @@ -1014,7 +1103,7 @@ public Conversion GetSpeculativeConversion(int position, ExpressionSyntax expres } /// - /// Gets a list of method or indexed property symbols for a syntax node. + /// Gets a list of method symbols for a syntax node. /// /// The syntax node to get semantic information for. /// The cancellation token. @@ -2514,6 +2603,33 @@ public bool TryGetSpeculativeSemanticModel(int position, ConstructorInitializerS internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel); + /// + /// Get a SemanticModel object that is associated with a constructor initializer that did not appear in + /// this source code. This can be used to get detailed semantic information about sub-parts + /// of a constructor initializer that did not appear in source code. + /// + /// NOTE: This will only work in locations where there is already a constructor initializer. + /// + /// A character position used to identify a declaration scope and accessibility. This + /// character position must be within the span of an existing constructor initializer. + /// + /// A syntax node that represents a parsed constructor initializer. + /// This node should not be present in the syntax tree associated with this object. + /// A SemanticModel object that can be used to inquire about the semantic + /// information associated with syntax nodes within . + /// Flag indicating whether a speculative semantic model was created. + /// Throws this exception if the node is contained any SyntaxTree in the current Compilation. + /// Throws this exception if is null. + /// Throws this exception if this model is a speculative semantic model, i.e. is true. + /// Chaining of speculative semantic model is not supported. + public bool TryGetSpeculativeSemanticModel(int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + { + CheckModelAndSyntaxNodeToSpeculate(constructorInitializer); + return TryGetSpeculativeSemanticModelCore((SyntaxTreeSemanticModel)this, position, constructorInitializer, out speculativeModel); + } + + internal abstract bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel); + /// /// Get a SemanticModel object that is associated with a cref that did not appear in /// this source code. This can be used to get detailed semantic information about sub-parts @@ -4753,6 +4869,8 @@ private SymbolInfo GetSymbolInfoFromNode(SyntaxNode node, CancellationToken canc return this.GetSymbolInfo(expression, cancellationToken); case ConstructorInitializerSyntax initializer: return this.GetSymbolInfo(initializer, cancellationToken); + case PrimaryConstructorBaseTypeSyntax initializer: + return this.GetSymbolInfo(initializer, cancellationToken); case AttributeSyntax attribute: return this.GetSymbolInfo(attribute, cancellationToken); case CrefSyntax cref: @@ -4820,6 +4938,8 @@ protected sealed override SymbolInfo GetSpeculativeSymbolInfoCore(int position, return GetSpeculativeSymbolInfo(position, expression, bindingOption); case ConstructorInitializerSyntax initializer: return GetSpeculativeSymbolInfo(position, initializer); + case PrimaryConstructorBaseTypeSyntax initializer: + return GetSpeculativeSymbolInfo(position, initializer); case AttributeSyntax attribute: return GetSpeculativeSymbolInfo(position, attribute); case CrefSyntax cref: diff --git a/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs index bcaca17ced874..7ae4615fa0954 100644 --- a/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/InitializerSemanticModel.cs @@ -30,7 +30,7 @@ private InitializerSemanticModel(CSharpSyntaxNode syntax, int speculatedPosition = 0) : base(syntax, symbol, rootBinder, containingSemanticModelOpt, parentSemanticModelOpt, snapshotManagerOpt: null, parentRemappedSymbolsOpt, speculatedPosition) { - Debug.Assert(!(syntax is ConstructorInitializerSyntax)); + Debug.Assert(!(syntax is ConstructorInitializerSyntax || syntax is PrimaryConstructorBaseTypeSyntax)); } /// @@ -104,11 +104,6 @@ internal override BoundNode GetBoundRoot() rootSyntax = ((EnumMemberDeclarationSyntax)rootSyntax).EqualsValue.Value; break; - case SyntaxKind.BaseConstructorInitializer: - case SyntaxKind.ThisConstructorInitializer: - case SyntaxKind.ArgumentList: - break; - case SyntaxKind.PropertyDeclaration: rootSyntax = ((PropertyDeclarationSyntax)rootSyntax).Initializer.Value; break; @@ -207,11 +202,6 @@ private bool IsBindableInitializer(CSharpSyntaxNode node) return this.Root == node || /*enum or parameter initializer*/ this.Root == node.Parent /*field initializer*/; - case SyntaxKind.BaseConstructorInitializer: - case SyntaxKind.ThisConstructorInitializer: - case SyntaxKind.ArgumentList: - return this.Root == node; - default: return false; } @@ -237,6 +227,12 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + { + speculativeModel = null; + return false; + } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ArrowExpressionClauseSyntax expressionBody, out SemanticModel speculativeModel) { speculativeModel = null; diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs index 197ac790a5366..9b41ade76912c 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.SpeculativeMemberSemanticModel.cs @@ -49,6 +49,11 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode throw ExceptionUtilities.Unreachable; } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + { + throw ExceptionUtilities.Unreachable; + } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) { throw ExceptionUtilities.Unreachable; diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index 6a2d25d132d91..233dc45f1f834 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -317,7 +317,7 @@ private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int { binder = rootBinder.GetBinder(current); } - else if (kind == SyntaxKind.ThisConstructorInitializer || kind == SyntaxKind.BaseConstructorInitializer || kind == SyntaxKind.SimpleBaseType) + else if (kind == SyntaxKind.ThisConstructorInitializer || kind == SyntaxKind.BaseConstructorInitializer || kind == SyntaxKind.PrimaryConstructorBaseType) { binder = rootBinder.GetBinder(current); } @@ -1549,6 +1549,7 @@ private CSharpSyntaxNode GetBindingRoot(CSharpSyntaxNode node) { case SyntaxKind.ThisConstructorInitializer: case SyntaxKind.BaseConstructorInitializer: + case SyntaxKind.PrimaryConstructorBaseType: return current; case SyntaxKind.ArrowExpressionClause: // If this is an arrow expression on a local function statement, then our bindable root is actually our parent syntax as it's @@ -2207,6 +2208,7 @@ internal protected virtual CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNo !(node is JoinIntoClauseSyntax) && !(node is QueryContinuationSyntax) && !(node is ConstructorInitializerSyntax) && + !(node is PrimaryConstructorBaseTypeSyntax) && !(node is ArrowExpressionClauseSyntax) && !(node is PatternSyntax)) { @@ -2433,6 +2435,11 @@ internal override BoundExpressionStatement BindConstructorInitializer(Constructo return (BoundExpressionStatement)TryGetBoundNodeFromMap(node) ?? base.BindConstructorInitializer(node, diagnostics); } + internal override BoundExpressionStatement BindConstructorInitializer(PrimaryConstructorBaseTypeSyntax node, DiagnosticBag diagnostics) + { + return (BoundExpressionStatement)TryGetBoundNodeFromMap(node) ?? base.BindConstructorInitializer(node, diagnostics); + } + internal override BoundBlock BindExpressionBodyAsBlock(ArrowExpressionClauseSyntax node, DiagnosticBag diagnostics) { BoundBlock block = (BoundBlock)TryGetBoundNodeFromMap(node); diff --git a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs index 7a2090bd1e8d8..9a43b48c9a43d 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -85,6 +86,9 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, Diagnosti case SyntaxKind.ThisConstructorInitializer: return binder.BindConstructorInitializer((ConstructorInitializerSyntax)node, diagnostics); + case SyntaxKind.PrimaryConstructorBaseType: + return binder.BindConstructorInitializer((PrimaryConstructorBaseTypeSyntax)node, diagnostics); + case SyntaxKind.MethodDeclaration: case SyntaxKind.ConversionOperatorDeclaration: case SyntaxKind.OperatorDeclaration: @@ -149,6 +153,19 @@ internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticMode return new MethodBodySemanticModel(owner, rootBinder, syntax, parentSemanticModelOpt: parentSemanticModel, speculatedPosition: position); } + /// + /// Creates a speculative SemanticModel for a constructor initializer that did not appear in the original source code. + /// + internal static MethodBodySemanticModel CreateSpeculative(SyntaxTreeSemanticModel parentSemanticModel, MethodSymbol owner, PrimaryConstructorBaseTypeSyntax syntax, Binder rootBinder, int position) + { + Debug.Assert(parentSemanticModel != null); + Debug.Assert(syntax != null); + Debug.Assert(rootBinder != null); + Debug.Assert(rootBinder.IsSemanticModelBinder); + + return new MethodBodySemanticModel(owner, rootBinder, syntax, parentSemanticModelOpt: parentSemanticModel, speculatedPosition: position); + } + internal override bool TryGetSpeculativeSemanticModelForMethodBodyCore(SyntaxTreeSemanticModel parentModel, int position, BaseMethodDeclarationSyntax method, out SemanticModel speculativeModel) { // CONSIDER: Do we want to ensure that speculated method and the original method have identical signatures? @@ -231,12 +248,12 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, ConstructorInitializerSyntax constructorInitializer, out SemanticModel speculativeModel) { - if ((MemberSymbol as MethodSymbol)?.MethodKind == MethodKind.Constructor) + if (MemberSymbol is MethodSymbol methodSymbol && methodSymbol.MethodKind == MethodKind.Constructor && + Root.FindToken(position).Parent?.AncestorsAndSelf().OfType().FirstOrDefault()?.Parent == Root) { var binder = this.GetEnclosingBinder(position); if (binder != null) { - var methodSymbol = (MethodSymbol)this.MemberSymbol; binder = new WithNullableContextBinder(SyntaxTree, position, binder); binder = new ExecutableCodeBinder(constructorInitializer, methodSymbol, binder); speculativeModel = CreateSpeculative(parentModel, methodSymbol, constructorInitializer, binder, position); @@ -248,6 +265,25 @@ internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticMode return false; } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + { + if (MemberSymbol is SynthesizedRecordConstructor primaryCtor && + Root.FindToken(position).Parent?.AncestorsAndSelf().OfType().FirstOrDefault() == primaryCtor.GetSyntax().PrimaryConstructorBaseType) + { + var binder = this.GetEnclosingBinder(position); + if (binder != null) + { + binder = new WithNullableContextBinder(SyntaxTree, position, binder); + binder = new ExecutableCodeBinder(constructorInitializer, primaryCtor, binder); + speculativeModel = CreateSpeculative(parentModel, primaryCtor, constructorInitializer, binder, position); + return true; + } + } + + speculativeModel = null; + return false; + } + internal override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, EqualsValueClauseSyntax initializer, out SemanticModel speculativeModel) { speculativeModel = null; diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index e31435850de7a..1c5c44a5064d7 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -711,10 +711,34 @@ internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSeman { position = CheckAndAdjustPosition(position); - var model = this.GetMemberModel(position); - if (model != null) + var existingConstructorInitializer = this.Root.FindToken(position).Parent.AncestorsAndSelf().OfType().FirstOrDefault(); + + if (existingConstructorInitializer != null) + { + var model = this.GetMemberModel(position); + if (model != null) + { + return model.TryGetSpeculativeSemanticModelCore(parentModel, position, constructorInitializer, out speculativeModel); + } + } + + speculativeModel = null; + return false; + } + + internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, PrimaryConstructorBaseTypeSyntax constructorInitializer, out SemanticModel speculativeModel) + { + position = CheckAndAdjustPosition(position); + + var existingConstructorInitializer = this.Root.FindToken(position).Parent.AncestorsAndSelf().OfType().FirstOrDefault(); + + if (existingConstructorInitializer != null) { - return model.TryGetSpeculativeSemanticModelCore(parentModel, position, constructorInitializer, out speculativeModel); + var model = this.GetMemberModel(existingConstructorInitializer); + if (model != null) + { + return model.TryGetSpeculativeSemanticModelCore(parentModel, position, constructorInitializer, out speculativeModel); + } } speculativeModel = null; @@ -784,7 +808,7 @@ private MemberSemanticModel GetMemberModel(int position) } else { - var argumentList = recordDecl.BaseWithArguments?.ArgumentList; + var argumentList = recordDecl.PrimaryConstructorBaseType?.ArgumentList; outsideMemberDecl = argumentList is null || !LookupPosition.IsBetweenTokens(position, argumentList.OpenParenToken, argumentList.CloseParenToken); } } @@ -846,7 +870,9 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node) case SyntaxKind.RecordDeclaration: { var recordDecl = (RecordDeclarationSyntax)memberDecl; - return recordDecl.ParameterList is object && recordDecl.BaseWithArguments?.ArgumentList.FullSpan.Contains(span) == true ? GetOrAddModel(memberDecl) : null; + return recordDecl.ParameterList is object && + recordDecl.PrimaryConstructorBaseType is PrimaryConstructorBaseTypeSyntax baseWithArguments && + (node == baseWithArguments || baseWithArguments.ArgumentList.FullSpan.Contains(span)) ? GetOrAddModel(memberDecl) : null; } case SyntaxKind.DestructorDeclaration: diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 9791532660a28..b522789a8b835 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -272,11 +272,16 @@ base_list ; base_type - : simple_base_type + : primary_constructor_base_type + | simple_base_type + ; + +primary_constructor_base_type + : type argument_list ; simple_base_type - : type argument_list? + : type ; enum_member_declaration diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs index 7c7c3d2403d0e..05ef43a82fc30 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Internal.Generated.cs @@ -22279,50 +22279,124 @@ protected BaseTypeSyntax(ObjectReader reader) internal sealed partial class SimpleBaseTypeSyntax : BaseTypeSyntax { internal readonly TypeSyntax type; - internal readonly ArgumentListSyntax? argumentList; - internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSyntax? argumentList, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) : base(kind, diagnostics, annotations) { - this.SlotCount = 2; + this.SlotCount = 1; this.AdjustFlagsAndWidth(type); this.type = type; - if (argumentList != null) + } + + internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 1; + this.AdjustFlagsAndWidth(type); + this.type = type; + } + + internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type) + : base(kind) + { + this.SlotCount = 1; + this.AdjustFlagsAndWidth(type); + this.type = type; + } + + public override TypeSyntax Type => this.type; + + internal override GreenNode? GetSlot(int index) + => index == 0 ? this.type : null; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.SimpleBaseTypeSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + + public SimpleBaseTypeSyntax Update(TypeSyntax type) + { + if (type != this.Type) { - this.AdjustFlagsAndWidth(argumentList); - this.argumentList = argumentList; + var newNode = SyntaxFactory.SimpleBaseType(type); + var diags = GetDiagnostics(); + if (diags?.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations?.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; } + + return this; + } + + internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) + => new SimpleBaseTypeSyntax(this.Kind, this.type, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new SimpleBaseTypeSyntax(this.Kind, this.type, GetDiagnostics(), annotations); + + internal SimpleBaseTypeSyntax(ObjectReader reader) + : base(reader) + { + this.SlotCount = 1; + var type = (TypeSyntax)reader.ReadValue(); + AdjustFlagsAndWidth(type); + this.type = type; + } + + internal override void WriteTo(ObjectWriter writer) + { + base.WriteTo(writer); + writer.WriteValue(this.type); + } + + static SimpleBaseTypeSyntax() + { + ObjectBinder.RegisterTypeReader(typeof(SimpleBaseTypeSyntax), r => new SimpleBaseTypeSyntax(r)); + } + } + + internal sealed partial class PrimaryConstructorBaseTypeSyntax : BaseTypeSyntax + { + internal readonly TypeSyntax type; + internal readonly ArgumentListSyntax argumentList; + + internal PrimaryConstructorBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSyntax argumentList, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 2; + this.AdjustFlagsAndWidth(type); + this.type = type; + this.AdjustFlagsAndWidth(argumentList); + this.argumentList = argumentList; } - internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSyntax? argumentList, SyntaxFactoryContext context) + internal PrimaryConstructorBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSyntax argumentList, SyntaxFactoryContext context) : base(kind) { this.SetFactoryContext(context); this.SlotCount = 2; this.AdjustFlagsAndWidth(type); this.type = type; - if (argumentList != null) - { - this.AdjustFlagsAndWidth(argumentList); - this.argumentList = argumentList; - } + this.AdjustFlagsAndWidth(argumentList); + this.argumentList = argumentList; } - internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSyntax? argumentList) + internal PrimaryConstructorBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSyntax argumentList) : base(kind) { this.SlotCount = 2; this.AdjustFlagsAndWidth(type); this.type = type; - if (argumentList != null) - { - this.AdjustFlagsAndWidth(argumentList); - this.argumentList = argumentList; - } + this.AdjustFlagsAndWidth(argumentList); + this.argumentList = argumentList; } public override TypeSyntax Type => this.type; - public ArgumentListSyntax? ArgumentList => this.argumentList; + public ArgumentListSyntax ArgumentList => this.argumentList; internal override GreenNode? GetSlot(int index) => index switch @@ -22332,16 +22406,16 @@ internal SimpleBaseTypeSyntax(SyntaxKind kind, TypeSyntax type, ArgumentListSynt _ => null, }; - internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.SimpleBaseTypeSyntax(this, parent, position); + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.PrimaryConstructorBaseTypeSyntax(this, parent, position); - public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); - public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitPrimaryConstructorBaseType(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitPrimaryConstructorBaseType(this); - public SimpleBaseTypeSyntax Update(TypeSyntax type, ArgumentListSyntax argumentList) + public PrimaryConstructorBaseTypeSyntax Update(TypeSyntax type, ArgumentListSyntax argumentList) { if (type != this.Type || argumentList != this.ArgumentList) { - var newNode = SyntaxFactory.SimpleBaseType(type, argumentList); + var newNode = SyntaxFactory.PrimaryConstructorBaseType(type, argumentList); var diags = GetDiagnostics(); if (diags?.Length > 0) newNode = newNode.WithDiagnosticsGreen(diags); @@ -22355,24 +22429,21 @@ public SimpleBaseTypeSyntax Update(TypeSyntax type, ArgumentListSyntax argumentL } internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) - => new SimpleBaseTypeSyntax(this.Kind, this.type, this.argumentList, diagnostics, GetAnnotations()); + => new PrimaryConstructorBaseTypeSyntax(this.Kind, this.type, this.argumentList, diagnostics, GetAnnotations()); internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) - => new SimpleBaseTypeSyntax(this.Kind, this.type, this.argumentList, GetDiagnostics(), annotations); + => new PrimaryConstructorBaseTypeSyntax(this.Kind, this.type, this.argumentList, GetDiagnostics(), annotations); - internal SimpleBaseTypeSyntax(ObjectReader reader) + internal PrimaryConstructorBaseTypeSyntax(ObjectReader reader) : base(reader) { this.SlotCount = 2; var type = (TypeSyntax)reader.ReadValue(); AdjustFlagsAndWidth(type); this.type = type; - var argumentList = (ArgumentListSyntax?)reader.ReadValue(); - if (argumentList != null) - { - AdjustFlagsAndWidth(argumentList); - this.argumentList = argumentList; - } + var argumentList = (ArgumentListSyntax)reader.ReadValue(); + AdjustFlagsAndWidth(argumentList); + this.argumentList = argumentList; } internal override void WriteTo(ObjectWriter writer) @@ -22382,9 +22453,9 @@ internal override void WriteTo(ObjectWriter writer) writer.WriteValue(this.argumentList); } - static SimpleBaseTypeSyntax() + static PrimaryConstructorBaseTypeSyntax() { - ObjectBinder.RegisterTypeReader(typeof(SimpleBaseTypeSyntax), r => new SimpleBaseTypeSyntax(r)); + ObjectBinder.RegisterTypeReader(typeof(PrimaryConstructorBaseTypeSyntax), r => new PrimaryConstructorBaseTypeSyntax(r)); } } @@ -32376,6 +32447,7 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => this.DefaultVisit(node); public virtual TResult VisitBaseList(BaseListSyntax node) => this.DefaultVisit(node); public virtual TResult VisitSimpleBaseType(SimpleBaseTypeSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => this.DefaultVisit(node); public virtual TResult VisitTypeParameterConstraintClause(TypeParameterConstraintClauseSyntax node) => this.DefaultVisit(node); public virtual TResult VisitConstructorConstraint(ConstructorConstraintSyntax node) => this.DefaultVisit(node); public virtual TResult VisitClassOrStructConstraint(ClassOrStructConstraintSyntax node) => this.DefaultVisit(node); @@ -32604,6 +32676,7 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => this.DefaultVisit(node); public virtual void VisitBaseList(BaseListSyntax node) => this.DefaultVisit(node); public virtual void VisitSimpleBaseType(SimpleBaseTypeSyntax node) => this.DefaultVisit(node); + public virtual void VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => this.DefaultVisit(node); public virtual void VisitTypeParameterConstraintClause(TypeParameterConstraintClauseSyntax node) => this.DefaultVisit(node); public virtual void VisitConstructorConstraint(ConstructorConstraintSyntax node) => this.DefaultVisit(node); public virtual void VisitClassOrStructConstraint(ClassOrStructConstraintSyntax node) => this.DefaultVisit(node); @@ -33148,6 +33221,9 @@ public override CSharpSyntaxNode VisitBaseList(BaseListSyntax node) => node.Update((SyntaxToken)Visit(node.ColonToken), VisitList(node.Types)); public override CSharpSyntaxNode VisitSimpleBaseType(SimpleBaseTypeSyntax node) + => node.Update((TypeSyntax)Visit(node.Type)); + + public override CSharpSyntaxNode VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => node.Update((TypeSyntax)Visit(node.Type), (ArgumentListSyntax)Visit(node.ArgumentList)); public override CSharpSyntaxNode VisitTypeParameterConstraintClause(TypeParameterConstraintClauseSyntax node) @@ -36736,17 +36812,37 @@ public BaseListSyntax BaseList(SyntaxToken colonToken, Microsoft.CodeAnalysis.Sy return result; } - public SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type, ArgumentListSyntax? argumentList) + public SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type) { #if DEBUG if (type == null) throw new ArgumentNullException(nameof(type)); #endif int hash; - var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.SimpleBaseType, type, argumentList, this.context, out hash); + var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.SimpleBaseType, type, this.context, out hash); if (cached != null) return (SimpleBaseTypeSyntax)cached; - var result = new SimpleBaseTypeSyntax(SyntaxKind.SimpleBaseType, type, argumentList, this.context); + var result = new SimpleBaseTypeSyntax(SyntaxKind.SimpleBaseType, type, this.context); + if (hash >= 0) + { + SyntaxNodeCache.AddNode(result, hash); + } + + return result; + } + + public PrimaryConstructorBaseTypeSyntax PrimaryConstructorBaseType(TypeSyntax type, ArgumentListSyntax argumentList) + { +#if DEBUG + if (type == null) throw new ArgumentNullException(nameof(type)); + if (argumentList == null) throw new ArgumentNullException(nameof(argumentList)); +#endif + + int hash; + var cached = CSharpSyntaxNodeCache.TryGetNode((int)SyntaxKind.PrimaryConstructorBaseType, type, argumentList, this.context, out hash); + if (cached != null) return (PrimaryConstructorBaseTypeSyntax)cached; + + var result = new PrimaryConstructorBaseTypeSyntax(SyntaxKind.PrimaryConstructorBaseType, type, argumentList, this.context); if (hash >= 0) { SyntaxNodeCache.AddNode(result, hash); @@ -41486,17 +41582,37 @@ public static BaseListSyntax BaseList(SyntaxToken colonToken, Microsoft.CodeAnal return result; } - public static SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type, ArgumentListSyntax? argumentList) + public static SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type) { #if DEBUG if (type == null) throw new ArgumentNullException(nameof(type)); #endif int hash; - var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.SimpleBaseType, type, argumentList, out hash); + var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.SimpleBaseType, type, out hash); if (cached != null) return (SimpleBaseTypeSyntax)cached; - var result = new SimpleBaseTypeSyntax(SyntaxKind.SimpleBaseType, type, argumentList); + var result = new SimpleBaseTypeSyntax(SyntaxKind.SimpleBaseType, type); + if (hash >= 0) + { + SyntaxNodeCache.AddNode(result, hash); + } + + return result; + } + + public static PrimaryConstructorBaseTypeSyntax PrimaryConstructorBaseType(TypeSyntax type, ArgumentListSyntax argumentList) + { +#if DEBUG + if (type == null) throw new ArgumentNullException(nameof(type)); + if (argumentList == null) throw new ArgumentNullException(nameof(argumentList)); +#endif + + int hash; + var cached = SyntaxNodeCache.TryGetNode((int)SyntaxKind.PrimaryConstructorBaseType, type, argumentList, out hash); + if (cached != null) return (PrimaryConstructorBaseTypeSyntax)cached; + + var result = new PrimaryConstructorBaseTypeSyntax(SyntaxKind.PrimaryConstructorBaseType, type, argumentList); if (hash >= 0) { SyntaxNodeCache.AddNode(result, hash); @@ -43012,6 +43128,7 @@ internal static IEnumerable GetNodeTypes() typeof(EnumMemberDeclarationSyntax), typeof(BaseListSyntax), typeof(SimpleBaseTypeSyntax), + typeof(PrimaryConstructorBaseTypeSyntax), typeof(TypeParameterConstraintClauseSyntax), typeof(ConstructorConstraintSyntax), typeof(ClassOrStructConstraintSyntax), diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs index 9ab4b6174aa34..2940354e56d5f 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Main.Generated.cs @@ -651,6 +651,10 @@ public partial class CSharpSyntaxVisitor [return: MaybeNull] public virtual TResult VisitSimpleBaseType(SimpleBaseTypeSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a PrimaryConstructorBaseTypeSyntax node. + [return: MaybeNull] + public virtual TResult VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a TypeParameterConstraintClauseSyntax node. [return: MaybeNull] public virtual TResult VisitTypeParameterConstraintClause(TypeParameterConstraintClauseSyntax node) => this.DefaultVisit(node); @@ -1391,6 +1395,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a SimpleBaseTypeSyntax node. public virtual void VisitSimpleBaseType(SimpleBaseTypeSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a PrimaryConstructorBaseTypeSyntax node. + public virtual void VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a TypeParameterConstraintClauseSyntax node. public virtual void VisitTypeParameterConstraintClause(TypeParameterConstraintClauseSyntax node) => this.DefaultVisit(node); @@ -2064,7 +2071,10 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor => node.Update(VisitToken(node.ColonToken), VisitList(node.Types)); public override SyntaxNode? VisitSimpleBaseType(SimpleBaseTypeSyntax node) - => node.Update((TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"), (ArgumentListSyntax?)Visit(node.ArgumentList)); + => node.Update((TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type")); + + public override SyntaxNode? VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) + => node.Update((TypeSyntax?)Visit(node.Type) ?? throw new ArgumentNullException("type"), (ArgumentListSyntax?)Visit(node.ArgumentList) ?? throw new ArgumentNullException("argumentList")); public override SyntaxNode? VisitTypeParameterConstraintClause(TypeParameterConstraintClauseSyntax node) => node.Update(VisitToken(node.WhereKeyword), (IdentifierNameSyntax?)Visit(node.Name) ?? throw new ArgumentNullException("name"), VisitToken(node.ColonToken), VisitList(node.Constraints)); @@ -5044,15 +5054,23 @@ public static BaseListSyntax BaseList(SeparatedSyntaxList types => SyntaxFactory.BaseList(SyntaxFactory.Token(SyntaxKind.ColonToken), types); /// Creates a new SimpleBaseTypeSyntax instance. - public static SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type, ArgumentListSyntax? argumentList) + public static SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type) { if (type == null) throw new ArgumentNullException(nameof(type)); - return (SimpleBaseTypeSyntax)Syntax.InternalSyntax.SyntaxFactory.SimpleBaseType((Syntax.InternalSyntax.TypeSyntax)type.Green, argumentList == null ? null : (Syntax.InternalSyntax.ArgumentListSyntax)argumentList.Green).CreateRed(); + return (SimpleBaseTypeSyntax)Syntax.InternalSyntax.SyntaxFactory.SimpleBaseType((Syntax.InternalSyntax.TypeSyntax)type.Green).CreateRed(); } - /// Creates a new SimpleBaseTypeSyntax instance. - public static SimpleBaseTypeSyntax SimpleBaseType(TypeSyntax type) - => SyntaxFactory.SimpleBaseType(type, default); + /// Creates a new PrimaryConstructorBaseTypeSyntax instance. + public static PrimaryConstructorBaseTypeSyntax PrimaryConstructorBaseType(TypeSyntax type, ArgumentListSyntax argumentList) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + if (argumentList == null) throw new ArgumentNullException(nameof(argumentList)); + return (PrimaryConstructorBaseTypeSyntax)Syntax.InternalSyntax.SyntaxFactory.PrimaryConstructorBaseType((Syntax.InternalSyntax.TypeSyntax)type.Green, (Syntax.InternalSyntax.ArgumentListSyntax)argumentList.Green).CreateRed(); + } + + /// Creates a new PrimaryConstructorBaseTypeSyntax instance. + public static PrimaryConstructorBaseTypeSyntax PrimaryConstructorBaseType(TypeSyntax type) + => SyntaxFactory.PrimaryConstructorBaseType(type, SyntaxFactory.ArgumentList()); /// Creates a new TypeParameterConstraintClauseSyntax instance. public static TypeParameterConstraintClauseSyntax TypeParameterConstraintClause(SyntaxToken whereKeyword, IdentifierNameSyntax name, SyntaxToken colonToken, SeparatedSyntaxList constraints) diff --git a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs index 88d43df3f1f7f..0535a9109294c 100644 --- a/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/Syntax.xml.Syntax.Generated.cs @@ -9578,7 +9578,6 @@ internal BaseTypeSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? paren public sealed partial class SimpleBaseTypeSyntax : BaseTypeSyntax { private TypeSyntax? type; - private ArgumentListSyntax? argumentList; internal SimpleBaseTypeSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) : base(green, parent, position) @@ -9587,13 +9586,49 @@ internal SimpleBaseTypeSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? public override TypeSyntax Type => GetRedAtZero(ref this.type)!; - public ArgumentListSyntax? ArgumentList => GetRed(ref this.argumentList, 1); + internal override SyntaxNode? GetNodeSlot(int index) => index == 0 ? GetRedAtZero(ref this.type)! : null; + + internal override SyntaxNode? GetCachedSlot(int index) => index == 0 ? this.type : null; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + [return: MaybeNull] + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + + public SimpleBaseTypeSyntax Update(TypeSyntax type) + { + if (type != this.Type) + { + var newNode = SyntaxFactory.SimpleBaseType(type); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + internal override BaseTypeSyntax WithTypeCore(TypeSyntax type) => WithType(type); + public new SimpleBaseTypeSyntax WithType(TypeSyntax type) => Update(type); + } + + public sealed partial class PrimaryConstructorBaseTypeSyntax : BaseTypeSyntax + { + private TypeSyntax? type; + private ArgumentListSyntax? argumentList; + + internal PrimaryConstructorBaseTypeSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public override TypeSyntax Type => GetRedAtZero(ref this.type)!; + + public ArgumentListSyntax ArgumentList => GetRed(ref this.argumentList, 1)!; internal override SyntaxNode? GetNodeSlot(int index) => index switch { 0 => GetRedAtZero(ref this.type)!, - 1 => GetRed(ref this.argumentList, 1), + 1 => GetRed(ref this.argumentList, 1)!, _ => null, }; @@ -9605,15 +9640,15 @@ internal SimpleBaseTypeSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? _ => null, }; - public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitPrimaryConstructorBaseType(this); [return: MaybeNull] - public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitSimpleBaseType(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitPrimaryConstructorBaseType(this); - public SimpleBaseTypeSyntax Update(TypeSyntax type, ArgumentListSyntax? argumentList) + public PrimaryConstructorBaseTypeSyntax Update(TypeSyntax type, ArgumentListSyntax argumentList) { if (type != this.Type || argumentList != this.ArgumentList) { - var newNode = SyntaxFactory.SimpleBaseType(type, argumentList); + var newNode = SyntaxFactory.PrimaryConstructorBaseType(type, argumentList); var annotations = GetAnnotations(); return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; } @@ -9622,14 +9657,10 @@ public SimpleBaseTypeSyntax Update(TypeSyntax type, ArgumentListSyntax? argument } internal override BaseTypeSyntax WithTypeCore(TypeSyntax type) => WithType(type); - public new SimpleBaseTypeSyntax WithType(TypeSyntax type) => Update(type, this.ArgumentList); - public SimpleBaseTypeSyntax WithArgumentList(ArgumentListSyntax? argumentList) => Update(this.Type, argumentList); + public new PrimaryConstructorBaseTypeSyntax WithType(TypeSyntax type) => Update(type, this.ArgumentList); + public PrimaryConstructorBaseTypeSyntax WithArgumentList(ArgumentListSyntax argumentList) => Update(this.Type, argumentList); - public SimpleBaseTypeSyntax AddArgumentListArguments(params ArgumentSyntax[] items) - { - var argumentList = this.ArgumentList ?? SyntaxFactory.ArgumentList(); - return WithArgumentList(argumentList.WithArguments(argumentList.Arguments.AddRange(items))); - } + public PrimaryConstructorBaseTypeSyntax AddArgumentListArguments(params ArgumentSyntax[] items) => WithArgumentList(this.ArgumentList.WithArguments(this.ArgumentList.Arguments.AddRange(items))); } /// Type parameter constraint clause. diff --git a/src/Compilers/CSharp/Portable/GlobalSuppressions.cs b/src/Compilers/CSharp/Portable/GlobalSuppressions.cs index 90b587b285b06..2f30945ffdf54 100644 --- a/src/Compilers/CSharp/Portable/GlobalSuppressions.cs +++ b/src/Compilers/CSharp/Portable/GlobalSuppressions.cs @@ -36,3 +36,5 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.DeconstructionPatternClause(Microsoft.CodeAnalysis.SeparatedSyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax})~Microsoft.CodeAnalysis.CSharp.Syntax.DeconstructionPatternClauseSyntax")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SeparatedSyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax})~Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(Microsoft.CodeAnalysis.SemanticModel,Microsoft.CodeAnalysis.CSharp.Syntax.CompilationUnitSyntax,System.Threading.CancellationToken)~Microsoft.CodeAnalysis.IMethodSymbol")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSymbolInfo(Microsoft.CodeAnalysis.SemanticModel,Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax,System.Threading.CancellationToken)~Microsoft.CodeAnalysis.SymbolInfo")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "", Scope = "member", Target = "~M:Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetMemberGroup(Microsoft.CodeAnalysis.SemanticModel,Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax,System.Threading.CancellationToken)~System.Collections.Immutable.ImmutableArray{Microsoft.CodeAnalysis.ISymbol}")] diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 78ad6ac933582..6b8ce9bac91d9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -1197,6 +1197,8 @@ private static void AppendMissingOptionalArguments( case SyntaxKind.BaseConstructorInitializer: case SyntaxKind.ThisConstructorInitializer: return new SourceLocation(((ConstructorInitializerSyntax)syntax).ArgumentList.OpenParenToken); + case SyntaxKind.PrimaryConstructorBaseType: + return new SourceLocation(((PrimaryConstructorBaseTypeSyntax)syntax).ArgumentList.OpenParenToken); case SyntaxKind.ElementAccessExpression: return new SourceLocation(((ElementAccessExpressionSyntax)syntax).ArgumentList.OpenBracketToken); case SyntaxKind.FromClause: diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 3f882b1b18ed0..53d6fe582c8be 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -1752,7 +1752,7 @@ private BaseListSyntax ParseBaseList(SyntaxToken typeKeyword, bool haveParameter } } - list.Add(_syntaxFactory.SimpleBaseType(firstType, argumentList)); + list.Add(argumentList is object ? _syntaxFactory.PrimaryConstructorBaseType(firstType, argumentList) : (BaseTypeSyntax)_syntaxFactory.SimpleBaseType(firstType)); // any additional types while (true) @@ -1766,7 +1766,7 @@ private BaseListSyntax ParseBaseList(SyntaxToken typeKeyword, bool haveParameter else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleType()) { list.AddSeparator(this.EatToken(SyntaxKind.CommaToken)); - list.Add(_syntaxFactory.SimpleBaseType(this.ParseType(), argumentList: null)); + list.Add(_syntaxFactory.SimpleBaseType(this.ParseType())); continue; } else if (this.SkipBadBaseListTokens(ref colon, list, SyntaxKind.CommaToken) == PostSkipAction.Abort) @@ -5013,7 +5013,7 @@ private EnumDeclarationSyntax ParseEnumDeclaration(SyntaxList(); - tmpList.Add(_syntaxFactory.SimpleBaseType(type, argumentList: null)); + tmpList.Add(_syntaxFactory.SimpleBaseType(type)); baseList = _syntaxFactory.BaseList(colon, tmpList); _pool.Free(tmpList); } diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index a940e60957f16..5a357a9120c6e 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,12 @@ *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.Initializer.get -> Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax *REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.ObjectCreationExpressionSyntax.NewKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.AddArgumentListArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.WithArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.WithType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.AddAttributeLists(params Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.AddBaseListTypes(params Microsoft.CodeAnalysis.CSharp.Syntax.BaseTypeSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax @@ -23,10 +29,7 @@ Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.WithOpenBraceToken( Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.WithTypeParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax.AddArgumentListArguments(params Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax.Update(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax -Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax.WithArgumentList(Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax +Microsoft.CodeAnalysis.CSharp.SyntaxKind.PrimaryConstructorBaseType = 9065 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.RecordDeclaration = 9063 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.RecordKeyword = 8444 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseObjectCreationExpressionSyntax.ArgumentList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax @@ -51,9 +54,13 @@ Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax.WithLessThanToken Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax.WithParameters(Microsoft.CodeAnalysis.SeparatedSyntaxList parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax Microsoft.CodeAnalysis.CSharp.SyntaxKind.FunctionPointerType = 9056 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitFunctionPointerType(Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitPrimaryConstructorBaseType(Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitRecordDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode override Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax.Type.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.AttributeLists.get -> Microsoft.CodeAnalysis.SyntaxList @@ -67,6 +74,9 @@ override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.Modifiers. override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.OpenBraceToken.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken override Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax.TypeParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax +static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSpeculativeSymbolInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, int position, Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax constructorInitializer) -> Microsoft.CodeAnalysis.SymbolInfo +static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetSymbolInfo(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax constructorInitializer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.SymbolInfo +static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.TryGetSpeculativeSemanticModel(this Microsoft.CodeAnalysis.SemanticModel semanticModel, int position, Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax constructorInitializer, out Microsoft.CodeAnalysis.SemanticModel speculativeModel) -> bool static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SeparatedSyntaxList parameters = default(Microsoft.CodeAnalysis.SeparatedSyntaxList)) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SyntaxToken callingConvention, Microsoft.CodeAnalysis.SeparatedSyntaxList parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FunctionPointerType(Microsoft.CodeAnalysis.SyntaxToken delegateKeyword, Microsoft.CodeAnalysis.SyntaxToken asteriskToken, Microsoft.CodeAnalysis.SyntaxToken callingConvention, Microsoft.CodeAnalysis.SyntaxToken lessThanToken, Microsoft.CodeAnalysis.SeparatedSyntaxList parameters, Microsoft.CodeAnalysis.SyntaxToken greaterThanToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax @@ -176,6 +186,8 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitObjectCreationExpress static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitObjectCreationExpression(Microsoft.CodeAnalysis.SyntaxToken newKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList, Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax initializer) -> Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseTypeName(string text, int offset = 0, Microsoft.CodeAnalysis.ParseOptions options = null, bool consumeFullText = true) -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseTypeName(string text, int offset, bool consumeFullText) -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.PrimaryConstructorBaseType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.PrimaryConstructorBaseType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RecordDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BaseListSyntax baseList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RecordDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BaseListSyntax baseList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxToken openBraceToken, Microsoft.CodeAnalysis.SyntaxList members, Microsoft.CodeAnalysis.SyntaxToken closeBraceToken, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RecordDeclaration(Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.SyntaxToken identifier) -> Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax @@ -183,12 +195,12 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RecordDeclaration(Microsoft.C static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.WithExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression, Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax initializer) -> Microsoft.CodeAnalysis.CSharp.Syntax.WithExpressionSyntax virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFunctionPointerType(Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitParenthesizedPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedPatternSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPrimaryConstructorBaseType(Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRecordDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax node) -> void virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFunctionPointerType(Microsoft.CodeAnalysis.CSharp.Syntax.FunctionPointerTypeSyntax node) -> TResult static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParenthesizedPattern(Microsoft.CodeAnalysis.SyntaxToken openParenToken, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern, Microsoft.CodeAnalysis.SyntaxToken closeParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.RelationalPattern(Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax expression) -> Microsoft.CodeAnalysis.CSharp.Syntax.RelationalPatternSyntax -static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.SimpleBaseType(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.CSharp.Syntax.ArgumentListSyntax argumentList) -> Microsoft.CodeAnalysis.CSharp.Syntax.SimpleBaseTypeSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TypePattern(Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type) -> Microsoft.CodeAnalysis.CSharp.Syntax.TypePatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.UnaryPattern(Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.UnaryPatternSyntax static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.UnaryPattern(Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.PatternSyntax pattern) -> Microsoft.CodeAnalysis.CSharp.Syntax.UnaryPatternSyntax @@ -208,6 +220,7 @@ virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitWithExpression(Mi virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitBinaryPattern(Microsoft.CodeAnalysis.CSharp.Syntax.BinaryPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitParenthesizedPattern(Microsoft.CodeAnalysis.CSharp.Syntax.ParenthesizedPatternSyntax node) -> TResult +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitPrimaryConstructorBaseType(Microsoft.CodeAnalysis.CSharp.Syntax.PrimaryConstructorBaseTypeSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRecordDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.RecordDeclarationSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitRelationalPattern(Microsoft.CodeAnalysis.CSharp.Syntax.RelationalPatternSyntax node) -> TResult virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitTypePattern(Microsoft.CodeAnalysis.CSharp.Syntax.TypePatternSyntax node) -> TResult diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index 1a024747e2ef8..33c990215fd7b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -169,9 +169,9 @@ internal static LocalSymbol MakeLocalSymbolWithEnclosingContext( nodeToBind.Kind() == SyntaxKind.CasePatternSwitchLabel || nodeToBind.Kind() == SyntaxKind.ThisConstructorInitializer || nodeToBind.Kind() == SyntaxKind.BaseConstructorInitializer || - nodeToBind.Kind() == SyntaxKind.SimpleBaseType || // initializer for a record constructor + nodeToBind.Kind() == SyntaxKind.PrimaryConstructorBaseType || // initializer for a record constructor nodeToBind.Kind() == SyntaxKind.SwitchExpressionArm || - nodeToBind.Kind() == SyntaxKind.ArgumentList && nodeToBind.Parent is ConstructorInitializerSyntax || + nodeToBind.Kind() == SyntaxKind.ArgumentList && (nodeToBind.Parent is ConstructorInitializerSyntax || nodeToBind.Parent is PrimaryConstructorBaseTypeSyntax) || nodeToBind.Kind() == SyntaxKind.GotoCaseStatement || // for error recovery nodeToBind.Kind() == SyntaxKind.VariableDeclarator && new[] { SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement, SyntaxKind.FixedStatement }. @@ -741,8 +741,8 @@ public LocalSymbolWithEnclosingContext( nodeToBind.Kind() == SyntaxKind.CasePatternSwitchLabel || nodeToBind.Kind() == SyntaxKind.ThisConstructorInitializer || nodeToBind.Kind() == SyntaxKind.BaseConstructorInitializer || - nodeToBind.Kind() == SyntaxKind.SimpleBaseType || // initializer for a record constructor - nodeToBind.Kind() == SyntaxKind.ArgumentList && nodeToBind.Parent is ConstructorInitializerSyntax || + nodeToBind.Kind() == SyntaxKind.PrimaryConstructorBaseType || // initializer for a record constructor + nodeToBind.Kind() == SyntaxKind.ArgumentList && (nodeToBind.Parent is ConstructorInitializerSyntax || nodeToBind.Parent is PrimaryConstructorBaseTypeSyntax) || nodeToBind.Kind() == SyntaxKind.VariableDeclarator || nodeToBind.Kind() == SyntaxKind.SwitchExpressionArm || nodeToBind.Kind() == SyntaxKind.GotoCaseStatement || @@ -769,12 +769,21 @@ protected override TypeWithAnnotations InferTypeOfVarVariable(DiagnosticBag diag var initializer = (ConstructorInitializerSyntax)_nodeToBind; _nodeBinder.BindConstructorInitializer(initializer, diagnostics); break; - case SyntaxKind.SimpleBaseType: - _nodeBinder.BindConstructorInitializer((SimpleBaseTypeSyntax)_nodeToBind, diagnostics); + case SyntaxKind.PrimaryConstructorBaseType: + _nodeBinder.BindConstructorInitializer((PrimaryConstructorBaseTypeSyntax)_nodeToBind, diagnostics); break; case SyntaxKind.ArgumentList: - var invocation = (ConstructorInitializerSyntax)_nodeToBind.Parent; - _nodeBinder.BindConstructorInitializer(invocation, diagnostics); + switch (_nodeToBind.Parent) + { + case ConstructorInitializerSyntax ctorInitializer: + _nodeBinder.BindConstructorInitializer(ctorInitializer, diagnostics); + break; + case PrimaryConstructorBaseTypeSyntax ctorInitializer: + _nodeBinder.BindConstructorInitializer(ctorInitializer, diagnostics); + break; + default: + throw ExceptionUtilities.UnexpectedValue(_nodeToBind.Parent); + } break; case SyntaxKind.CasePatternSwitchLabel: _nodeBinder.BindPatternSwitchLabelForInference((CasePatternSwitchLabelSyntax)_nodeToBind, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordConstructor.cs index 733a516bba650..3d083b323c07c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordConstructor.cs @@ -33,14 +33,7 @@ internal RecordDeclarationSyntax GetSyntax() protected override CSharpSyntaxNode? GetInitializer() { - var baseTypeSyntax = GetSyntax().BaseList?.Types.FirstOrDefault() as SimpleBaseTypeSyntax; - - if (baseTypeSyntax?.ArgumentList is object) - { - return baseTypeSyntax; - } - - return null; + return GetSyntax().PrimaryConstructorBaseType; } internal override bool IsExpressionBodied diff --git a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs index 697435103fca4..c22abae528445 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs @@ -404,8 +404,6 @@ internal static bool IsClosureScope(SyntaxNode node) case SyntaxKind.IfStatement: case SyntaxKind.LockStatement: case SyntaxKind.ReturnStatement: - case SyntaxKind.ThisConstructorInitializer: - case SyntaxKind.BaseConstructorInitializer: case SyntaxKind.ThrowStatement: case SyntaxKind.WhileStatement: case SyntaxKind.YieldReturnStatement: diff --git a/src/Compilers/CSharp/Portable/Syntax/RecordDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/RecordDeclarationSyntax.cs index 19ac288a66fd9..0e2ab8a4a75d9 100644 --- a/src/Compilers/CSharp/Portable/Syntax/RecordDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/RecordDeclarationSyntax.cs @@ -8,16 +8,11 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class RecordDeclarationSyntax { - internal SimpleBaseTypeSyntax? BaseWithArguments + internal PrimaryConstructorBaseTypeSyntax? PrimaryConstructorBaseType { get { - if (BaseList?.Types.FirstOrDefault() is SimpleBaseTypeSyntax { ArgumentList: { } } firstBase) - { - return firstBase; - } - - return null; + return BaseList?.Types.FirstOrDefault() as PrimaryConstructorBaseTypeSyntax; } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SimpleBaseTypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/SimpleBaseTypeSyntax.cs deleted file mode 100644 index f6c8032094ff5..0000000000000 --- a/src/Compilers/CSharp/Portable/Syntax/SimpleBaseTypeSyntax.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable enable - -namespace Microsoft.CodeAnalysis.CSharp.Syntax -{ - public partial class SimpleBaseTypeSyntax - { - public SimpleBaseTypeSyntax Update(TypeSyntax type) - => Update(type, argumentList: null); - } -} diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index f7e0fecb2c6d6..1549c7356df01 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -3451,9 +3451,14 @@ - + + + + + + Type parameter constraint clause. diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs index e2132deefc706..77d1b868cc6db 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxExtensions.cs @@ -250,6 +250,7 @@ private static bool IsInContextWhichNeedsDynamicAttribute(CSharpSyntaxNode node) case SyntaxKind.EventFieldDeclaration: case SyntaxKind.BaseList: case SyntaxKind.SimpleBaseType: + case SyntaxKind.PrimaryConstructorBaseType: return true; case SyntaxKind.Block: diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs index c7ecb7a3edfdd..d6794e6184813 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs @@ -186,6 +186,9 @@ public static bool IsInTypeOnlyContext(ExpressionSyntax node) case SimpleBaseType: return true; + case PrimaryConstructorBaseType: + return ((PrimaryConstructorBaseTypeSyntax)parent).Type == node; + case CrefParameter: return true; @@ -313,6 +316,7 @@ public static bool IsNamedArgumentName(SyntaxNode node) case Attribute: case BaseConstructorInitializer: case ThisConstructorInitializer: + case PrimaryConstructorBaseType: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index b25aa6cbd8093..fde6f519abf04 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -621,6 +621,8 @@ public enum SyntaxKind : ushort WithExpression = 9061, WithInitializerExpression = 9062, - RecordDeclaration = 9063 + RecordDeclaration = 9063, + + PrimaryConstructorBaseType = 9065, } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index 9651b6d3d119b..5dab21460a44a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -78,14 +78,11 @@ internal static bool CanHaveAssociatedLocalBinder(this SyntaxNode syntax) case SyntaxKind.BaseConstructorInitializer: case SyntaxKind.ThisConstructorInitializer: case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.PrimaryConstructorBaseType: return true; case SyntaxKind.RecordDeclaration: return ((RecordDeclarationSyntax)syntax).ParameterList is object; - case SyntaxKind.SimpleBaseType: - return ((SimpleBaseTypeSyntax)syntax).ArgumentList is object && - syntax.Parent?.Parent is RecordDeclarationSyntax recordDecl && - recordDecl.ParameterList is object && recordDecl.BaseWithArguments == syntax; default: return syntax is StatementSyntax || IsValidScopeDesignator(syntax as ExpressionSyntax); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index ca68d91577e0d..2627940b0f6d1 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -4,6 +4,7 @@ #nullable enable +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -7033,6 +7034,8 @@ public static void Main() var c = new C(1, 2); Console.WriteLine(c.Z); } + + C(int X, int Y, int Z) : this(X, Y) {} }"; var verifier = CompileAndVerify(src, expectedOutput: @" 1 @@ -7077,10 +7080,42 @@ .maxstack 3 Assert.Contains(symbol, model.LookupSymbols(x.SpanStart, name: "X")); Assert.Contains("X", model.LookupNames(x.SpanStart)); - var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); - Assert.Equal("Base(X, Y)", baseWithargs.ToString()); - Assert.Null(model.GetTypeInfo(baseWithargs).Type); - Assert.Null(model.GetSymbolInfo(baseWithargs).Symbol); + { + var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("Base(X, Y)", baseWithargs.ToString()); + Assert.Equal("Base", model.GetTypeInfo(baseWithargs.Type).Type.ToTestDisplayString()); + Assert.Equal(TypeInfo.None, model.GetTypeInfo(baseWithargs)); + Assert.Equal("Base..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo((SyntaxNode)baseWithargs).Symbol.ToTestDisplayString()); + Assert.Equal("Base..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo(baseWithargs).Symbol.ToTestDisplayString()); + Assert.Equal("Base..ctor(System.Int32 X, System.Int32 Y)", CSharpExtensions.GetSymbolInfo(model, baseWithargs).Symbol.ToTestDisplayString()); + + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs)); + Assert.Empty(model.GetMemberGroup(baseWithargs)); + + model = comp.GetSemanticModel(tree); + Assert.Equal("Base..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo((SyntaxNode)baseWithargs).Symbol.ToTestDisplayString()); + model = comp.GetSemanticModel(tree); + Assert.Equal("Base..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo(baseWithargs).Symbol.ToTestDisplayString()); + model = comp.GetSemanticModel(tree); + Assert.Equal("Base..ctor(System.Int32 X, System.Int32 Y)", CSharpExtensions.GetSymbolInfo(model, baseWithargs).Symbol.ToTestDisplayString()); + + model = comp.GetSemanticModel(tree); + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs)); + model = comp.GetSemanticModel(tree); + Assert.Empty(model.GetMemberGroup(baseWithargs)); + model = comp.GetSemanticModel(tree); + } + { + var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(": this(X, Y)", baseWithargs.ToString()); + Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo((SyntaxNode)baseWithargs).Symbol.ToTestDisplayString()); + Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", model.GetSymbolInfo(baseWithargs).Symbol.ToTestDisplayString()); + Assert.Equal("C..ctor(System.Int32 X, System.Int32 Y)", CSharpExtensions.GetSymbolInfo(model, baseWithargs).Symbol.ToTestDisplayString()); + + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs).Select(m => m.ToTestDisplayString())); + Assert.Empty(model.GetMemberGroup(baseWithargs).Select(m => m.ToTestDisplayString())); + Assert.Empty(CSharpExtensions.GetMemberGroup(model, baseWithargs).Select(m => m.ToTestDisplayString())); + } } [Fact] @@ -7825,6 +7860,327 @@ private static int Test(int x, out int y) ); } + [Fact] + public void BaseArguments_19() + { + var src = @" +record Base +{ + public Base(int X) + { + } + + public Base() {} +} + +record C(int X, int Y) : Base(GetInt(X, out var xx) + xx, Y), I +{ + C(int X, int Y, int Z) : this(X, Y, Z, 1) { return; } + + static int GetInt(int x1, out int x2) + { + throw null; + } +} + +interface I {} +"; + + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (11,30): error CS1729: 'Base' does not contain a constructor that takes 2 arguments + // record C(int X, int Y) : Base(GetInt(X, out var xx) + xx, Y) + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "(GetInt(X, out var xx) + xx, Y)").WithArguments("Base", "2").WithLocation(11, 30), + // (13,30): error CS1729: 'C' does not contain a constructor that takes 4 arguments + // C(int X, int Y, int Z) : this(X, Y, Z, 1) {} + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "this").WithArguments("C", "4").WithLocation(13, 30) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + SymbolInfo symbolInfo; + PrimaryConstructorBaseTypeSyntax speculativePrimaryInitializer; + ConstructorInitializerSyntax speculativeBaseInitializer; + + { + var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("Base(GetInt(X, out var xx) + xx, Y)", baseWithargs.ToString()); + Assert.Equal("Base", model.GetTypeInfo(baseWithargs.Type).Type.ToTestDisplayString()); + Assert.Equal(TypeInfo.None, model.GetTypeInfo(baseWithargs)); + symbolInfo = model.GetSymbolInfo((SyntaxNode)baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + string[] candidates = new[] { "Base..ctor(System.Int32 X)", "Base..ctor()", "Base..ctor(Base )" }; + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + symbolInfo = model.GetSymbolInfo(baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + symbolInfo = CSharpExtensions.GetSymbolInfo(model, baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs)); + Assert.Empty(model.GetMemberGroup(baseWithargs)); + + model = comp.GetSemanticModel(tree); + symbolInfo = model.GetSymbolInfo((SyntaxNode)baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + model = comp.GetSemanticModel(tree); + symbolInfo = model.GetSymbolInfo(baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + model = comp.GetSemanticModel(tree); + symbolInfo = CSharpExtensions.GetSymbolInfo(model, baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + + model = comp.GetSemanticModel(tree); + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs)); + model = comp.GetSemanticModel(tree); + Assert.Empty(model.GetMemberGroup(baseWithargs)); + model = comp.GetSemanticModel(tree); + + SemanticModel speculativeModel; + speculativePrimaryInitializer = baseWithargs.WithArgumentList(baseWithargs.ArgumentList.WithArguments(baseWithargs.ArgumentList.Arguments.RemoveAt(1))); + + speculativeBaseInitializer = SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, speculativePrimaryInitializer.ArgumentList); + Assert.False(model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativeBaseInitializer, out _)); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativeBaseInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativeBaseInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + Assert.False(model.TryGetSpeculativeSemanticModel(tree.GetRoot().DescendantNodes().OfType().Single().SpanStart, + speculativeBaseInitializer, out _)); + + var otherBasePosition = ((BaseListSyntax)baseWithargs.Parent!).Types[1].SpanStart; + Assert.False(model.TryGetSpeculativeSemanticModel(otherBasePosition, speculativePrimaryInitializer, out _)); + + Assert.True(model.TryGetSpeculativeSemanticModel(baseWithargs.SpanStart, speculativePrimaryInitializer, out speculativeModel!)); + Assert.Equal("Base..ctor(System.Int32 X)", speculativeModel!.GetSymbolInfo((SyntaxNode)speculativePrimaryInitializer).Symbol.ToTestDisplayString()); + Assert.Equal("Base..ctor(System.Int32 X)", speculativeModel.GetSymbolInfo(speculativePrimaryInitializer).Symbol.ToTestDisplayString()); + Assert.Equal("Base..ctor(System.Int32 X)", CSharpExtensions.GetSymbolInfo(speculativeModel, speculativePrimaryInitializer).Symbol.ToTestDisplayString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer, out speculativeModel!)); + + var xxDecl = OutVarTests.GetOutVarDeclaration(speculativePrimaryInitializer.SyntaxTree, "xx"); + var xxRef = OutVarTests.GetReferences(speculativePrimaryInitializer.SyntaxTree, "xx").ToArray(); + Assert.Equal(1, xxRef.Length); + OutVarTests.VerifyModelForOutVar(speculativeModel, xxDecl, xxRef); + + Assert.Equal("Base..ctor(System.Int32 X)", speculativeModel!.GetSymbolInfo((SyntaxNode)speculativePrimaryInitializer).Symbol.ToTestDisplayString()); + Assert.Equal("Base..ctor(System.Int32 X)", speculativeModel.GetSymbolInfo(speculativePrimaryInitializer).Symbol.ToTestDisplayString()); + Assert.Equal("Base..ctor(System.Int32 X)", CSharpExtensions.GetSymbolInfo(speculativeModel, speculativePrimaryInitializer).Symbol.ToTestDisplayString()); + + Assert.Throws(() => model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (PrimaryConstructorBaseTypeSyntax)null!, out _)); + Assert.Throws(() => model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, baseWithargs, out _)); + + symbolInfo = model.GetSpeculativeSymbolInfo(otherBasePosition, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, otherBasePosition, speculativePrimaryInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.SpanStart, speculativePrimaryInitializer); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + Assert.Equal(TypeInfo.None, model.GetSpeculativeTypeInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression)); + Assert.Equal(TypeInfo.None, model.GetSpeculativeTypeInfo(tree.GetRoot().DescendantNodes().OfType().Single().ArgumentList.OpenParenToken.SpanStart, + (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression)); + } + { + var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(": this(X, Y, Z, 1)", baseWithargs.ToString()); + symbolInfo = model.GetSymbolInfo((SyntaxNode)baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + string[] candidates = new[] { "C..ctor(System.Int32 X, System.Int32 Y, System.Int32 Z)", "C..ctor(System.Int32 X, System.Int32 Y)", "C..ctor(C )" }; + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + symbolInfo = model.GetSymbolInfo(baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + symbolInfo = CSharpExtensions.GetSymbolInfo(model, baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs).Select(m => m.ToTestDisplayString())); + Assert.Empty(model.GetMemberGroup(baseWithargs).Select(m => m.ToTestDisplayString())); + Assert.Empty(CSharpExtensions.GetMemberGroup(model, baseWithargs).Select(m => m.ToTestDisplayString())); + + Assert.False(model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer, out _)); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativeBaseInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativeBaseInitializer); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + Assert.Equal(TypeInfo.None, model.GetSpeculativeTypeInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression)); + } + } + + [Fact] + public void BaseArguments_20() + { + var src = @" +class Base +{ + public Base(int X) + { + } + + public Base() {} +} + +class C : Base(GetInt(X, out var xx) + xx, Y), I +{ + C(int X, int Y, int Z) : base(X, Y, Z, 1) { return; } + + static int GetInt(int x1, out int x2) + { + throw null; + } +} + +interface I {} +"; + + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (11,15): error CS8861: Unexpected argument list. + // class C : Base(GetInt(X, out var xx) + xx, Y), I + Diagnostic(ErrorCode.ERR_UnexpectedArgumentList, "(").WithLocation(11, 15), + // (13,30): error CS1729: 'Base' does not contain a constructor that takes 4 arguments + // C(int X, int Y, int Z) : base(X, Y, Z, 1) { return; } + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "base").WithArguments("Base", "4").WithLocation(13, 30) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + SymbolInfo symbolInfo; + PrimaryConstructorBaseTypeSyntax speculativePrimaryInitializer; + ConstructorInitializerSyntax speculativeBaseInitializer; + + { + var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("Base(GetInt(X, out var xx) + xx, Y)", baseWithargs.ToString()); + Assert.Equal("Base", model.GetTypeInfo(baseWithargs.Type).Type.ToTestDisplayString()); + Assert.Equal(TypeInfo.None, model.GetTypeInfo(baseWithargs)); + symbolInfo = model.GetSymbolInfo((SyntaxNode)baseWithargs); + Assert.Equal(SymbolInfo.None, symbolInfo); + symbolInfo = model.GetSymbolInfo(baseWithargs); + Assert.Equal(SymbolInfo.None, symbolInfo); + symbolInfo = CSharpExtensions.GetSymbolInfo(model, baseWithargs); + Assert.Equal(SymbolInfo.None, symbolInfo); + + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs)); + Assert.Empty(model.GetMemberGroup(baseWithargs)); + + speculativePrimaryInitializer = baseWithargs.WithArgumentList(baseWithargs.ArgumentList.WithArguments(baseWithargs.ArgumentList.Arguments.RemoveAt(1))); + + speculativeBaseInitializer = SyntaxFactory.ConstructorInitializer(SyntaxKind.BaseConstructorInitializer, speculativePrimaryInitializer.ArgumentList); + Assert.False(model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativeBaseInitializer, out _)); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativeBaseInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativeBaseInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + Assert.False(model.TryGetSpeculativeSemanticModel(tree.GetRoot().DescendantNodes().OfType().Single().SpanStart, + speculativeBaseInitializer, out _)); + + var otherBasePosition = ((BaseListSyntax)baseWithargs.Parent!).Types[1].SpanStart; + Assert.False(model.TryGetSpeculativeSemanticModel(otherBasePosition, speculativePrimaryInitializer, out _)); + + Assert.False(model.TryGetSpeculativeSemanticModel(baseWithargs.SpanStart, speculativePrimaryInitializer, out _)); + Assert.False(model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer, out _)); + + Assert.Throws(() => model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (PrimaryConstructorBaseTypeSyntax)null!, out _)); + Assert.Throws(() => model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, baseWithargs, out _)); + + symbolInfo = model.GetSpeculativeSymbolInfo(otherBasePosition, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, otherBasePosition, speculativePrimaryInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.SpanStart, speculativePrimaryInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + Assert.Equal(TypeInfo.None, model.GetSpeculativeTypeInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression)); + Assert.Equal(TypeInfo.None, model.GetSpeculativeTypeInfo(tree.GetRoot().DescendantNodes().OfType().Single().ArgumentList.OpenParenToken.SpanStart, + (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression)); + } + { + var baseWithargs = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal(": base(X, Y, Z, 1)", baseWithargs.ToString()); + symbolInfo = model.GetSymbolInfo((SyntaxNode)baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + string[] candidates = new[] { "Base..ctor(System.Int32 X)", "Base..ctor()" }; + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + symbolInfo = model.GetSymbolInfo(baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + symbolInfo = CSharpExtensions.GetSymbolInfo(model, baseWithargs); + Assert.Null(symbolInfo.Symbol); + Assert.Equal(CandidateReason.OverloadResolutionFailure, symbolInfo.CandidateReason); + Assert.Equal(candidates, symbolInfo.CandidateSymbols.Select(m => m.ToTestDisplayString())); + + Assert.Empty(model.GetMemberGroup((SyntaxNode)baseWithargs).Select(m => m.ToTestDisplayString())); + Assert.Empty(model.GetMemberGroup(baseWithargs).Select(m => m.ToTestDisplayString())); + Assert.Empty(CSharpExtensions.GetMemberGroup(model, baseWithargs).Select(m => m.ToTestDisplayString())); + + Assert.False(model.TryGetSpeculativeSemanticModel(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer, out _)); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativePrimaryInitializer); + Assert.Equal(SymbolInfo.None, symbolInfo); + + symbolInfo = model.GetSpeculativeSymbolInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativeBaseInitializer, SpeculativeBindingOption.BindAsExpression); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + symbolInfo = CSharpExtensions.GetSpeculativeSymbolInfo(model, baseWithargs.ArgumentList.OpenParenToken.SpanStart, speculativeBaseInitializer); + Assert.Equal("Base..ctor(System.Int32 X)", symbolInfo.Symbol.ToTestDisplayString()); + + Assert.Equal(TypeInfo.None, model.GetSpeculativeTypeInfo(baseWithargs.ArgumentList.OpenParenToken.SpanStart, (SyntaxNode)speculativePrimaryInitializer, SpeculativeBindingOption.BindAsExpression)); + } + } + [Fact(Skip = "record struct")] public void Equality_01() { diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index e7080f3e3a947..f66303e94ff84 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -2145,7 +2145,7 @@ void M(int x) SemanticModel speculativeModel; Assert.Throws(() => model.TryGetSpeculativeSemanticModel(statement.SpanStart, statement: null, speculativeModel: out speculativeModel)); - Assert.Throws(() => model.TryGetSpeculativeSemanticModel(statement.SpanStart, constructorInitializer: null, speculativeModel: out speculativeModel)); + Assert.Throws(() => model.TryGetSpeculativeSemanticModel(statement.SpanStart, constructorInitializer: (ConstructorInitializerSyntax)null, speculativeModel: out speculativeModel)); Assert.Throws(() => model.TryGetSpeculativeSemanticModel(statement.SpanStart, attribute: null, speculativeModel: out speculativeModel)); // Speculate on a node from the same syntax tree. diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index 68752b7c4a6ab..1fe6a5b00fce9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -485,7 +485,10 @@ private static Syntax.InternalSyntax.BaseListSyntax GenerateBaseList() => InternalSyntaxFactory.BaseList(InternalSyntaxFactory.Token(SyntaxKind.ColonToken), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList()); private static Syntax.InternalSyntax.SimpleBaseTypeSyntax GenerateSimpleBaseType() - => InternalSyntaxFactory.SimpleBaseType(GenerateIdentifierName(), null); + => InternalSyntaxFactory.SimpleBaseType(GenerateIdentifierName()); + + private static Syntax.InternalSyntax.PrimaryConstructorBaseTypeSyntax GeneratePrimaryConstructorBaseType() + => InternalSyntaxFactory.PrimaryConstructorBaseType(GenerateIdentifierName(), GenerateArgumentList()); private static Syntax.InternalSyntax.TypeParameterConstraintClauseSyntax GenerateTypeParameterConstraintClause() => InternalSyntaxFactory.TypeParameterConstraintClause(InternalSyntaxFactory.Token(SyntaxKind.WhereKeyword), GenerateIdentifierName(), InternalSyntaxFactory.Token(SyntaxKind.ColonToken), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList()); @@ -2690,7 +2693,17 @@ public void TestSimpleBaseTypeFactoryAndProperties() var node = GenerateSimpleBaseType(); Assert.NotNull(node.Type); - Assert.Null(node.ArgumentList); + + AttachAndCheckDiagnostics(node); + } + + [Fact] + public void TestPrimaryConstructorBaseTypeFactoryAndProperties() + { + var node = GeneratePrimaryConstructorBaseType(); + + Assert.NotNull(node.Type); + Assert.NotNull(node.ArgumentList); AttachAndCheckDiagnostics(node); } @@ -7700,6 +7713,32 @@ public void TestSimpleBaseTypeIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestPrimaryConstructorBaseTypeTokenDeleteRewriter() + { + var oldNode = GeneratePrimaryConstructorBaseType(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestPrimaryConstructorBaseTypeIdentityRewriter() + { + var oldNode = GeneratePrimaryConstructorBaseType(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestTypeParameterConstraintClauseTokenDeleteRewriter() { @@ -9870,7 +9909,10 @@ private static BaseListSyntax GenerateBaseList() => SyntaxFactory.BaseList(SyntaxFactory.Token(SyntaxKind.ColonToken), new SeparatedSyntaxList()); private static SimpleBaseTypeSyntax GenerateSimpleBaseType() - => SyntaxFactory.SimpleBaseType(GenerateIdentifierName(), default(ArgumentListSyntax)); + => SyntaxFactory.SimpleBaseType(GenerateIdentifierName()); + + private static PrimaryConstructorBaseTypeSyntax GeneratePrimaryConstructorBaseType() + => SyntaxFactory.PrimaryConstructorBaseType(GenerateIdentifierName(), GenerateArgumentList()); private static TypeParameterConstraintClauseSyntax GenerateTypeParameterConstraintClause() => SyntaxFactory.TypeParameterConstraintClause(SyntaxFactory.Token(SyntaxKind.WhereKeyword), GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.ColonToken), new SeparatedSyntaxList()); @@ -12075,7 +12117,17 @@ public void TestSimpleBaseTypeFactoryAndProperties() var node = GenerateSimpleBaseType(); Assert.NotNull(node.Type); - Assert.Null(node.ArgumentList); + var newNode = node.WithType(node.Type); + Assert.Equal(node, newNode); + } + + [Fact] + public void TestPrimaryConstructorBaseTypeFactoryAndProperties() + { + var node = GeneratePrimaryConstructorBaseType(); + + Assert.NotNull(node.Type); + Assert.NotNull(node.ArgumentList); var newNode = node.WithType(node.Type).WithArgumentList(node.ArgumentList); Assert.Equal(node, newNode); } @@ -17085,6 +17137,32 @@ public void TestSimpleBaseTypeIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestPrimaryConstructorBaseTypeTokenDeleteRewriter() + { + var oldNode = GeneratePrimaryConstructorBaseType(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestPrimaryConstructorBaseTypeIdentityRewriter() + { + var oldNode = GeneratePrimaryConstructorBaseType(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestTypeParameterConstraintClauseTokenDeleteRewriter() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs index e18ae00885564..60c4e35205323 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/RecordParsing.cs @@ -1499,7 +1499,7 @@ public void Base_01( N(SyntaxKind.BaseList); { N(SyntaxKind.ColonToken); - N(SyntaxKind.SimpleBaseType); + N(withBaseArguments ? SyntaxKind.PrimaryConstructorBaseType : SyntaxKind.SimpleBaseType); { N(SyntaxKind.IdentifierName); { @@ -1797,7 +1797,7 @@ public void Base_05() N(SyntaxKind.BaseList); { N(SyntaxKind.ColonToken); - N(SyntaxKind.SimpleBaseType); + N(SyntaxKind.PrimaryConstructorBaseType); { N(SyntaxKind.IdentifierName); { From 9a658fab0803051491fa888bbed5a6668b8874e3 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Tue, 23 Jun 2020 16:33:42 -0700 Subject: [PATCH 22/22] Annotate more public syntax APIs (#44266) --- .../Portable/Binder/Binder_Attributes.cs | 3 +- .../Symbols/Attributes/AttributeData.cs | 15 ++--- .../Symbols/TypedConstantExtensions.cs | 51 +++++++++------- .../Syntax/AliasedQualifiedNameSyntax.cs | 2 + .../AnonymousFunctionExpressionSyntax.cs | 4 +- .../CSharp/Portable/Syntax/ArgumentSyntax.cs | 2 + .../Syntax/ArrayRankSpecifierSyntax.cs | 2 + .../CSharp/Portable/Syntax/AttributeSyntax.cs | 4 +- .../Syntax/AttributeTargetSpecifierSyntax.cs | 2 + .../CSharp/Portable/Syntax/BlockSyntax.cs | 2 + .../Portable/Syntax/BreakStatementSyntax.cs | 2 + .../Portable/Syntax/CSharpLineDirectiveMap.cs | 6 +- .../Syntax/CSharpPragmaWarningStateMap.cs | 4 +- .../Portable/Syntax/CSharpSyntaxNode.cs | 2 +- .../Portable/Syntax/CSharpSyntaxRewriter.cs | 2 + .../Portable/Syntax/CSharpSyntaxTree.Dummy.cs | 2 + .../CSharpSyntaxTree.ParsedSyntaxTree.cs | 19 +++--- .../Portable/Syntax/CheckedStatementSyntax.cs | 2 + .../Syntax/ClassOrStructConstraintSyntax.cs | 2 + .../Portable/Syntax/CompilationUnitSyntax.cs | 4 +- .../Syntax/ConstructorDeclarationSyntax.cs | 2 + .../Syntax/ContinueStatementSyntax.cs | 2 + .../Portable/Syntax/CrefParameterSyntax.cs | 2 + .../Syntax/DeclarationStatementSyntax.cs | 2 + .../Syntax/DelegateDeclarationSyntax.cs | 2 + .../Syntax/DestructorDeclarationSyntax.cs | 2 + .../Portable/Syntax/DirectiveTriviaSyntax.cs | 26 ++++---- .../Portable/Syntax/DoStatementSyntax.cs | 2 + .../Portable/Syntax/EmptyStatementSyntax.cs | 2 + .../Syntax/EnumMemberDeclarationSyntax.cs | 2 + .../Portable/Syntax/EventDeclarationSyntax.cs | 2 + .../Syntax/ExpressionStatementSyntax.cs | 2 + .../Portable/Syntax/FixedStatementSyntax.cs | 2 + .../Portable/Syntax/ForEachStatementSyntax.cs | 2 + .../Syntax/ForEachVariableStatementSyntax.cs | 2 + .../Portable/Syntax/ForStatementSyntax.cs | 2 + .../Portable/Syntax/GenericNameSyntax.cs | 2 + .../Portable/Syntax/GlobalStatementSyntax.cs | 2 + .../Portable/Syntax/GotoStatementSyntax.cs | 2 + .../Portable/Syntax/IdentifierNameSyntax.cs | 2 + .../Portable/Syntax/IfStatementSyntax.cs | 2 + .../Syntax/IndexerDeclarationSyntax.cs | 2 + .../Portable/Syntax/LabeledStatementSyntax.cs | 2 + .../CSharp/Portable/Syntax/LambdaUtilities.cs | 19 ++++-- .../Syntax/LocalDeclarationStatementSyntax.cs | 2 + .../Syntax/LocalFunctionStatementSyntax.cs | 2 + .../Portable/Syntax/LockStatementSyntax.cs | 2 + .../CSharp/Portable/Syntax/LookupPosition.cs | 16 ++--- .../Syntax/MethodDeclarationSyntax.cs | 2 + .../CSharp/Portable/Syntax/NameSyntax.cs | 4 +- .../Syntax/NamespaceDeclarationSyntax.cs | 2 + .../NamespaceDeclarationSyntaxReference.cs | 2 + .../Syntax/NullableContextStateMap.cs | 2 + .../Portable/Syntax/ParameterListSyntax.cs | 2 + .../CSharp/Portable/Syntax/ParameterSyntax.cs | 2 + .../ParenthesizedLambdaExpressionSyntax.cs | 2 + .../Syntax/PropertyDeclarationSyntax.cs | 2 + .../Portable/Syntax/QualifiedNameSyntax.cs | 2 + .../CSharp/Portable/Syntax/RefTypeSyntax.cs | 9 ++- .../Portable/Syntax/ReturnStatementSyntax.cs | 2 + .../Portable/Syntax/SimpleNameSyntax.cs | 2 + .../Portable/Syntax/SimpleSyntaxReference.cs | 3 +- .../Syntax/SkippedTokensTriviaSyntax.cs | 2 + ...StackAllocArrayCreationExpressionSyntax.cs | 2 + .../Portable/Syntax/SwitchStatementSyntax.cs | 2 + .../Portable/Syntax/SyntaxKindExtensions.cs | 2 + .../Portable/Syntax/SyntaxNodeExtensions.cs | 14 +++-- .../Portable/Syntax/SyntaxNodeRemover.cs | 26 ++++---- .../Portable/Syntax/SyntaxNormalizer.cs | 26 ++++---- .../CSharp/Portable/Syntax/SyntaxReplacer.cs | 60 ++++++++++--------- .../Syntax/SyntaxTreeDiagnosticEnumerator.cs | 11 ++-- .../Portable/Syntax/ThrowStatementSyntax.cs | 2 + .../Portable/Syntax/TryStatementSyntax.cs | 2 + .../Portable/Syntax/TypeDeclarationSyntax.cs | 3 - .../CSharp/Portable/Syntax/TypeSyntax.cs | 2 + .../Portable/Syntax/UnsafeStatementSyntax.cs | 2 + .../Portable/Syntax/UsingStatementSyntax.cs | 2 + .../Portable/Syntax/WhileStatementSyntax.cs | 2 + .../Syntax/XmlNameAttributeElementKind.cs | 2 + .../Portable/Syntax/YieldStatementSyntax.cs | 2 + .../Test/Emit/Attributes/AttributeTests.cs | 21 +++++++ .../Attributes/AttributeTests_Security.cs | 36 +++++++++++ .../Symbols/Attributes/CommonAttributeData.cs | 11 ++-- .../Core/Portable/Symbols/TypedConstant.cs | 22 +++---- .../LineDirectiveMap.LineMappingEntry.cs | 2 +- .../Core/Portable/Syntax/SyntaxNavigator.cs | 34 +++++------ .../Core/Portable/Syntax/SyntaxNode.cs | 2 +- .../Portable/Syntax/SyntaxNodeExtensions.cs | 8 +-- .../Core/Portable/Syntax/SyntaxTree.cs | 2 +- .../Semantic/Binding/BindingErrorTests.vb | 6 ++ .../CSharpEditAndContinueAnalyzer.cs | 4 +- ...SwitchCodeRefactoringProvider.Rewriting.cs | 2 + .../AbstractEditAndContinueAnalyzer.cs | 4 +- .../Rules/SuppressFormattingRule.cs | 2 +- 94 files changed, 419 insertions(+), 182 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index d041478f85fc2..4d8b7aaca042c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -666,7 +666,7 @@ private ImmutableArray GetRewrittenAttributeConstructorArguments( } else if (reorderedArgument.Kind == TypedConstantKind.Array && parameter.Type.TypeKind == TypeKind.Array && - !((TypeSymbol)reorderedArgument.TypeInternal).Equals(parameter.Type, TypeCompareKind.AllIgnoreOptions)) + !((TypeSymbol)reorderedArgument.TypeInternal!).Equals(parameter.Type, TypeCompareKind.AllIgnoreOptions)) { // NOTE: As in dev11, we don't allow array covariance conversions (presumably, we don't have a way to // represent the conversion in metadata). @@ -922,6 +922,7 @@ private static bool TryGetNormalParamValue(ParameterSymbol parameter, ImmutableA } HashSet? useSiteDiagnostics = null; // ignoring, since already bound argument and parameter + Debug.Assert(argument.TypeInternal is object); Conversion conversion = conversions.ClassifyBuiltInConversion((TypeSymbol)argument.TypeInternal, parameter.Type, ref useSiteDiagnostics); // NOTE: Won't always succeed, even though we've performed overload resolution. diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs index 3b7206d7ed084..7c6061f946cee 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/AttributeData.cs @@ -370,8 +370,8 @@ private DeclarativeSecurityAction DecodeSecurityAttributeAction(Symbol targetSym else { TypedConstant firstArg = ctorArgs.First(); - TypeSymbol firstArgType = (TypeSymbol)firstArg.TypeInternal; - if ((object)firstArgType != null && firstArgType.Equals(compilation.GetWellKnownType(WellKnownType.System_Security_Permissions_SecurityAction))) + var firstArgType = (TypeSymbol?)firstArg.TypeInternal; + if (firstArgType is object && firstArgType.Equals(compilation.GetWellKnownType(WellKnownType.System_Security_Permissions_SecurityAction))) { return DecodeSecurityAction(firstArg, targetSymbol, nodeOpt, diagnostics, out hasErrors); } @@ -387,6 +387,7 @@ private DeclarativeSecurityAction DecodeSecurityAction(TypedConstant typedValue, { Debug.Assert((object)targetSymbol != null); Debug.Assert(targetSymbol.Kind == SymbolKind.Assembly || targetSymbol.Kind == SymbolKind.NamedType || targetSymbol.Kind == SymbolKind.Method); + Debug.Assert(typedValue.ValueInternal is object); int securityAction = (int)typedValue.ValueInternal; bool isPermissionRequestAction; @@ -527,15 +528,15 @@ private static Location GetSecurityAttributeActionSyntaxLocation(AttributeSyntax PermissionSetAttributeTypeHasRequiredProperty(attrType, filePropName)) { // resolve file prop path - var fileName = (string)namedArg.Value.ValueInternal; + var fileName = (string?)namedArg.Value.ValueInternal; var resolver = compilation.Options.XmlReferenceResolver; - resolvedFilePath = (resolver != null) ? resolver.ResolveReference(fileName, baseFilePath: null) : null; + resolvedFilePath = (resolver != null && fileName != null) ? resolver.ResolveReference(fileName, baseFilePath: null) : null; if (resolvedFilePath == null) { // CS7053: Unable to resolve file path '{0}' specified for the named argument '{1}' for PermissionSet attribute - Location argSyntaxLocation = nodeOpt != null ? nodeOpt.GetNamedArgumentSyntax(filePropName).Location : NoLocation.Singleton; + Location argSyntaxLocation = nodeOpt?.GetNamedArgumentSyntax(filePropName)?.Location ?? NoLocation.Singleton; diagnostics.Add(ErrorCode.ERR_PermissionSetAttributeInvalidFile, argSyntaxLocation, fileName ?? "", filePropName); } else if (!PermissionSetAttributeTypeHasRequiredProperty(attrType, hexPropName)) @@ -629,7 +630,7 @@ internal string DecodeGuidAttribute(AttributeSyntax? nodeOpt, DiagnosticBag diag { Debug.Assert(!this.HasErrors); - var guidString = (string)this.CommonConstructorArguments[0].ValueInternal; + var guidString = (string?)this.CommonConstructorArguments[0].ValueInternal; // Native compiler allows only a specific GUID format: "D" format (32 digits separated by hyphens) Guid guid; @@ -641,7 +642,7 @@ internal string DecodeGuidAttribute(AttributeSyntax? nodeOpt, DiagnosticBag diag guidString = String.Empty; } - return guidString; + return guidString!; } private protected sealed override bool IsStringProperty(string memberName) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypedConstantExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypedConstantExtensions.cs index ec4acfc67ce15..9d6493b07ed7e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypedConstantExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypedConstantExtensions.cs @@ -2,18 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Collections.Immutable; +#nullable enable + using System.Diagnostics; using System.Linq; using System.Text; -using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -35,8 +29,9 @@ public static string ToCSharpString(this TypedConstant constant) return "{" + string.Join(", ", constant.Values.Select(v => v.ToCSharpString())) + "}"; } - if (constant.Kind == TypedConstantKind.Type || constant.TypeInternal.SpecialType == SpecialType.System_Object) + if (constant.Kind == TypedConstantKind.Type || constant.TypeInternal!.SpecialType == SpecialType.System_Object) { + Debug.Assert(constant.Value is object); return "typeof(" + constant.Value.ToString() + ")"; } @@ -46,6 +41,7 @@ public static string ToCSharpString(this TypedConstant constant) return DisplayEnumConstant(constant); } + Debug.Assert(constant.ValueInternal is object); return SymbolDisplay.FormatPrimitive(constant.ValueInternal, quoteStrings: true, useHexadecimalNumbers: false); } @@ -55,7 +51,8 @@ private static string DisplayEnumConstant(TypedConstant constant) Debug.Assert(constant.Kind == TypedConstantKind.Enum); // Create a ConstantValue of enum underlying type - SpecialType splType = ((INamedTypeSymbol)constant.Type).EnumUnderlyingType.SpecialType; + SpecialType splType = ((INamedTypeSymbol)constant.Type!).EnumUnderlyingType!.SpecialType; + Debug.Assert(constant.ValueInternal is object); ConstantValue valueConstant = ConstantValue.Create(constant.ValueInternal, splType); string typeName = constant.Type.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat); @@ -71,6 +68,8 @@ private static string DisplayEnumConstant(TypedConstant constant) private static string DisplayUnsignedEnumConstant(TypedConstant constant, SpecialType specialType, ulong constantToDecode, string typeName) { + Debug.Assert(constant.Kind == TypedConstantKind.Enum); + // Specified valueConstant might have an exact matching enum field // or it might be a bitwise Or of multiple enum fields. // For the later case, we keep track of the current value of @@ -78,18 +77,18 @@ private static string DisplayUnsignedEnumConstant(TypedConstant constant, Specia ulong curValue = 0; // Initialize the value string to empty - PooledStringBuilder pooledBuilder = null; - StringBuilder valueStringBuilder = null; + PooledStringBuilder? pooledBuilder = null; + StringBuilder? valueStringBuilder = null; // Iterate through all the constant members in the enum type - var members = constant.Type.GetMembers(); + var members = constant.Type!.GetMembers(); foreach (var member in members) { var field = member as IFieldSymbol; - if ((object)field != null && field.HasConstantValue) + if (field is object && field.HasConstantValue) { - ConstantValue memberConstant = ConstantValue.Create(field.ConstantValue, specialType); + ConstantValue memberConstant = ConstantValue.Create(field.ConstantValue!, specialType); // use MemberNotNull when available https://github.com/dotnet/roslyn/issues/41964 ulong memberValue = memberConstant.UInt64Value; // Do we have an exact matching enum field @@ -140,11 +139,16 @@ private static string DisplayUnsignedEnumConstant(TypedConstant constant, Specia } // Unable to decode the enum constant, just display the integral value - return constant.ValueInternal.ToString(); + Debug.Assert(constant.ValueInternal is object); + var result = constant.ValueInternal.ToString(); + Debug.Assert(result is object); + return result; } private static string DisplaySignedEnumConstant(TypedConstant constant, SpecialType specialType, long constantToDecode, string typeName) { + Debug.Assert(constant.Kind == TypedConstantKind.Enum); + // Specified valueConstant might have an exact matching enum field // or it might be a bitwise Or of multiple enum fields. // For the later case, we keep track of the current value of @@ -152,17 +156,17 @@ private static string DisplaySignedEnumConstant(TypedConstant constant, SpecialT long curValue = 0; // Initialize the value string to empty - PooledStringBuilder pooledBuilder = null; - StringBuilder valueStringBuilder = null; + PooledStringBuilder? pooledBuilder = null; + StringBuilder? valueStringBuilder = null; // Iterate through all the constant members in the enum type - var members = constant.Type.GetMembers(); + var members = constant.Type!.GetMembers(); foreach (var member in members) { var field = member as IFieldSymbol; - if ((object)field != null && field.HasConstantValue) + if (field is object && field.HasConstantValue) { - ConstantValue memberConstant = ConstantValue.Create(field.ConstantValue, specialType); + ConstantValue memberConstant = ConstantValue.Create(field.ConstantValue!, specialType); // use MemberNotNull when available https://github.com/dotnet/roslyn/issues/41964 long memberValue = memberConstant.Int64Value; // Do we have an exact matching enum field @@ -213,7 +217,10 @@ private static string DisplaySignedEnumConstant(TypedConstant constant, SpecialT } // Unable to decode the enum constant, just display the integral value - return constant.ValueInternal.ToString(); + Debug.Assert(constant.ValueInternal is object); + var result = constant.ValueInternal.ToString(); + Debug.Assert(result is object); + return result; } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/AliasedQualifiedNameSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/AliasedQualifiedNameSyntax.cs index bd2cb57afb836..3f67d1046129e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/AliasedQualifiedNameSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/AliasedQualifiedNameSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public sealed partial class AliasQualifiedNameSyntax : NameSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/AnonymousFunctionExpressionSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/AnonymousFunctionExpressionSyntax.cs index 564bd1b752ce7..3c813fd2e1aec 100644 --- a/src/Compilers/CSharp/Portable/Syntax/AnonymousFunctionExpressionSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/AnonymousFunctionExpressionSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class AnonymousFunctionExpressionSyntax @@ -10,7 +12,7 @@ public partial class AnonymousFunctionExpressionSyntax /// Either the if it is not null or the /// otherwise. /// - public CSharpSyntaxNode Body => Block ?? (CSharpSyntaxNode)ExpressionBody; + public CSharpSyntaxNode Body => Block ?? (CSharpSyntaxNode)ExpressionBody!; public AnonymousFunctionExpressionSyntax WithBody(CSharpSyntaxNode body) => body is BlockSyntax block diff --git a/src/Compilers/CSharp/Portable/Syntax/ArgumentSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ArgumentSyntax.cs index f9292b41857a8..4973d3a3d6c65 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ArgumentSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ArgumentSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.ComponentModel; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/ArrayRankSpecifierSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ArrayRankSpecifierSyntax.cs index ca28ad5da1982..18663c32841a4 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ArrayRankSpecifierSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ArrayRankSpecifierSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class ArrayRankSpecifierSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/AttributeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/AttributeSyntax.cs index d1f9ca0182652..688116858c168 100644 --- a/src/Compilers/CSharp/Portable/Syntax/AttributeSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/AttributeSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Diagnostics; @@ -20,7 +22,7 @@ internal string GetErrorDisplayName() return Name.ErrorDisplayName(); } - internal AttributeArgumentSyntax GetNamedArgumentSyntax(string namedArgName) + internal AttributeArgumentSyntax? GetNamedArgumentSyntax(string namedArgName) { Debug.Assert(!String.IsNullOrEmpty(namedArgName)); diff --git a/src/Compilers/CSharp/Portable/Syntax/AttributeTargetSpecifierSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/AttributeTargetSpecifierSyntax.cs index a450587688d4b..0352e3d1c806d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/AttributeTargetSpecifierSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/AttributeTargetSpecifierSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/BlockSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/BlockSyntax.cs index 95319677e94a3..eb3492ed5eab3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/BlockSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/BlockSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/BreakStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/BreakStatementSyntax.cs index c4a4ceb4c1e83..4b4bae8e50eec 100644 --- a/src/Compilers/CSharp/Portable/Syntax/BreakStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/BreakStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs index bcb1d43284100..fc2ecf9a54f6d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpLineDirectiveMap.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Diagnostics; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -60,7 +62,7 @@ protected override LineMappingEntry GetEntry(DirectiveTriviaSyntax directiveNode // skip both the mapped line and the filename if the line number is not valid if (!lineToken.ContainsDiagnostics) { - object value = lineToken.Value; + object? value = lineToken.Value; if (value is int) { // convert one-based line number into zero-based line number @@ -69,7 +71,7 @@ protected override LineMappingEntry GetEntry(DirectiveTriviaSyntax directiveNode if (directive.File.Kind() == SyntaxKind.StringLiteralToken) { - mappedPathOpt = (string)directive.File.Value; + mappedPathOpt = (string?)directive.File.Value; } state = PositionState.Remapped; diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs index 75c2eebd0d40c..aa9d0a9c2bfab 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpPragmaWarningStateMap.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -125,7 +127,7 @@ private static WarningStateMapEntry[] CreatePragmaWarningStateEntries(ArrayBuild if (currentErrorCode.Kind() == SyntaxKind.NumericLiteralExpression) { var token = ((LiteralExpressionSyntax)currentErrorCode).Token; - errorId = MessageProvider.Instance.GetIdForErrorCode((int)token.Value); + errorId = MessageProvider.Instance.GetIdForErrorCode((int)token.Value!); } else if (currentErrorCode.Kind() == SyntaxKind.IdentifierName) { diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs index b14985aee8897..68d0be4fd2059 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxNode.cs @@ -520,7 +520,7 @@ protected internal override SyntaxNode InsertTriviaInListCore(SyntaxTrivia origi return SyntaxReplacer.InsertTriviaInList(this, originalTrivia, newTrivia, insertBefore).AsRootOfNewTreeWithOptionsFrom(this.SyntaxTree); } - protected internal override SyntaxNode RemoveNodesCore(IEnumerable nodes, SyntaxRemoveOptions options) + protected internal override SyntaxNode? RemoveNodesCore(IEnumerable nodes, SyntaxRemoveOptions options) { return SyntaxNodeRemover.RemoveNodes(this, nodes.Cast(), options).AsRootOfNewTreeWithOptionsFrom(this.SyntaxTree); } diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxRewriter.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxRewriter.cs index f44481578108d..2eca35b725827 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxRewriter.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxRewriter.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Syntax; @@ -31,6 +32,7 @@ public virtual bool VisitIntoStructuredTrivia private int _recursionDepth; + [return: NotNullIfNotNull("node")] public override SyntaxNode? Visit(SyntaxNode? node) { if (node != null) diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs index ccc199d778c98..b50540d22e896 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.Dummy.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Immutable; using System.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.ParsedSyntaxTree.cs b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.ParsedSyntaxTree.cs index 83ce912b4ca23..b4502d0b85bed 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.ParsedSyntaxTree.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CSharpSyntaxTree.ParsedSyntaxTree.cs @@ -2,10 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading; @@ -22,20 +25,20 @@ private class ParsedSyntaxTree : CSharpSyntaxTree private readonly string _path; private readonly CSharpSyntaxNode _root; private readonly bool _hasCompilationUnitRoot; - private readonly Encoding _encodingOpt; + private readonly Encoding? _encodingOpt; private readonly SourceHashAlgorithm _checksumAlgorithm; private readonly ImmutableDictionary _diagnosticOptions; - private SourceText _lazyText; + private SourceText? _lazyText; internal ParsedSyntaxTree( - SourceText textOpt, - Encoding encodingOpt, + SourceText? textOpt, + Encoding? encodingOpt, SourceHashAlgorithm checksumAlgorithm, string path, CSharpParseOptions options, CSharpSyntaxNode root, Syntax.InternalSyntax.DirectiveStack directives, - ImmutableDictionary diagnosticOptions, + ImmutableDictionary? diagnosticOptions, bool? isGeneratedCode, bool cloneRoot) { @@ -75,13 +78,13 @@ public override SourceText GetText(CancellationToken cancellationToken) return _lazyText; } - public override bool TryGetText(out SourceText text) + public override bool TryGetText([NotNullWhen(true)] out SourceText? text) { text = _lazyText; return text != null; } - public override Encoding Encoding + public override Encoding? Encoding { get { return _encodingOpt; } } @@ -133,7 +136,7 @@ public override SyntaxTree WithRootAndOptions(SyntaxNode root, ParseOptions opti } return new ParsedSyntaxTree( - null, + textOpt: null, _encodingOpt, _checksumAlgorithm, _path, diff --git a/src/Compilers/CSharp/Portable/Syntax/CheckedStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/CheckedStatementSyntax.cs index 9789f47a1b634..54fe62e236c64 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CheckedStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CheckedStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/ClassOrStructConstraintSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ClassOrStructConstraintSyntax.cs index e6a6fbe9a1595..bcb1263a97562 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ClassOrStructConstraintSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ClassOrStructConstraintSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class ClassOrStructConstraintSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs index 41f18136ca60f..469886c9863c4 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CompilationUnitSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; @@ -17,7 +19,7 @@ public IList GetReferenceDirectives() return GetReferenceDirectives(null); } - internal IList GetReferenceDirectives(Func filter) + internal IList GetReferenceDirectives(Func? filter) { // #r directives are always on the first token of the compilation unit. var firstToken = (SyntaxNodeOrToken)this.GetFirstToken(includeZeroWidth: true); diff --git a/src/Compilers/CSharp/Portable/Syntax/ConstructorDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ConstructorDeclarationSyntax.cs index bc5f77fa7fd0d..2608ed0c8df3d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ConstructorDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ConstructorDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/ContinueStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ContinueStatementSyntax.cs index 1cdda97bff701..145f80e275067 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ContinueStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ContinueStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/CrefParameterSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/CrefParameterSyntax.cs index d45df4f20ea1f..22f33fd4403fa 100644 --- a/src/Compilers/CSharp/Portable/Syntax/CrefParameterSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/CrefParameterSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.ComponentModel; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/DeclarationStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DeclarationStatementSyntax.cs index 71bcee364af47..296fcf0a399c3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/DeclarationStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/DeclarationStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class LocalDeclarationStatementSyntax : StatementSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/DelegateDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DelegateDeclarationSyntax.cs index 5113e2fb062a3..3afd660c52465 100644 --- a/src/Compilers/CSharp/Portable/Syntax/DelegateDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/DelegateDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/DestructorDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DestructorDeclarationSyntax.cs index b6a1ffc1ec00e..65ddd215eac9d 100644 --- a/src/Compilers/CSharp/Portable/Syntax/DestructorDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/DestructorDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs index a7a2e04776db9..e2fbe2067dc90 100644 --- a/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; using System.Linq; @@ -62,7 +64,7 @@ public SyntaxToken DirectiveNameToken } } - public DirectiveTriviaSyntax GetNextDirective(Func predicate = null) + public DirectiveTriviaSyntax? GetNextDirective(Func? predicate = null) { var token = (SyntaxToken)this.ParentTrivia.Token; bool next = false; @@ -74,7 +76,7 @@ public DirectiveTriviaSyntax GetNextDirective(Func { if (tr.IsDirective) { - var d = (DirectiveTriviaSyntax)tr.GetStructure(); + var d = (DirectiveTriviaSyntax)tr.GetStructure()!; if (predicate == null || predicate(d)) { return d; @@ -93,7 +95,7 @@ public DirectiveTriviaSyntax GetNextDirective(Func return null; } - public DirectiveTriviaSyntax GetPreviousDirective(Func predicate = null) + public DirectiveTriviaSyntax? GetPreviousDirective(Func? predicate = null) { var token = (SyntaxToken)this.ParentTrivia.Token; bool next = false; @@ -105,7 +107,7 @@ public DirectiveTriviaSyntax GetPreviousDirective(Func list) } } - private DirectiveTriviaSyntax GetNextRelatedDirective() + private DirectiveTriviaSyntax? GetNextRelatedDirective() { - DirectiveTriviaSyntax d = this; + DirectiveTriviaSyntax? d = this; switch (d.Kind()) { case SyntaxKind.IfDirectiveTrivia: @@ -217,9 +219,9 @@ private DirectiveTriviaSyntax GetNextRelatedDirective() return null; } - private DirectiveTriviaSyntax GetNextPossiblyRelatedDirective() + private DirectiveTriviaSyntax? GetNextPossiblyRelatedDirective() { - DirectiveTriviaSyntax d = this; + DirectiveTriviaSyntax? d = this; while (d != null) { d = d.GetNextDirective(); @@ -251,9 +253,9 @@ private DirectiveTriviaSyntax GetNextPossiblyRelatedDirective() return null; } - private DirectiveTriviaSyntax GetPreviousRelatedDirective() + private DirectiveTriviaSyntax? GetPreviousRelatedDirective() { - DirectiveTriviaSyntax d = this; + DirectiveTriviaSyntax? d = this; switch (d.Kind()) { case SyntaxKind.EndIfDirectiveTrivia: @@ -318,9 +320,9 @@ private DirectiveTriviaSyntax GetPreviousRelatedDirective() return null; } - private DirectiveTriviaSyntax GetPreviousPossiblyRelatedDirective() + private DirectiveTriviaSyntax? GetPreviousPossiblyRelatedDirective() { - DirectiveTriviaSyntax d = this; + DirectiveTriviaSyntax? d = this; while (d != null) { d = d.GetPreviousDirective(); diff --git a/src/Compilers/CSharp/Portable/Syntax/DoStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DoStatementSyntax.cs index 634e1fafb6db5..0c4d6f689e174 100644 --- a/src/Compilers/CSharp/Portable/Syntax/DoStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/DoStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/EmptyStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/EmptyStatementSyntax.cs index 48f59f5bbbf4c..fea389fa85f35 100644 --- a/src/Compilers/CSharp/Portable/Syntax/EmptyStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/EmptyStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/EnumMemberDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/EnumMemberDeclarationSyntax.cs index 00254bd610961..11471dc4f8265 100644 --- a/src/Compilers/CSharp/Portable/Syntax/EnumMemberDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/EnumMemberDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class EnumMemberDeclarationSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/EventDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/EventDeclarationSyntax.cs index 22136c749bc25..f223314c6873b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/EventDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/EventDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.ComponentModel; diff --git a/src/Compilers/CSharp/Portable/Syntax/ExpressionStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ExpressionStatementSyntax.cs index bf12dcf019bab..54a9261ac1163 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ExpressionStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ExpressionStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/FixedStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/FixedStatementSyntax.cs index f0038681378e9..2892b9c1faa74 100644 --- a/src/Compilers/CSharp/Portable/Syntax/FixedStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/FixedStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs index 308a75fedb796..a4f5db2523590 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ForEachStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/ForEachVariableStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ForEachVariableStatementSyntax.cs index 5d84db42596f8..0af79155f6d09 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ForEachVariableStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ForEachVariableStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/ForStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ForStatementSyntax.cs index 059c6078c0bbd..549da08f3ea25 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ForStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ForStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/GenericNameSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/GenericNameSyntax.cs index 67ed2f3a3f589..1adbd7090d7de 100644 --- a/src/Compilers/CSharp/Portable/Syntax/GenericNameSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/GenericNameSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Compilers/CSharp/Portable/Syntax/GlobalStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/GlobalStatementSyntax.cs index 4f444586d234c..fc9cf4fd553de 100644 --- a/src/Compilers/CSharp/Portable/Syntax/GlobalStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/GlobalStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class GlobalStatementSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/GotoStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/GotoStatementSyntax.cs index 4090c54296041..290b0ae8486aa 100644 --- a/src/Compilers/CSharp/Portable/Syntax/GotoStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/GotoStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/IdentifierNameSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/IdentifierNameSyntax.cs index 4134ff6973865..fe9a3ccb43de3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/IdentifierNameSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/IdentifierNameSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class IdentifierNameSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/IfStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/IfStatementSyntax.cs index 6cf4645cfc140..d334a4db11f85 100644 --- a/src/Compilers/CSharp/Portable/Syntax/IfStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/IfStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/IndexerDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/IndexerDeclarationSyntax.cs index c87269a9fe8bc..75ec4c53dfa5b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/IndexerDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/IndexerDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.ComponentModel; diff --git a/src/Compilers/CSharp/Portable/Syntax/LabeledStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/LabeledStatementSyntax.cs index b93ed2bf29e50..d39ab9260c3f8 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LabeledStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LabeledStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs index c22abae528445..b25062eb443b6 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -49,14 +52,15 @@ public static bool IsLambda(SyntaxNode node) /// public static SyntaxNode GetLambda(SyntaxNode lambdaBody) { + Debug.Assert(lambdaBody.Parent is object); var lambda = lambdaBody.Parent; - if (lambda.Kind() == SyntaxKind.ArrowExpressionClause) + if (lambda.IsKind(SyntaxKind.ArrowExpressionClause)) { // In case of expression bodied local functions there is a three level hierarchy: // LocalFunctionStatement -> ArrowExpressionClause -> Expression. // And the lambda is the LocalFunctionStatement. lambda = lambda.Parent; - Debug.Assert(lambda.Kind() == SyntaxKind.LocalFunctionStatement); + Debug.Assert(lambda.IsKind(SyntaxKind.LocalFunctionStatement)); } Debug.Assert(IsLambda(lambda)); @@ -66,8 +70,10 @@ public static SyntaxNode GetLambda(SyntaxNode lambdaBody) /// /// See SyntaxNode.GetCorrespondingLambdaBody. /// - internal static SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) + internal static SyntaxNode? TryGetCorrespondingLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) { + Debug.Assert(oldBody.Parent is object); + switch (newLambda.Kind()) { case SyntaxKind.ParenthesizedLambdaExpression: @@ -120,7 +126,7 @@ public static SyntaxNode GetNestedFunctionBody(SyntaxNode nestedFunction) => nestedFunction switch { AnonymousFunctionExpressionSyntax anonymousFunctionExpressionSyntax => anonymousFunctionExpressionSyntax.Body, - LocalFunctionStatementSyntax localFunctionStatementSyntax => (CSharpSyntaxNode)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody.Expression, + LocalFunctionStatementSyntax localFunctionStatementSyntax => (CSharpSyntaxNode?)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody!.Expression, _ => throw ExceptionUtilities.UnexpectedValue(nestedFunction), }; @@ -221,6 +227,7 @@ private static bool IsReducedSelectOrGroupByClause(SelectOrGroupClauseSyntax sel SyntaxToken sourceIdentifier; QueryBodySyntax containingBody; + Debug.Assert(selectOrGroupClause.Parent!.Parent is object); var containingQueryOrContinuation = selectOrGroupClause.Parent.Parent; if (containingQueryOrContinuation.IsKind(SyntaxKind.QueryExpression)) { @@ -275,7 +282,7 @@ public static bool IsLambdaBodyStatementOrExpression(SyntaxNode node, out Syntax /// /// If the specified node represents a lambda returns a node (or nodes) that represent its body (bodies). /// - public static bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode lambdaBody1, out SyntaxNode lambdaBody2) + public static bool TryGetLambdaBodies(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? lambdaBody1, out SyntaxNode? lambdaBody2) { lambdaBody1 = null; lambdaBody2 = null; @@ -474,7 +481,7 @@ internal static int GetDeclaratorPosition(SyntaxNode node) private static SyntaxNode GetLocalFunctionBody(LocalFunctionStatementSyntax localFunctionStatementSyntax) { - return (SyntaxNode)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody?.Expression; + return (SyntaxNode?)localFunctionStatementSyntax.Body ?? localFunctionStatementSyntax.ExpressionBody!.Expression; } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/LocalDeclarationStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/LocalDeclarationStatementSyntax.cs index 53c210809e458..1fe84d117a82a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LocalDeclarationStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LocalDeclarationStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/LocalFunctionStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/LocalFunctionStatementSyntax.cs index 5930b520c8572..8acac93cbfe34 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LocalFunctionStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LocalFunctionStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp diff --git a/src/Compilers/CSharp/Portable/Syntax/LockStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/LockStatementSyntax.cs index 7ff788e494f25..499f6f93fe49e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LockStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LockStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs index fc2b987217e8e..efb5ea2cfb747 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Diagnostics; using Roslyn.Utilities; @@ -20,21 +22,21 @@ internal static class LookupPosition /// A position is considered to be inside a block if it is on or after /// the open brace and strictly before the close brace. /// - internal static bool IsInBlock(int position, BlockSyntax blockOpt) + internal static bool IsInBlock(int position, BlockSyntax? blockOpt) { return blockOpt != null && IsBeforeToken(position, blockOpt, blockOpt.CloseBraceToken); } internal static bool IsInExpressionBody( int position, - ArrowExpressionClauseSyntax expressionBodyOpt, + ArrowExpressionClauseSyntax? expressionBodyOpt, SyntaxToken semicolonToken) { return expressionBodyOpt != null && IsBeforeToken(position, expressionBodyOpt, semicolonToken); } - private static bool IsInBody(int position, BlockSyntax blockOpt, ArrowExpressionClauseSyntax exprOpt, SyntaxToken semiOpt) + private static bool IsInBody(int position, BlockSyntax? blockOpt, ArrowExpressionClauseSyntax? exprOpt, SyntaxToken semiOpt) { return IsInExpressionBody(position, exprOpt, semiOpt) || IsInBlock(position, blockOpt); @@ -185,7 +187,7 @@ internal static bool IsInConstructorParameterScope(int position, ConstructorDecl return initializerOpt == null ? IsInBody(position, constructorDecl) : IsBetweenTokens(position, initializerOpt.ColonToken, - constructorDecl.SemicolonToken.Kind() == SyntaxKind.None ? constructorDecl.Body.CloseBraceToken : constructorDecl.SemicolonToken); + constructorDecl.SemicolonToken.Kind() == SyntaxKind.None ? constructorDecl.Body!.CloseBraceToken : constructorDecl.SemicolonToken); } internal static bool IsInMethodTypeParameterScope(int position, MethodDeclarationSyntax methodDecl) @@ -370,7 +372,7 @@ internal static SyntaxToken GetFirstExcludedToken(StatementSyntax statement) return ((GotoStatementSyntax)statement).SemicolonToken; case SyntaxKind.IfStatement: IfStatementSyntax ifStmt = (IfStatementSyntax)statement; - ElseClauseSyntax elseOpt = ifStmt.Else; + ElseClauseSyntax? elseOpt = ifStmt.Else; return GetFirstExcludedToken(elseOpt == null ? ifStmt.Statement : elseOpt.Statement); case SyntaxKind.LabeledStatement: return GetFirstExcludedToken(((LabeledStatementSyntax)statement).Statement); @@ -385,13 +387,13 @@ internal static SyntaxToken GetFirstExcludedToken(StatementSyntax statement) case SyntaxKind.TryStatement: TryStatementSyntax tryStmt = (TryStatementSyntax)statement; - FinallyClauseSyntax finallyClause = tryStmt.Finally; + FinallyClauseSyntax? finallyClause = tryStmt.Finally; if (finallyClause != null) { return finallyClause.Block.CloseBraceToken; } - CatchClauseSyntax lastCatch = tryStmt.Catches.LastOrDefault(); + CatchClauseSyntax? lastCatch = tryStmt.Catches.LastOrDefault(); if (lastCatch != null) { return lastCatch.Block.CloseBraceToken; diff --git a/src/Compilers/CSharp/Portable/Syntax/MethodDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/MethodDeclarationSyntax.cs index 79e1e850d10d9..6af2463864611 100644 --- a/src/Compilers/CSharp/Portable/Syntax/MethodDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/MethodDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/NameSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/NameSyntax.cs index 7b74f08718361..ba083ce97f482 100644 --- a/src/Compilers/CSharp/Portable/Syntax/NameSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/NameSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; @@ -38,7 +40,7 @@ public int Arity /// containing the explicitly implemented interface symbol - there may be more than one. We just want to know /// how the name was qualified in source so that we can make a similar qualification (for uniqueness purposes). /// - internal string GetAliasQualifierOpt() + internal string? GetAliasQualifierOpt() { NameSyntax name = this; while (true) diff --git a/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntax.cs index b853c6e098b87..af976477df698 100644 --- a/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs b/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs index 10aae6d6b8710..4e3beea132236 100644 --- a/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs +++ b/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Compilers/CSharp/Portable/Syntax/NullableContextStateMap.cs b/src/Compilers/CSharp/Portable/Syntax/NullableContextStateMap.cs index 717d724cbef9e..67d1c91b46e6a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/NullableContextStateMap.cs +++ b/src/Compilers/CSharp/Portable/Syntax/NullableContextStateMap.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Compilers/CSharp/Portable/Syntax/ParameterListSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ParameterListSyntax.cs index 0a554029ab0ee..351a6171d41ba 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ParameterListSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ParameterListSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/ParameterSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ParameterSyntax.cs index 420409f8577e4..79d848c174f3e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ParameterSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ParameterSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/ParenthesizedLambdaExpressionSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ParenthesizedLambdaExpressionSyntax.cs index a2a6703e37999..21fa5c18fd427 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ParenthesizedLambdaExpressionSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ParenthesizedLambdaExpressionSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public partial class ParenthesizedLambdaExpressionSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/PropertyDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/PropertyDeclarationSyntax.cs index aff5609cb9091..8404cb1ee9c98 100644 --- a/src/Compilers/CSharp/Portable/Syntax/PropertyDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/PropertyDeclarationSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.ComponentModel; diff --git a/src/Compilers/CSharp/Portable/Syntax/QualifiedNameSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/QualifiedNameSyntax.cs index 6e7fe58edfbc8..ff3ffea89ea0f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/QualifiedNameSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/QualifiedNameSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.ComponentModel; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Compilers/CSharp/Portable/Syntax/RefTypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/RefTypeSyntax.cs index 599708eb6ae24..d0bd800ac5ab5 100644 --- a/src/Compilers/CSharp/Portable/Syntax/RefTypeSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/RefTypeSyntax.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.CSharp.Symbols; +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Syntax { @@ -13,7 +12,7 @@ public partial class RefTypeSyntax { public RefTypeSyntax Update(SyntaxToken refKeyword, TypeSyntax type) { - return Update(refKeyword, default(SyntaxToken), type); + return Update(refKeyword, readOnlyKeyword: default, type); } } } @@ -25,7 +24,7 @@ public partial class SyntaxFactory /// Creates a new RefTypeSyntax instance. public static RefTypeSyntax RefType(SyntaxToken refKeyword, TypeSyntax type) { - return RefType(refKeyword, default(SyntaxToken), type); + return RefType(refKeyword, readOnlyKeyword: default, type); } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/ReturnStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ReturnStatementSyntax.cs index e873eae09f6e2..6b8aeda025d8e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ReturnStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ReturnStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/SimpleNameSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/SimpleNameSyntax.cs index b4ff4ab165602..ab747b1d39bb8 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SimpleNameSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SimpleNameSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.ComponentModel; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Compilers/CSharp/Portable/Syntax/SimpleSyntaxReference.cs b/src/Compilers/CSharp/Portable/Syntax/SimpleSyntaxReference.cs index ef0717fea9991..3b410f3a1d599 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SimpleSyntaxReference.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SimpleSyntaxReference.cs @@ -2,8 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp diff --git a/src/Compilers/CSharp/Portable/Syntax/SkippedTokensTriviaSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/SkippedTokensTriviaSyntax.cs index abc4eb8c6f901..0bb7a60f0f750 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SkippedTokensTriviaSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SkippedTokensTriviaSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public sealed partial class SkippedTokensTriviaSyntax : StructuredTriviaSyntax, ISkippedTokensTriviaSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/StackAllocArrayCreationExpressionSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/StackAllocArrayCreationExpressionSyntax.cs index 059555cc67c7f..fe6a2a9cfee72 100644 --- a/src/Compilers/CSharp/Portable/Syntax/StackAllocArrayCreationExpressionSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/StackAllocArrayCreationExpressionSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/SwitchStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/SwitchStatementSyntax.cs index bedc1f169890f..f2c1bbc5da47e 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SwitchStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SwitchStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs index 303178fa057e7..5e77790f81ca5 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindExtensions.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index 5dab21460a44a..54dea9ddf7b75 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -90,10 +92,10 @@ internal static bool CanHaveAssociatedLocalBinder(this SyntaxNode syntax) } } - internal static bool IsValidScopeDesignator(this ExpressionSyntax expression) + internal static bool IsValidScopeDesignator(this ExpressionSyntax? expression) { // All these nodes are valid scope designators due to the pattern matching and out vars features. - CSharpSyntaxNode parent = expression?.Parent; + CSharpSyntaxNode? parent = expression?.Parent; switch (parent?.Kind()) { case SyntaxKind.SimpleLambdaExpression: @@ -137,7 +139,7 @@ internal static bool IsLegalCSharp73SpanStackAllocPosition(this SyntaxNode node) node = node.Parent; } - SyntaxNode parentNode = node.Parent; + SyntaxNode? parentNode = node.Parent; if (parentNode is null) { @@ -149,7 +151,7 @@ internal static bool IsLegalCSharp73SpanStackAllocPosition(this SyntaxNode node) // In case of a declaration of a Span variable case SyntaxKind.EqualsValueClause: { - SyntaxNode variableDeclarator = parentNode.Parent; + SyntaxNode? variableDeclarator = parentNode.Parent; return variableDeclarator.IsKind(SyntaxKind.VariableDeclarator) && variableDeclarator.Parent.IsKind(SyntaxKind.VariableDeclaration); @@ -231,8 +233,8 @@ internal static TypeSyntax SkipRef(this TypeSyntax syntax, out RefKind refKind) return syntax; } - internal static ExpressionSyntax CheckAndUnwrapRefExpression( - this ExpressionSyntax syntax, + internal static ExpressionSyntax? CheckAndUnwrapRefExpression( + this ExpressionSyntax? syntax, DiagnosticBag diagnostics, out RefKind refKind) { diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs index 2d82c19ea8b63..c0718c3bd4c5a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeRemover.cs @@ -2,10 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Syntax; using Microsoft.CodeAnalysis.Text; @@ -13,7 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Syntax { internal static class SyntaxNodeRemover { - internal static TRoot RemoveNodes(TRoot root, + internal static TRoot? RemoveNodes(TRoot root, IEnumerable nodes, SyntaxRemoveOptions options) where TRoot : SyntaxNode @@ -41,7 +44,7 @@ internal static TRoot RemoveNodes(TRoot root, result = result.WithTrailingTrivia(result.GetTrailingTrivia().Concat(residualTrivia)); } - return (TRoot)result; + return (TRoot?)result; } private class SyntaxRemover : CSharpSyntaxRewriter @@ -50,7 +53,7 @@ private class SyntaxRemover : CSharpSyntaxRewriter private readonly SyntaxRemoveOptions _options; private readonly TextSpan _searchSpan; private readonly SyntaxTriviaListBuilder _residualTrivia; - private HashSet _directivesToKeep; + private HashSet? _directivesToKeep; public SyntaxRemover( SyntaxNode[] nodesToRemove, @@ -160,9 +163,10 @@ private bool ShouldVisit(SyntaxNode node) return node.FullSpan.IntersectsWith(_searchSpan) || (_residualTrivia != null && _residualTrivia.Count > 0); } - public override SyntaxNode Visit(SyntaxNode node) + [return: NotNullIfNotNull("node")] + public override SyntaxNode? Visit(SyntaxNode? node) { - SyntaxNode result = node; + SyntaxNode? result = node; if (node != null) { @@ -207,7 +211,7 @@ public override SeparatedSyntaxList VisitList(SeparatedSyntaxList< var withSeps = list.GetWithSeparators(); bool removeNextSeparator = false; - SyntaxNodeOrTokenListBuilder alternate = null; + SyntaxNodeOrTokenListBuilder? alternate = null; for (int i = 0, n = withSeps.Count; i < n; i++) { var item = withSeps[i]; @@ -227,7 +231,7 @@ public override SeparatedSyntaxList VisitList(SeparatedSyntaxList< } else { - var node = (TNode)item.AsNode(); + var node = (TNode)item.AsNode()!; if (this.IsForRemoval(node)) { @@ -264,7 +268,7 @@ public override SeparatedSyntaxList VisitList(SeparatedSyntaxList< } else { - visited = this.VisitListElement((TNode)item.AsNode()); + visited = this.VisitListElement(node); } } @@ -321,6 +325,7 @@ private void AddTrivia(SyntaxNode node) private void AddTrivia(SyntaxToken token, SyntaxNode node) { + Debug.Assert(node.Parent is object); if ((_options & SyntaxRemoveOptions.KeepLeadingTrivia) != 0) { this.AddResidualTrivia(token.LeadingTrivia); @@ -360,6 +365,7 @@ private void AddTrivia(SyntaxToken token, SyntaxNode node) private void AddTrivia(SyntaxNode node, SyntaxToken token) { + Debug.Assert(node.Parent is object); if ((_options & SyntaxRemoveOptions.KeepLeadingTrivia) != 0) { this.AddResidualTrivia(node.GetLeadingTrivia()); @@ -429,7 +435,7 @@ private void AddDirectives(SyntaxNode node, TextSpan span) var directivesInSpan = node.DescendantTrivia(span, n => n.ContainsDirectives, descendIntoTrivia: true) .Where(tr => tr.IsDirective) - .Select(tr => (DirectiveTriviaSyntax)tr.GetStructure()); + .Select(tr => (DirectiveTriviaSyntax)tr.GetStructure()!); foreach (var directive in directivesInSpan) { diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs index 857ab9646f26f..477131f0d4cc1 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -29,7 +31,7 @@ internal class SyntaxNormalizer : CSharpSyntaxRewriter // CONSIDER: if we become concerned about space, we shouldn't actually need any // of the values between indentations[0] and indentations[initialDepth] (exclusive). - private ArrayBuilder _indentations; + private ArrayBuilder? _indentations; private SyntaxNormalizer(TextSpan consideredSpan, int initialDepth, string indentWhitespace, string eolWhitespace, bool useElasticTrivia) : base(visitIntoStructuredTrivia: true) @@ -239,10 +241,10 @@ private int LineBreaksAfter(SyntaxToken currentToken, SyntaxToken nextToken) (nextToken.IsKind(SyntaxKind.LetKeyword) && nextToken.Parent.IsKind(SyntaxKind.LetClause)) || (nextToken.IsKind(SyntaxKind.WhereKeyword) && nextToken.Parent.IsKind(SyntaxKind.WhereClause)) || (nextToken.IsKind(SyntaxKind.JoinKeyword) && nextToken.Parent.IsKind(SyntaxKind.JoinClause)) || - (nextToken.IsKind(SyntaxKind.JoinKeyword) && nextToken.Parent.Kind() == SyntaxKind.JoinIntoClause) || - (nextToken.Kind() == SyntaxKind.OrderByKeyword && nextToken.Parent.Kind() == SyntaxKind.OrderByClause) || - (nextToken.Kind() == SyntaxKind.SelectKeyword && nextToken.Parent.Kind() == SyntaxKind.SelectClause) || - (nextToken.Kind() == SyntaxKind.GroupKeyword && nextToken.Parent.Kind() == SyntaxKind.GroupClause)) + (nextToken.IsKind(SyntaxKind.JoinKeyword) && nextToken.Parent.IsKind(SyntaxKind.JoinIntoClause)) || + (nextToken.IsKind(SyntaxKind.OrderByKeyword) && nextToken.Parent.IsKind(SyntaxKind.OrderByClause)) || + (nextToken.IsKind(SyntaxKind.SelectKeyword) && nextToken.Parent.IsKind(SyntaxKind.SelectClause)) || + (nextToken.IsKind(SyntaxKind.GroupKeyword) && nextToken.Parent.IsKind(SyntaxKind.GroupClause))) { return 1; } @@ -309,7 +311,7 @@ private static int LineBreaksAfterCloseBrace(SyntaxToken currentToken, SyntaxTok private static int LineBreaksAfterSemicolon(SyntaxToken currentToken, SyntaxToken nextToken) { - if (currentToken.Parent.Kind() == SyntaxKind.ForStatement) + if (currentToken.Parent.IsKind(SyntaxKind.ForStatement)) { return 0; } @@ -317,13 +319,13 @@ private static int LineBreaksAfterSemicolon(SyntaxToken currentToken, SyntaxToke { return 1; } - else if (currentToken.Parent.Kind() == SyntaxKind.UsingDirective) + else if (currentToken.Parent.IsKind(SyntaxKind.UsingDirective)) { - return nextToken.Parent.Kind() == SyntaxKind.UsingDirective ? 1 : 2; + return nextToken.Parent.IsKind(SyntaxKind.UsingDirective) ? 1 : 2; } - else if (currentToken.Parent.Kind() == SyntaxKind.ExternAliasDirective) + else if (currentToken.Parent.IsKind(SyntaxKind.ExternAliasDirective)) { - return nextToken.Parent.Kind() == SyntaxKind.ExternAliasDirective ? 1 : 2; + return nextToken.Parent.IsKind(SyntaxKind.ExternAliasDirective) ? 1 : 2; } else { @@ -743,7 +745,7 @@ private static bool EndsInLineBreak(SyntaxTrivia trivia) if (trivia.HasStructure) { - var node = trivia.GetStructure(); + var node = trivia.GetStructure()!; var trailing = node.GetTrailingTrivia(); if (trailing.Count > 0) { @@ -800,7 +802,7 @@ private static int GetDeclarationDepth(SyntaxTrivia trivia) return GetDeclarationDepth((SyntaxToken)trivia.Token); } - private static int GetDeclarationDepth(SyntaxNode node) + private static int GetDeclarationDepth(SyntaxNode? node) { if (node != null) { diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs index 8eda2ae638bdb..045f565e8b49c 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Text; @@ -15,12 +18,12 @@ internal static class SyntaxReplacer { internal static SyntaxNode Replace( SyntaxNode root, - IEnumerable nodes = null, - Func computeReplacementNode = null, - IEnumerable tokens = null, - Func computeReplacementToken = null, - IEnumerable trivia = null, - Func computeReplacementTrivia = null) + IEnumerable? nodes = null, + Func? computeReplacementNode = null, + IEnumerable? tokens = null, + Func? computeReplacementToken = null, + IEnumerable? trivia = null, + Func? computeReplacementTrivia = null) where TNode : SyntaxNode { var replacer = new Replacer( @@ -40,12 +43,12 @@ internal static SyntaxNode Replace( internal static SyntaxToken Replace( SyntaxToken root, - IEnumerable nodes = null, - Func computeReplacementNode = null, - IEnumerable tokens = null, - Func computeReplacementToken = null, - IEnumerable trivia = null, - Func computeReplacementTrivia = null) + IEnumerable? nodes = null, + Func? computeReplacementNode = null, + IEnumerable? tokens = null, + Func? computeReplacementToken = null, + IEnumerable? trivia = null, + Func? computeReplacementTrivia = null) { var replacer = new Replacer( nodes, computeReplacementNode, @@ -64,9 +67,9 @@ internal static SyntaxToken Replace( private class Replacer : CSharpSyntaxRewriter where TNode : SyntaxNode { - private readonly Func _computeReplacementNode; - private readonly Func _computeReplacementToken; - private readonly Func _computeReplacementTrivia; + private readonly Func? _computeReplacementNode; + private readonly Func? _computeReplacementToken; + private readonly Func? _computeReplacementTrivia; private readonly HashSet _nodeSet; private readonly HashSet _tokenSet; @@ -78,12 +81,12 @@ private class Replacer : CSharpSyntaxRewriter where TNode : SyntaxNode private readonly bool _shouldVisitTrivia; public Replacer( - IEnumerable nodes, - Func computeReplacementNode, - IEnumerable tokens, - Func computeReplacementToken, - IEnumerable trivia, - Func computeReplacementTrivia) + IEnumerable? nodes, + Func? computeReplacementNode, + IEnumerable? tokens, + Func? computeReplacementToken, + IEnumerable? trivia, + Func? computeReplacementTrivia) { _computeReplacementNode = computeReplacementNode; _computeReplacementToken = computeReplacementToken; @@ -175,9 +178,10 @@ private bool ShouldVisit(TextSpan span) return false; } - public override SyntaxNode Visit(SyntaxNode node) + [return: NotNullIfNotNull("node")] + public override SyntaxNode? Visit(SyntaxNode? node) { - SyntaxNode rewritten = node; + SyntaxNode? rewritten = node; if (node != null) { @@ -188,7 +192,7 @@ public override SyntaxNode Visit(SyntaxNode node) if (_nodeSet.Contains(node) && _computeReplacementNode != null) { - rewritten = _computeReplacementNode((TNode)node, (TNode)rewritten); + rewritten = _computeReplacementNode((TNode)node, (TNode)rewritten!); } } @@ -322,9 +326,10 @@ private bool ShouldVisit(TextSpan span) return false; } - public override SyntaxNode Visit(SyntaxNode node) + [return: NotNullIfNotNull("node")] + public override SyntaxNode? Visit(SyntaxNode? node) { - SyntaxNode rewritten = node; + SyntaxNode? rewritten = node; if (node != null) { @@ -377,7 +382,8 @@ public NodeListEditor( _newNodes = replacementNodes; } - public override SyntaxNode Visit(SyntaxNode node) + [return: NotNullIfNotNull("node")] + public override SyntaxNode? Visit(SyntaxNode? node) { if (node == _originalNode) { diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxTreeDiagnosticEnumerator.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxTreeDiagnosticEnumerator.cs index 7f09d5366a321..0c4c22046875b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxTreeDiagnosticEnumerator.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxTreeDiagnosticEnumerator.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using System; using System.Diagnostics; using Microsoft.CodeAnalysis.Text; @@ -13,13 +15,13 @@ namespace Microsoft.CodeAnalysis.CSharp /// internal struct SyntaxTreeDiagnosticEnumerator { - private readonly SyntaxTree _syntaxTree; + private readonly SyntaxTree? _syntaxTree; private NodeIterationStack _stack; - private Diagnostic _current; + private Diagnostic? _current; private int _position; private const int DefaultStackCapacity = 8; - internal SyntaxTreeDiagnosticEnumerator(SyntaxTree syntaxTree, GreenNode node, int position) + internal SyntaxTreeDiagnosticEnumerator(SyntaxTree syntaxTree, GreenNode? node, int position) { _syntaxTree = null; _current = null; @@ -58,6 +60,7 @@ public bool MoveNext() int leadingWidthAlreadyCounted = node.IsToken ? node.GetLeadingTriviaWidth() : 0; // don't produce locations outside of tree span + Debug.Assert(_syntaxTree is object); var length = _syntaxTree.GetRoot().FullSpan.Length; var spanStart = Math.Min(_position - leadingWidthAlreadyCounted + sdi.Offset, length); var spanWidth = Math.Min(spanStart + sdi.Width, length) - spanStart; @@ -107,7 +110,7 @@ public bool MoveNext() /// public Diagnostic Current { - get { return _current; } + get { Debug.Assert(_current is object); return _current; } } private struct NodeIteration diff --git a/src/Compilers/CSharp/Portable/Syntax/ThrowStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ThrowStatementSyntax.cs index e6798c0de46c4..f0b1445d11b01 100644 --- a/src/Compilers/CSharp/Portable/Syntax/ThrowStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/ThrowStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/TryStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/TryStatementSyntax.cs index 48ff0334a6a86..4ffc88b713682 100644 --- a/src/Compilers/CSharp/Portable/Syntax/TryStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/TryStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/TypeDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/TypeDeclarationSyntax.cs index a4209bb26f1c9..6b9fbbebea061 100644 --- a/src/Compilers/CSharp/Portable/Syntax/TypeDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/TypeDeclarationSyntax.cs @@ -5,10 +5,7 @@ #nullable enable using System; -using System.Diagnostics; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs index 115be06a438a5..5a1ac32a8549a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/TypeSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp.Syntax { public abstract partial class TypeSyntax diff --git a/src/Compilers/CSharp/Portable/Syntax/UnsafeStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/UnsafeStatementSyntax.cs index 9fff878b2b648..4ad31257f5031 100644 --- a/src/Compilers/CSharp/Portable/Syntax/UnsafeStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/UnsafeStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/UsingStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/UsingStatementSyntax.cs index 9feda6b733cda..a048169aaa6e3 100644 --- a/src/Compilers/CSharp/Portable/Syntax/UsingStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/UsingStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/WhileStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/WhileStatementSyntax.cs index a29071e434bf8..f2430647dae3b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/WhileStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/WhileStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Portable/Syntax/XmlNameAttributeElementKind.cs b/src/Compilers/CSharp/Portable/Syntax/XmlNameAttributeElementKind.cs index 6135a8ebc023e..90e28b80830eb 100644 --- a/src/Compilers/CSharp/Portable/Syntax/XmlNameAttributeElementKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/XmlNameAttributeElementKind.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; diff --git a/src/Compilers/CSharp/Portable/Syntax/YieldStatementSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/YieldStatementSyntax.cs index 90f70a7fce803..d19b616bf186b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/YieldStatementSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/YieldStatementSyntax.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs index ecafaa59f5743..60587d23a1711 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests.cs @@ -533,6 +533,27 @@ static class Program ); } + [Fact] + public void TestNullAsParamsArgument() + { + var comp = CreateCompilationWithMscorlib46(@" +using System; + +class MarkAttribute : Attribute +{ + public MarkAttribute(params object[] b) + { + } +} + +[Mark(null)] +static class Program +{ +}"); + comp.VerifyDiagnostics( + ); + } + [Fact] [WorkItem(20741, "https://github.com/dotnet/roslyn/issues/20741")] public void TestNamedArgumentOnOrderedObjectParamsArgument() diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Security.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Security.cs index a66488dd5fe52..bb0db3e58dc04 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Security.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_Security.cs @@ -1520,6 +1520,42 @@ public class MyClass Diagnostic(ErrorCode.ERR_PermissionSetAttributeInvalidFile, "File = null").WithArguments("", "File").WithLocation(5, 46)); } + [Fact] + public void CS7056ERR_PermissionSetAttributeInvalidFile_WithXmlReferenceResolver() + { + var tempDir = Temp.CreateDirectory(); + var tempFile = tempDir.CreateFile("pset.xml"); + + string text = @" + +"; + + tempFile.WriteAllText(text); + + string source = @" +using System.Security.Permissions; + +[PermissionSetAttribute(SecurityAction.Deny, File = @""NonExistentFile.xml"")] +[PermissionSetAttribute(SecurityAction.Deny, File = null)] +public class MyClass +{ +}"; + var resolver = new XmlFileResolver(tempDir.Path); + CreateCompilationWithMscorlib40(source, options: TestOptions.DebugDll.WithXmlReferenceResolver(resolver)).VerifyDiagnostics( + // (4,25): warning CS0618: 'System.Security.Permissions.SecurityAction.Deny' is obsolete: 'Deny is obsolete and will be removed in a future release of the .NET Framework. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.' + // [PermissionSetAttribute(SecurityAction.Deny, File = @"NonExistentFile.xml")] + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "SecurityAction.Deny").WithArguments("System.Security.Permissions.SecurityAction.Deny", "Deny is obsolete and will be removed in a future release of the .NET Framework. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information."), + // (5,25): warning CS0618: 'System.Security.Permissions.SecurityAction.Deny' is obsolete: 'Deny is obsolete and will be removed in a future release of the .NET Framework. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.' + // [PermissionSetAttribute(SecurityAction.Deny, File = null)] + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "SecurityAction.Deny").WithArguments("System.Security.Permissions.SecurityAction.Deny", "Deny is obsolete and will be removed in a future release of the .NET Framework. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information."), + // (4,46): error CS7056: Unable to resolve file path 'NonExistentFile.xml' specified for the named argument 'File' for PermissionSet attribute + // [PermissionSetAttribute(SecurityAction.Deny, File = @"NonExistentFile.xml")] + Diagnostic(ErrorCode.ERR_PermissionSetAttributeInvalidFile, @"File = @""NonExistentFile.xml""").WithArguments("NonExistentFile.xml", "File").WithLocation(4, 46), + // (5,46): error CS7056: Unable to resolve file path '' specified for the named argument 'File' for PermissionSet attribute + // [PermissionSetAttribute(SecurityAction.Deny, File = null)] + Diagnostic(ErrorCode.ERR_PermissionSetAttributeInvalidFile, "File = null").WithArguments("", "File").WithLocation(5, 46)); + } + [WorkItem(545084, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545084"), WorkItem(529492, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529492")] [Fact] public void CS7057ERR_PermissionSetAttributeFileReadError() diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs index 0162b5c8e6453..904a59041f540 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonAttributeData.cs @@ -285,11 +285,12 @@ private ObsoleteAttributeData DecodeObsoleteAttribute() // ObsoleteAttribute(string, bool) Debug.Assert(args.Length <= 2); - message = (string)args[0].ValueInternal; + message = (string?)args[0].ValueInternal; if (args.Length == 2) { - isError = (bool)args[1].ValueInternal; + Debug.Assert(args[1].ValueInternal is object); + isError = (bool)args[1].ValueInternal!; } } @@ -341,8 +342,9 @@ private ObsoleteAttributeData DecodeDeprecatedAttribute() // DeprecatedAttribute(String, DeprecationType, UInt32, Platform) // DeprecatedAttribute(String, DeprecationType, UInt32, String) - message = (string)args[0].ValueInternal; - isError = ((int)args[1].ValueInternal == 1); + Debug.Assert(args[1].ValueInternal is object); + message = (string?)args[0].ValueInternal; + isError = ((int)args[1].ValueInternal! == 1); } return new ObsoleteAttributeData(ObsoleteAttributeKind.Deprecated, message, isError, diagnosticId: null, urlFormat: null); @@ -537,6 +539,7 @@ internal static AttributeUsageInfo DecodeAttributeUsageAttribute(TypedConstant p // // See Roslyn Bug 8603: ETA crashes with InvalidOperationException on duplicate attributes for details. + Debug.Assert(positionalArg.ValueInternal is object); var validOn = (AttributeTargets)positionalArg.ValueInternal; bool allowMultiple = DecodeNamedArgument(namedArgs, "AllowMultiple", SpecialType.System_Boolean, false); bool inherited = DecodeNamedArgument(namedArgs, "Inherited", SpecialType.System_Boolean, true); diff --git a/src/Compilers/Core/Portable/Symbols/TypedConstant.cs b/src/Compilers/Core/Portable/Symbols/TypedConstant.cs index 8ae68df2c2132..0321183883401 100644 --- a/src/Compilers/Core/Portable/Symbols/TypedConstant.cs +++ b/src/Compilers/Core/Portable/Symbols/TypedConstant.cs @@ -19,13 +19,15 @@ namespace Microsoft.CodeAnalysis public struct TypedConstant : IEquatable { private readonly TypedConstantKind _kind; - private readonly ITypeSymbolInternal _type; + private readonly ITypeSymbolInternal? _type; private readonly object? _value; - internal TypedConstant(ITypeSymbolInternal type, TypedConstantKind kind, object? value) + internal TypedConstant(ITypeSymbolInternal? type, TypedConstantKind kind, object? value) { Debug.Assert(kind == TypedConstantKind.Array || !(value is ImmutableArray)); Debug.Assert(!(value is ISymbol) || value is ISymbolInternal); + Debug.Assert(type is object || kind == TypedConstantKind.Error); + _kind = kind; _type = type; _value = value; @@ -48,12 +50,12 @@ public TypedConstantKind Kind /// Returns the of the constant, /// or null if the type can't be determined (error). /// - public ITypeSymbol Type + public ITypeSymbol? Type { - get { return _type.GetITypeSymbol(); } + get { return _type?.GetITypeSymbol(); } } - internal ITypeSymbolInternal TypeInternal + internal ITypeSymbolInternal? TypeInternal { get { return _type; } } @@ -90,7 +92,7 @@ public object? Value /// /// Unlike returns when the value is a symbol. /// - internal object ValueInternal + internal object? ValueInternal { get { @@ -99,7 +101,7 @@ internal object ValueInternal throw new InvalidOperationException("TypedConstant is an array. Use Values property."); } - return _value!; + return _value; } } @@ -131,7 +133,7 @@ internal T DecodeValue(SpecialType specialType) return value; } - internal bool TryDecodeValue(SpecialType specialType, [MaybeNull][NotNullWhen(returnValue: true)] out T value) + internal bool TryDecodeValue(SpecialType specialType, [MaybeNull] out T value) { if (_kind == TypedConstantKind.Error) { @@ -139,9 +141,9 @@ internal bool TryDecodeValue(SpecialType specialType, [MaybeNull][NotNullWhen return false; } - if (_type.SpecialType == specialType || (_type.TypeKind == TypeKind.Enum && specialType == SpecialType.System_Enum)) + if (_type!.SpecialType == specialType || (_type.TypeKind == TypeKind.Enum && specialType == SpecialType.System_Enum)) { - value = (T)_value!; + value = (T)_value; return true; } diff --git a/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs b/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs index 8749e03951cad..8319c13504b80 100644 --- a/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs +++ b/src/Compilers/Core/Portable/Syntax/LineDirectiveMap.LineMappingEntry.cs @@ -76,7 +76,7 @@ public LineMappingEntry(int unmappedLine) public LineMappingEntry( int unmappedLine, int mappedLine, - string mappedPathOpt, + string? mappedPathOpt, PositionState state) { this.UnmappedLine = unmappedLine; diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNavigator.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNavigator.cs index ecaf05bd916cc..bb7a289007864 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNavigator.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNavigator.cs @@ -128,7 +128,7 @@ internal SyntaxToken GetFirstToken(SyntaxNode current, Func? } } - return default(SyntaxToken); + return default; } finally { @@ -175,7 +175,7 @@ internal SyntaxToken GetLastToken(SyntaxNode current, Func pr } } - return default(SyntaxToken); + return default; } finally { @@ -202,7 +202,7 @@ private SyntaxToken GetFirstToken( } } - return default(SyntaxToken); + return default; } private SyntaxToken GetLastToken( @@ -221,7 +221,7 @@ private SyntaxToken GetLastToken( } } - return default(SyntaxToken); + return default; } private bool TryGetLastTokenForStructuredTrivia( @@ -230,7 +230,7 @@ private bool TryGetLastTokenForStructuredTrivia( Func? stepInto, out SyntaxToken token) { - token = default(SyntaxToken); + token = default; if (!trivia.TryGetStructure(out var structure) || stepInto == null || !stepInto(trivia)) { @@ -273,7 +273,7 @@ private SyntaxToken GetFirstToken( } } - return default(SyntaxToken); + return default; } private SyntaxToken GetLastToken( @@ -307,12 +307,12 @@ private SyntaxToken GetLastToken( } } - return default(SyntaxToken); + return default; } internal SyntaxToken GetNextToken( SyntaxTrivia current, - Func predicate, + Func? predicate, Func? stepInto) { bool returnNext = false; @@ -377,7 +377,7 @@ internal SyntaxToken GetPreviousToken( private SyntaxToken GetNextToken( SyntaxTrivia current, SyntaxTriviaList list, - Func predicate, + Func? predicate, Func? stepInto, ref bool returnNext) { @@ -400,7 +400,7 @@ private SyntaxToken GetNextToken( } } - return default(SyntaxToken); + return default; } private SyntaxToken GetPreviousToken( @@ -426,12 +426,12 @@ private SyntaxToken GetPreviousToken( } } - return default(SyntaxToken); + return default; } internal SyntaxToken GetNextToken( SyntaxNode node, - Func predicate, + Func? predicate, Func? stepInto) { while (node.Parent != null) @@ -476,7 +476,7 @@ internal SyntaxToken GetNextToken( return GetNextToken(((IStructuredTriviaSyntax)node).ParentTrivia, predicate, stepInto); } - return default(SyntaxToken); + return default; } internal SyntaxToken GetPreviousToken( @@ -526,10 +526,10 @@ internal SyntaxToken GetPreviousToken( return GetPreviousToken(((IStructuredTriviaSyntax)node).ParentTrivia, predicate, stepInto); } - return default(SyntaxToken); + return default; } - internal SyntaxToken GetNextToken(in SyntaxToken current, Func predicate, bool searchInsideCurrentTokenTrailingTrivia, Func? stepInto) + internal SyntaxToken GetNextToken(in SyntaxToken current, Func? predicate, bool searchInsideCurrentTokenTrailingTrivia, Func? stepInto) { Debug.Assert(searchInsideCurrentTokenTrailingTrivia == false || stepInto != null); if (current.Parent != null) @@ -579,7 +579,7 @@ internal SyntaxToken GetNextToken(in SyntaxToken current, Func predicate, bool searchInsideCurrentTokenLeadingTrivia, @@ -633,7 +633,7 @@ internal SyntaxToken GetPreviousToken(in SyntaxToken current, Func( /// /// Creates a new tree of nodes with the specified node removed. /// - protected internal abstract SyntaxNode RemoveNodesCore( + protected internal abstract SyntaxNode? RemoveNodesCore( IEnumerable nodes, SyntaxRemoveOptions options); diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs index b2d21d0c03cca..79ad33a0c1fbd 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNodeExtensions.cs @@ -261,12 +261,12 @@ public static TRoot ReplaceTrivia(this TRoot root, SyntaxTrivia trivia, S /// The root node from which to remove a descendant node from. /// The node to remove. /// Options that determine how the node's trivia is treated. - public static TRoot RemoveNode(this TRoot root, + public static TRoot? RemoveNode(this TRoot root, SyntaxNode node, SyntaxRemoveOptions options) where TRoot : SyntaxNode { - return (TRoot)root.RemoveNodesCore(new[] { node }, options); + return (TRoot?)root.RemoveNodesCore(new[] { node }, options); } /// @@ -276,13 +276,13 @@ public static TRoot RemoveNode(this TRoot root, /// The root node from which to remove a descendant node from. /// The nodes to remove. /// Options that determine how the nodes' trivia is treated. - public static TRoot RemoveNodes( + public static TRoot? RemoveNodes( this TRoot root, IEnumerable nodes, SyntaxRemoveOptions options) where TRoot : SyntaxNode { - return (TRoot)root.RemoveNodesCore(nodes, options); + return (TRoot?)root.RemoveNodesCore(nodes, options); } internal const string DefaultIndentation = " "; diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs b/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs index 2ff1b5674864e..587df78df25dd 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxTree.cs @@ -102,7 +102,7 @@ public virtual ImmutableDictionary DiagnosticOptions /// /// The text encoding of the source document. /// - public abstract Encoding Encoding { get; } + public abstract Encoding? Encoding { get; } /// /// Gets the text of the source document asynchronously. diff --git a/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingErrorTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingErrorTests.vb index 8626ed1c10094..204f234758be6 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingErrorTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Binding/BindingErrorTests.vb @@ -1166,6 +1166,12 @@ Imports System ). VerifyDiagnostics(Diagnostic(ERRID.ERR_BadAttributeConstructor1, "myattr1").WithArguments("M1.c1()"), Diagnostic(ERRID.ERR_BadAttributeConstructor1, "myattr2").WithArguments("M1.delegate1()")) + + Dim scen18 = compilation.GlobalNamespace.GetTypeMember("M1").GetTypeMember("Scen18") + Dim attribute = scen18.GetAttributes().Single() + Assert.Equal("M1.myattr1(Nothing)", attribute.ToString()) + Dim argument = attribute.CommonConstructorArguments(0) + Assert.Null(argument.Type) End Sub diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index ccc1eecf89b38..2ea840063fa1a 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -545,7 +545,7 @@ internal override bool IsClosureScope(SyntaxNode node) protected override IEnumerable GetLambdaBodyExpressionsAndStatements(SyntaxNode lambdaBody) => SpecializedCollections.SingletonEnumerable(lambdaBody); - protected override SyntaxNode TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) + protected override SyntaxNode? TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) => LambdaUtilities.TryGetCorrespondingLambdaBody(oldBody, newLambda); protected override Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit) @@ -1083,7 +1083,7 @@ internal override bool IsLocalFunction(SyntaxNode node) internal override bool IsNestedFunction(SyntaxNode node) => node is LambdaExpressionSyntax || node is LocalFunctionStatementSyntax; - internal override bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2) + internal override bool TryGetLambdaBodies(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? body1, out SyntaxNode? body2) => LambdaUtilities.TryGetLambdaBodies(node, out body1, out body2); internal override SyntaxNode GetLambda(SyntaxNode lambdaBody) diff --git a/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs b/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs index e47024c0dc9e0..44e2178780b81 100644 --- a/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs +++ b/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -52,6 +53,7 @@ private async Task UpdateDocumentAsync( var nodesToRemove = sections.Skip(1).Select(s => s.SyntaxToRemove).Where(s => s.Parent == ifStatement.Parent); root = root.RemoveNodes(nodesToRemove, SyntaxRemoveOptions.KeepNoTrivia); + Debug.Assert(root is object); // we didn't remove the root root = root.ReplaceNode(root.FindNode(ifSpan, getInnermostNodeForTie: true), @switch); return document.WithSyntaxRoot(root); } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 24d544e61ee00..cc2db6cb3d6a2 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -168,7 +168,7 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, TextSpan span, out /// protected abstract IEnumerable GetLambdaBodyExpressionsAndStatements(SyntaxNode lambdaBody); - protected abstract SyntaxNode TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda); + protected abstract SyntaxNode? TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda); protected abstract Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit); protected abstract Match ComputeBodyMatch(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable>? knownMatches); @@ -344,7 +344,7 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind /// /// Some lambda queries (group by, join by) have two bodies. /// - internal abstract bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2); + internal abstract bool TryGetLambdaBodies(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? body1, out SyntaxNode? body2); internal abstract bool IsStateMachineMethod(SyntaxNode declaration); internal abstract SyntaxNode? TryGetContainingTypeDeclaration(SyntaxNode node); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs index 4dedb96972c51..230840efc068c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/SuppressFormattingRule.cs @@ -339,7 +339,7 @@ static void ProcessStructuredTrivia(List list, SyntaxNode str } } - private static bool IsFormatDirective(DirectiveTriviaSyntax trivia, SyntaxKind disableOrRestoreKeyword) + private static bool IsFormatDirective(DirectiveTriviaSyntax? trivia, SyntaxKind disableOrRestoreKeyword) { if (!(trivia is PragmaWarningDirectiveTriviaSyntax pragmaWarningDirectiveTrivia)) {