diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4ea77816e3aab..49c955a19b4a3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: true contact_links: + - name: Report issues related to CAxxxx rules + url: https://github.com/dotnet/roslyn-analyzers/issues/new/choose + about: Enhancements and bug reports to CAxxxx rules are reported to dotnet/roslyn-analyzers repository. - name: Suggest language feature url: https://github.com/dotnet/csharplang/issues/new/choose about: Language feature suggestions are discussed in dotnet/csharplang repository first. diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index f16df6c5ce694..35bb7e5b7e90f 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -59,6 +59,8 @@ variables: value: true - name: Insertion.TitlePrefix value: '[Auto Insertion]' + - name: Insertion.TitleSuffix + value: '[Skip-SymbolCheck]' stages: @@ -296,7 +298,6 @@ stages: - stage: insert dependsOn: - build - - publish_using_darc displayName: Insert to VS jobs: @@ -361,6 +362,7 @@ stages: -insertionCount "1" ` -insertToolset "$(Insertion.InsertToolset)" ` -titlePrefix "$(Insertion.TitlePrefix)" ` + -titleSuffix "$(Insertion.TitleSuffix)" ` -queueValidation "true" ` -requiredValueSentinel "REQUIRED" ` -reviewerGUID "6c25b447-1d90-4840-8fde-d8b22cb8733e" ` diff --git a/azure-pipelines-richnav.yml b/azure-pipelines-richnav.yml index 7f37752a94118..d327060f6c65e 100644 --- a/azure-pipelines-richnav.yml +++ b/azure-pipelines-richnav.yml @@ -7,12 +7,14 @@ trigger: - demos/* # Branches that trigger builds on PR -pr: -- main -- main-vs-deps -- release/* -- features/* -- demos/* +pr: none +# Temporarily disabling richnav job on PRs +# pr: +# - main +# - main-vs-deps +# - release/* +# - features/* +# - demos/* jobs: - job: RichCodeNav_Indexing diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 629edf6934c73..e3d71b5799150 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -18,9 +18,9 @@ 78da7776965b428ff31da8f1ff2cb073506212b7 - + https://github.com/dotnet/roslyn - 03a07d1dd606ce11d62c9a595041c4c2d44c39e3 + ca27d128f3533dc41a46b010b8e878916328f2e4 https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index c2b3f72ab58ce..1a88275962197 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -23,7 +23,7 @@ - 4.0.0-2.21327.4 + 4.0.0-2.21359.14 diff --git a/eng/config/PublishData.json b/eng/config/PublishData.json index 72df9d4da8fbc..b51b144524a5b 100644 --- a/eng/config/PublishData.json +++ b/eng/config/PublishData.json @@ -196,7 +196,7 @@ "version": "4.0.*", "packageFeeds": "default", "channels": [], - "vsBranch": "main", + "vsBranch": "rel/d17.0", "vsMajorVersion": 17, "insertionTitlePrefix": "[d17p2]" }, @@ -210,8 +210,7 @@ "channels": [], "vsBranch": "main", "vsMajorVersion": 17, - "insertionCreateDraftPR": true, - "insertionTitlePrefix": "[d17p3]" + "insertionCreateDraftPR": false }, "main": { "nugetKind": [ diff --git a/eng/pipelines/test-unix-job.yml b/eng/pipelines/test-unix-job.yml index 8235d6ac21809..add1171b002a6 100644 --- a/eng/pipelines/test-unix-job.yml +++ b/eng/pipelines/test-unix-job.yml @@ -27,7 +27,7 @@ jobs: # a thin client that kicks off a helix job and waits for it to complete. # Thus we don't use a helix queue to run the job here, and instead use the plentiful AzDO vmImages. vmImage: ubuntu-20.04 - timeoutInMinutes: 40 + timeoutInMinutes: 90 steps: - checkout: none diff --git a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs index 2f36df1f646c4..df534932a3bdc 100644 --- a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs @@ -119,7 +119,7 @@ protected override async Task FixAllAsync( private sealed class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(CSharpAnalyzersResources.Convert_switch_statement_to_expression, createChangedDocument) + : base(CSharpAnalyzersResources.Convert_switch_statement_to_expression, createChangedDocument, nameof(CSharpAnalyzersResources.Convert_switch_statement_to_expression)) { } } diff --git a/src/Analyzers/CSharp/CodeFixes/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs index 5665e0045e9fc..b106d7ad42428 100644 --- a/src/Analyzers/CSharp/CodeFixes/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessCodeFixProvider.cs @@ -166,7 +166,7 @@ private static T AppendTriviaWithoutEndOfLines(T newStatement, IfStatementSyn private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(CSharpAnalyzersResources.Delegate_invocation_can_be_simplified, createChangedDocument) + : base(CSharpAnalyzersResources.Delegate_invocation_can_be_simplified, createChangedDocument, nameof(CSharpAnalyzersResources.Delegate_invocation_can_be_simplified)) { } } diff --git a/src/Analyzers/CSharp/CodeFixes/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableCodeFixProvider.cs index de4f87e2e1570..4051c106e79f5 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableCodeFixProvider.cs @@ -79,7 +79,7 @@ protected override Task FixAllAsync( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(CSharpAnalyzersResources.Make_readonly_fields_writable, createChangedDocument) + : base(CSharpAnalyzersResources.Make_readonly_fields_writable, createChangedDocument, nameof(CSharpAnalyzersResources.Make_readonly_fields_writable)) { } } diff --git a/src/Analyzers/CSharp/CodeFixes/MisplacedUsingDirectives/MisplacedUsingDirectivesCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MisplacedUsingDirectives/MisplacedUsingDirectivesCodeFixProvider.cs index 46728cfa09c7e..0c4c0153d8bbc 100644 --- a/src/Analyzers/CSharp/CodeFixes/MisplacedUsingDirectives/MisplacedUsingDirectivesCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MisplacedUsingDirectives/MisplacedUsingDirectivesCodeFixProvider.cs @@ -423,7 +423,7 @@ private static CompilationUnitSyntax AddFileHeader(CompilationUnitSyntax compila private class MoveMisplacedUsingsCodeAction : CustomCodeActions.DocumentChangeAction { public MoveMisplacedUsingsCodeAction(Func> createChangedDocument) - : base(CSharpAnalyzersResources.Move_misplaced_using_directives, createChangedDocument) + : base(CSharpAnalyzersResources.Move_misplaced_using_directives, createChangedDocument, nameof(CSharpAnalyzersResources.Move_misplaced_using_directives)) { } } diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs index b69ab50ccc5b5..69309be37e093 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs @@ -41,7 +41,6 @@ public CSharpRemoveUnnecessaryCastCodeFixProvider() public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { context.RegisterCodeFix(new MyCodeAction( - AnalyzersResources.Remove_Unnecessary_Cast, c => FixAsync(context.Document, context.Diagnostics.First(), c)), context.Diagnostics); return Task.CompletedTask; @@ -98,8 +97,8 @@ private ExpressionSyntax Recurse(ExpressionSyntax old) private class MyCodeAction : CustomCodeActions.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public MyCodeAction(Func> createChangedDocument) + : base(AnalyzersResources.Remove_Unnecessary_Cast, createChangedDocument, nameof(AnalyzersResources.Remove_Unnecessary_Cast)) { } } diff --git a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs index e09d8c8075911..10f7e8738ac58 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs @@ -108,14 +108,14 @@ private class MyCodeAction : CustomCodeActions.DocumentChangeAction { #if CODE_STYLE // 'CodeActionPriority' is not a public API, hence not supported in CodeStyle layer. public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } #else internal override CodeActionPriority Priority { get; } public MyCodeAction(string title, CodeActionPriority priority, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { Priority = priority; } diff --git a/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs index 91542e600ea95..97f67c73cc435 100644 --- a/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs @@ -105,7 +105,7 @@ private static DeclarationModifiers WithReadOnly(DeclarationModifiers modifiers) private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Add_readonly_modifier, createChangedDocument) + : base(AnalyzersResources.Add_readonly_modifier, createChangedDocument, nameof(AnalyzersResources.Add_readonly_modifier)) { } } diff --git a/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.CustomFixAllProvider.cs b/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.CustomFixAllProvider.cs index 68e4558b8d6af..28cfe57790cb0 100644 --- a/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.CustomFixAllProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.CustomFixAllProvider.cs @@ -39,8 +39,9 @@ private class CustomFixAllProvider : FixAllProvider if (diagnostics.IsDefaultOrEmpty) return null; + var title = FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext); return new MyCodeAction( - FixAllContextHelper.GetDefaultFixAllTitle(fixAllContext), + title, cancellationToken => FixAllByDocumentAsync( fixAllContext.Project.Solution, diagnostics, diff --git a/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.cs index 94a21a782d2e5..af0a10d370e09 100644 --- a/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MatchFolderAndNamespace/AbstractChangeNamespaceToMatchFolderCodeFixProvider.cs @@ -65,7 +65,7 @@ private static async Task FixAllInDocumentAsync(Document document, Imm private sealed class MyCodeAction : CustomCodeActions.SolutionChangeAction { public MyCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs index 5f4f22e75a230..46b4b9eeced33 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsCodeFixProvider.cs @@ -44,7 +44,7 @@ private static Task RemoveUnnecessaryImportsAsync( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnusedMembers/AbstractRemoveUnusedMembersCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnusedMembers/AbstractRemoveUnusedMembersCodeFixProvider.cs index 35f65b278b8ec..84341bcf02663 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnusedMembers/AbstractRemoveUnusedMembersCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnusedMembers/AbstractRemoveUnusedMembersCodeFixProvider.cs @@ -128,7 +128,7 @@ protected static void AdjustAndAddAppropriateDeclaratorsToRemove(SyntaxNode pare private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Remove_unused_member, createChangedDocument) + : base(AnalyzersResources.Remove_unused_member, createChangedDocument, nameof(AnalyzersResources.Remove_unused_member)) { } } diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs index e1ea32784b3f7..6b6179144cba3 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionCodeFixProvider.cs @@ -115,7 +115,7 @@ private static SyntaxNode GetCoalesceExpression( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Use_coalesce_expression, createChangedDocument) + : base(AnalyzersResources.Use_coalesce_expression, createChangedDocument, nameof(AnalyzersResources.Use_coalesce_expression)) { } diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs index 38e6ed223e9b6..3c2be7100994a 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableCodeFixProvider.cs @@ -91,7 +91,7 @@ protected override async Task FixAllAsync( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Use_coalesce_expression, createChangedDocument) + : base(AnalyzersResources.Use_coalesce_expression, createChangedDocument, nameof(AnalyzersResources.Use_coalesce_expression)) { } diff --git a/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs index f89e0935c7182..3761246a2e89f 100644 --- a/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs @@ -126,7 +126,7 @@ protected abstract TStatementSyntax GetNewStatement( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Collection_initialization_can_be_simplified, createChangedDocument) + : base(AnalyzersResources.Collection_initialization_can_be_simplified, createChangedDocument, nameof(AnalyzersResources.Collection_initialization_can_be_simplified)) { } } diff --git a/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs index 3b017bc2368fe..df4b76985f2c6 100644 --- a/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs @@ -162,7 +162,7 @@ private SyntaxNode CreateConditionalAccessExpression( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Use_null_propagation, createChangedDocument) + : base(AnalyzersResources.Use_null_propagation, createChangedDocument, nameof(AnalyzersResources.Use_null_propagation)) { } } diff --git a/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs index 5c9ef89534387..61b1b16d8ed99 100644 --- a/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs @@ -125,7 +125,7 @@ protected abstract TStatementSyntax GetNewStatement( private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(AnalyzersResources.Object_initialization_can_be_simplified, createChangedDocument) + : base(AnalyzersResources.Object_initialization_can_be_simplified, createChangedDocument, nameof(AnalyzersResources.Object_initialization_can_be_simplified)) { } } diff --git a/src/Analyzers/Core/CodeFixes/UseThrowExpression/UseThrowExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseThrowExpression/UseThrowExpressionCodeFixProvider.cs index 24805a595c8e9..7b6c672038951 100644 --- a/src/Analyzers/Core/CodeFixes/UseThrowExpression/UseThrowExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseThrowExpression/UseThrowExpressionCodeFixProvider.cs @@ -76,7 +76,7 @@ private class MyCodeAction : CustomCodeActions.DocumentChangeAction { public MyCodeAction( Func> createChangedDocument) - : base(AnalyzersResources.Use_throw_expression, createChangedDocument) + : base(AnalyzersResources.Use_throw_expression, createChangedDocument, nameof(AnalyzersResources.Use_throw_expression)) { } } diff --git a/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryByVal/VisualBasicRemoveUnnecessaryByValCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryByVal/VisualBasicRemoveUnnecessaryByValCodeFixProvider.vb index 3df6572f0ffd1..bd730f1af920b 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryByVal/VisualBasicRemoveUnnecessaryByValCodeFixProvider.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryByVal/VisualBasicRemoveUnnecessaryByValCodeFixProvider.vb @@ -31,7 +31,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task For Each diagnostic In context.Diagnostics context.RegisterCodeFix(New MyCodeAction( - VisualBasicAnalyzersResources.Remove_ByVal, Function(ct) FixAsync(context.Document, diagnostic, ct)), diagnostic) Next @@ -51,8 +50,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal Private Class MyCodeAction Inherits CustomCodeActions.DocumentChangeAction - Friend Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document))) - MyBase.New(title, createChangedDocument) + Friend Sub New(createChangedDocument As Func(Of CancellationToken, Task(Of Document))) + MyBase.New(VisualBasicAnalyzersResources.Remove_ByVal, createChangedDocument, NameOf(VisualBasicAnalyzersResources.Remove_ByVal)) End Sub End Class End Class diff --git a/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryCast/VisualBasicRemoveUnnecessaryCastCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryCast/VisualBasicRemoveUnnecessaryCastCodeFixProvider.vb index fc9d448a43b79..1a3347ddc78c0 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryCast/VisualBasicRemoveUnnecessaryCastCodeFixProvider.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/RemoveUnnecessaryCast/VisualBasicRemoveUnnecessaryCastCodeFixProvider.vb @@ -38,7 +38,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryCast Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task context.RegisterCodeFix(New MyCodeAction( - AnalyzersResources.Remove_Unnecessary_Cast, Function(c) FixAsync(context.Document, context.Diagnostics.First(), c)), context.Diagnostics) Return Task.CompletedTask @@ -173,8 +172,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryCast Private Class MyCodeAction Inherits CustomCodeActions.DocumentChangeAction - Public Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document))) - MyBase.New(title, createChangedDocument) + Public Sub New(createChangedDocument As Func(Of CancellationToken, Task(Of Document))) + MyBase.New(AnalyzersResources.Remove_Unnecessary_Cast, createChangedDocument, NameOf(AnalyzersResources.Remove_Unnecessary_Cast)) End Sub End Class End Class diff --git a/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs b/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs index f7e4503bb47d1..cb7937b2de234 100644 --- a/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs +++ b/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs @@ -75,8 +75,9 @@ private static void ComputeDeclarations( switch (node.Kind()) { case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: { - var ns = (NamespaceDeclarationSyntax)node; + var ns = (BaseNamespaceDeclarationSyntax)node; foreach (var decl in ns.Members) { ComputeDeclarations(model, associatedSymbol: null, decl, shouldSkip, getSymbol, builder, newLevel, cancellationToken); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 93f4bfed5391f..dbba1849b977e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1261,6 +1261,15 @@ private bool IsBadBaseAccess(SyntaxNode node, BoundExpression receiverOpt, Symbo return false; } + internal static uint GetInterpolatedStringHandlerConversionEscapeScope( + BoundInterpolatedString interpolatedString, + uint scopeOfTheContainingExpression) + { + Debug.Assert(interpolatedString.InterpolationData != null); + var data = interpolatedString.InterpolationData.GetValueOrDefault(); + return GetValEscape(data.Construction, scopeOfTheContainingExpression); + } + /// /// Computes the scope to which the given invocation can escape /// NOTE: the escape scope for ref and val escapes is the same for invocations except for trivial cases (ordinary type returned by val) @@ -2661,6 +2670,11 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin var conversion = (BoundConversion)expr; Debug.Assert(conversion.ConversionKind != ConversionKind.StackAllocToSpanType, "StackAllocToSpanType unexpected"); + if (conversion.ConversionKind == ConversionKind.InterpolatedStringHandler) + { + return GetInterpolatedStringHandlerConversionEscapeScope((BoundInterpolatedString)conversion.Operand, scopeOfTheContainingExpression); + } + return GetValEscape(conversion.Operand, scopeOfTheContainingExpression); case BoundKind.AssignmentOperator: @@ -2717,6 +2731,15 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin // just say it does not escape anywhere, so that we do not get false errors. return scopeOfTheContainingExpression; + case BoundKind.InterpolatedStringHandlerPlaceholder: + // The handler placeholder cannot escape out of the current expression, as it's a compiler-synthesized + // location. + return scopeOfTheContainingExpression; + + case BoundKind.InterpolatedStringArgumentPlaceholder: + // We saved off the safe-to-escape of the argument when we did binding + return ((BoundInterpolatedStringArgumentPlaceholder)expr).ValSafeToEscape; + case BoundKind.DisposableValuePlaceholder: // Disposable value placeholder is only ever used to lookup a pattern dispose method // then immediately discarded. The actual expression will be generated during lowering @@ -2867,6 +2890,14 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint } return true; + case BoundKind.InterpolatedStringArgumentPlaceholder: + if (((BoundInterpolatedStringArgumentPlaceholder)expr).ValSafeToEscape > escapeTo) + { + Error(diagnostics, ErrorCode.ERR_EscapeLocal, node, expr.Syntax); + return false; + } + return true; + case BoundKind.Local: var localSymbol = ((BoundLocal)expr).LocalSymbol; if (localSymbol.ValEscapeScope > escapeTo) @@ -3076,6 +3107,12 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint case BoundKind.Conversion: var conversion = (BoundConversion)expr; Debug.Assert(conversion.ConversionKind != ConversionKind.StackAllocToSpanType, "StackAllocToSpanType unexpected"); + + if (conversion.ConversionKind == ConversionKind.InterpolatedStringHandler) + { + return CheckInterpolatedStringHandlerConversionEscape((BoundInterpolatedString)conversion.Operand, escapeFrom, escapeTo, diagnostics); + } + return CheckValEscape(node, conversion.Operand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); case BoundKind.AssignmentOperator: @@ -3346,6 +3383,40 @@ private static bool CheckValEscape(ImmutableArray expressions, return true; } + private static bool CheckInterpolatedStringHandlerConversionEscape(BoundInterpolatedString interpolatedString, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + { + Debug.Assert(interpolatedString.InterpolationData is not null); + + // We need to check to see if any values could potentially escape outside the max depth via the handler type. + // Consider the case where a ref-struct handler saves off the result of one call to AppendFormatted, + // and then on a subsequent call it either assigns that saved value to another ref struct with a larger + // escape, or does the opposite. In either case, we need to check. + + CheckValEscape(interpolatedString.Syntax, interpolatedString.InterpolationData.GetValueOrDefault().Construction, escapeFrom, escapeTo, checkingReceiver: false, diagnostics); + + foreach (var part in interpolatedString.Parts) + { + if (part is not BoundCall { Method: { Name: "AppendFormatted" } } call) + { + // Dynamic calls cannot have ref struct parameters, and AppendLiteral calls will always have literal + // string arguments and do not require us to be concerned with escape + continue; + } + + // The interpolation component is always the first argument to the method, and it was not passed by name + // so there can be no reordering. + var argument = call.Arguments[0]; + var success = CheckValEscape(argument.Syntax, argument, escapeFrom, escapeTo, checkingReceiver: false, diagnostics); + + if (!success) + { + return false; + } + } + + return true; + } + internal enum AddressKind { // reference may be written to diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index ac2e06add3d45..a6323c00edf77 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -758,7 +758,7 @@ public override Binder VisitInterfaceDeclaration(InterfaceDeclarationSyntax node public override Binder VisitRecordDeclaration(RecordDeclarationSyntax node) => VisitTypeDeclarationCore(node); - public override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent) + public sealed override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent) { if (!LookupPosition.IsInNamespaceDeclaration(_position, parent)) { @@ -774,7 +774,22 @@ public override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax pare return VisitNamespaceDeclaration(parent, _position, inBody, inUsing); } - internal Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent, int position, bool inBody, bool inUsing) + public override Binder VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax parent) + { + if (!LookupPosition.IsInNamespaceDeclaration(_position, parent)) + { + return VisitCore(parent.Parent); + } + + // Anywhere after the `;` is in the 'body' of this namespace. + bool inBody = _position >= parent.SemicolonToken.EndPosition; + + bool inUsing = IsInUsing(parent); + + return VisitNamespaceDeclaration(parent, _position, inBody, inUsing); + } + + internal Binder VisitNamespaceDeclaration(BaseNamespaceDeclarationSyntax parent, int position, bool inBody, bool inUsing) { Debug.Assert(!inUsing || inBody, "inUsing => inBody"); @@ -840,7 +855,7 @@ private static Binder MakeNamespaceBinder(CSharpSyntaxNode node, NameSyntax name NamespaceSymbol ns = ((NamespaceSymbol)container).GetNestedNamespace(name); if ((object)ns == null) return outer; - if (node is NamespaceDeclarationSyntax namespaceDecl) + if (node is BaseNamespaceDeclarationSyntax namespaceDecl) { outer = AddInImportsBinders((SourceNamespaceSymbol)outer.Compilation.SourceModule.GetModuleNamespace(ns), namespaceDecl, outer, inUsing); } @@ -957,7 +972,7 @@ internal Binder VisitCompilationUnit(CompilationUnitSyntax compilationUnit, bool private static Binder AddInImportsBinders(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool inUsing) { - Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration)); + Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration); if (inUsing) { diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs index ef4a330d5a7ae..7c45b4cd1e836 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.cs @@ -173,10 +173,11 @@ internal Binder GetInNamespaceBinder(CSharpSyntaxNode unit) switch (unit.Kind()) { case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: { BinderFactoryVisitor visitor = _binderFactoryVisitorPool.Allocate(); visitor.Initialize(0, null, null); - Binder result = visitor.VisitNamespaceDeclaration((NamespaceDeclarationSyntax)unit, unit.SpanStart, inBody: true, inUsing: false); + Binder result = visitor.VisitNamespaceDeclaration((BaseNamespaceDeclarationSyntax)unit, unit.SpanStart, inBody: true, inUsing: false); _binderFactoryVisitorPool.Free(visitor); return result; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index d1586fcb68699..766776e69ef34 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -188,7 +188,7 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a } } - ImmutableArray boundConstructorArgumentNamesOpt = analyzedArguments.ConstructorArguments.GetNames(); + ImmutableArray boundConstructorArgumentNamesOpt = analyzedArguments.ConstructorArguments.GetNames(); ImmutableArray boundNamedArguments = analyzedArguments.NamedArguments?.ToImmutableAndFree() ?? ImmutableArray.Empty; Debug.Assert(boundNamedArguments.All(arg => !arg.Right.NeedsToBeConverted())); @@ -591,7 +591,7 @@ private ImmutableArray GetRewrittenAttributeConstructorArguments( out ImmutableArray constructorArgumentsSourceIndices, MethodSymbol attributeConstructor, ImmutableArray constructorArgsArray, - ImmutableArray constructorArgumentNamesOpt, + ImmutableArray constructorArgumentNamesOpt, AttributeSyntax syntax, BindingDiagnosticBag diagnostics, ref bool hasErrors) @@ -721,7 +721,7 @@ private static int[] CreateSourceIndicesArray(int paramIndex, int parameterCount private TypedConstant GetMatchingNamedOrOptionalConstructorArgument( out int matchingArgumentIndex, ImmutableArray constructorArgsArray, - ImmutableArray constructorArgumentNamesOpt, + ImmutableArray constructorArgumentNamesOpt, ParameterSymbol parameter, int startIndex, int argumentsCount, @@ -748,7 +748,7 @@ private TypedConstant GetMatchingNamedOrOptionalConstructorArgument( } } - private static int GetMatchingNamedConstructorArgumentIndex(string parameterName, ImmutableArray argumentNamesOpt, int startIndex, int argumentsCount) + private static int GetMatchingNamedConstructorArgumentIndex(string parameterName, ImmutableArray argumentNamesOpt, int startIndex, int argumentsCount) { RoslynDebug.Assert(parameterName != null); Debug.Assert(startIndex >= 0 && startIndex < argumentsCount); @@ -877,7 +877,7 @@ private TypedConstant GetDefaultValueArgument(ParameterSymbol parameter, Attribu } private static TypedConstant GetParamArrayArgument(ParameterSymbol parameter, ImmutableArray constructorArgsArray, - ImmutableArray constructorArgumentNamesOpt, int argumentsCount, int argsConsumedCount, Conversions conversions, out bool foundNamed) + ImmutableArray constructorArgumentNamesOpt, int argumentsCount, int argsConsumedCount, Conversions conversions, out bool foundNamed) { Debug.Assert(argsConsumedCount <= argumentsCount); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 19405f567c66c..a4555b2a11018 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -143,12 +143,27 @@ protected BoundExpression CreateConversion( var unconvertedSource = (BoundUnconvertedInterpolatedString)source; source = new BoundInterpolatedString( unconvertedSource.Syntax, - unconvertedSource.Parts, + interpolationData: null, + BindInterpolatedStringParts(unconvertedSource, diagnostics), unconvertedSource.ConstantValue, unconvertedSource.Type, unconvertedSource.HasErrors); } + if (conversion.Kind == ConversionKind.InterpolatedStringHandler) + { + var unconvertedSource = (BoundUnconvertedInterpolatedString)source; + return new BoundConversion( + syntax, + BindUnconvertedInterpolatedStringToHandlerType(unconvertedSource, (NamedTypeSymbol)destination, diagnostics, isHandlerConversion: true), + conversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: isCast && !wasCompilerGenerated, + conversionGroupOpt, + constantValueOpt: null, + destination); + } + if (source.Kind == BoundKind.UnconvertedSwitchExpression) { TypeSymbol? type = source.Type; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 24f36003c8f29..e3b9bf1aa121d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -167,13 +167,14 @@ private BoundBadExpression BadExpression(SyntaxNode syntax, LookupResultKind res /// Generates a new with no known type, given lookupResultKind and given symbols for GetSemanticInfo API, /// and the given bound children. /// - private BoundBadExpression BadExpression(SyntaxNode syntax, LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childNodes) + private BoundBadExpression BadExpression(SyntaxNode syntax, LookupResultKind resultKind, ImmutableArray symbols, ImmutableArray childNodes, bool wasCompilerGenerated = false) { return new BoundBadExpression(syntax, resultKind, symbols, childNodes.SelectAsArray((e, self) => self.BindToTypeForErrorRecovery(e), this), - CreateErrorType()); + CreateErrorType()) + { WasCompilerGenerated = wasCompilerGenerated }; } /// @@ -376,14 +377,7 @@ internal BoundExpression BindToNaturalType(BoundExpression expression, BindingDi break; case BoundUnconvertedInterpolatedString unconvertedInterpolatedString: { - // We determine the best method of emitting as a string (either via Concat, Format, or the builder pattern) - // during lowering, and it's not part of the publicly-visible API, unlike conversion to a builder type. - result = new BoundInterpolatedString( - unconvertedInterpolatedString.Syntax, - unconvertedInterpolatedString.Parts, - unconvertedInterpolatedString.ConstantValue, - unconvertedInterpolatedString.Type, - unconvertedInterpolatedString.HasErrors); + result = BindUnconvertedInterpolatedStringToString(unconvertedInterpolatedString, diagnostics); } break; default: @@ -2918,7 +2912,7 @@ private void BindArgumentAndName( } } - result.Names.Add(nameColonSyntax.Name); + result.AddName(nameColonSyntax.Name); } else if (hasNames) { @@ -2963,10 +2957,14 @@ private BoundExpression BindArgumentExpression(BindingDiagnosticBag diagnostics, return argument; } +#nullable enable private void CoerceArguments( MemberResolutionResult methodResult, ArrayBuilder arguments, - BindingDiagnosticBag diagnostics) + BindingDiagnosticBag diagnostics, + TypeSymbol? receiverType, + RefKind? receiverRefKind, + uint receiverEscapeScope) where TMember : Symbol { var result = methodResult.Result; @@ -2979,18 +2977,18 @@ private void CoerceArguments( var kind = result.ConversionForArg(arg); BoundExpression argument = arguments[arg]; + if (kind.IsInterpolatedStringHandler) + { + Debug.Assert(argument is BoundUnconvertedInterpolatedString); + TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); + reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations); + arguments[arg] = BindInterpolatedStringHandlerInMemberCall((BoundUnconvertedInterpolatedString)argument, arguments, parameters, ref result, arg, receiverType, receiverRefKind, receiverEscapeScope, diagnostics); + } // https://github.com/dotnet/roslyn/issues/37119 : should we create an (Identity) conversion when the kind is Identity but the types differ? - if (!kind.IsIdentity) + else if (!kind.IsIdentity) { TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); - - // NOTE: for some reason, dev10 doesn't report this for indexer accesses. - if (!methodResult.Member.IsIndexer() && !argument.HasAnyErrors && parameterTypeWithAnnotations.Type.IsUnsafe()) - { - // CONSIDER: dev10 uses the call syntax, but this seems clearer. - ReportUnsafeIfNotAllowed(argument.Syntax, diagnostics); - //CONSIDER: Return a bad expression so that HasErrors is true? - } + reportUnsafeIfNeeded(methodResult, diagnostics, argument, parameterTypeWithAnnotations); arguments[arg] = CreateConversion(argument.Syntax, argument, kind, isCast: false, conversionGroupOpt: null, parameterTypeWithAnnotations.Type, diagnostics); } @@ -3013,7 +3011,7 @@ private void CoerceArguments( else if (argument.NeedsToBeConverted()) { Debug.Assert(kind.IsIdentity); - if (argument is BoundTupleLiteral sourceTuple) + if (argument is BoundTupleLiteral) { TypeWithAnnotations parameterTypeWithAnnotations = GetCorrespondingParameterTypeWithAnnotations(ref result, parameters, arg); // CreateConversion reports tuple literal name mismatches, and constructs the expected pattern of bound nodes. @@ -3025,8 +3023,26 @@ private void CoerceArguments( } } } + + void reportUnsafeIfNeeded(MemberResolutionResult methodResult, BindingDiagnosticBag diagnostics, BoundExpression argument, TypeWithAnnotations parameterTypeWithAnnotations) + { + // NOTE: for some reason, dev10 doesn't report this for indexer accesses. + if (!methodResult.Member.IsIndexer() && !argument.HasAnyErrors && parameterTypeWithAnnotations.Type.IsUnsafe()) + { + // CONSIDER: dev10 uses the call syntax, but this seems clearer. + ReportUnsafeIfNotAllowed(argument.Syntax, diagnostics); + //CONSIDER: Return a bad expression so that HasErrors is true? + } + } } + private static ParameterSymbol GetCorrespondingParameter(ref MemberAnalysisResult result, ImmutableArray parameters, int arg) + { + int paramNum = result.ParameterFromArgument(arg); + return parameters[paramNum]; + } +#nullable disable + private static TypeWithAnnotations GetCorrespondingParameterTypeWithAnnotations(ref MemberAnalysisResult result, ImmutableArray parameters, int arg) { int paramNum = result.ParameterFromArgument(arg); @@ -4406,6 +4422,41 @@ private BoundExpression BindClassCreationExpression(ObjectCreationExpressionSynt } } +#nullable enable + /// + /// Helper method to create a synthesized constructor invocation. + /// + private BoundExpression MakeConstructorInvocation( + NamedTypeSymbol type, + ArrayBuilder arguments, + ArrayBuilder refKinds, + SyntaxNode node, + BindingDiagnosticBag diagnostics) + { + Debug.Assert(type.TypeKind is TypeKind.Class or TypeKind.Struct); + var analyzedArguments = AnalyzedArguments.GetInstance(); + + try + { + analyzedArguments.Arguments.AddRange(arguments); + analyzedArguments.RefKinds.AddRange(refKinds); + + if (type.IsStatic) + { + diagnostics.Add(ErrorCode.ERR_InstantiatingStaticClass, node.Location, type); + return MakeBadExpressionForObjectCreation(node, type, analyzedArguments, initializerOpt: null, typeSyntax: null, diagnostics, wasCompilerGenerated: true); + } + + var creation = BindClassCreationExpression(node, type.Name, node, type, analyzedArguments, diagnostics); + creation.WasCompilerGenerated = true; + return creation; + } + finally + { + analyzedArguments.Free(); + } + } + internal BoundExpression BindObjectCreationForErrorRecovery(BoundUnconvertedObjectCreationExpression node, BindingDiagnosticBag diagnostics) { var arguments = AnalyzedArguments.GetInstance(node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt); @@ -4414,17 +4465,19 @@ internal BoundExpression BindObjectCreationForErrorRecovery(BoundUnconvertedObje return result; } - private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpressionSyntax node, TypeSymbol type, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) + private BoundExpression MakeBadExpressionForObjectCreation(ObjectCreationExpressionSyntax node, TypeSymbol type, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics, bool wasCompilerGenerated = false) { - return MakeBadExpressionForObjectCreation(node, type, analyzedArguments, node.Initializer, node.Type, diagnostics); + return MakeBadExpressionForObjectCreation(node, type, analyzedArguments, node.Initializer, node.Type, diagnostics, wasCompilerGenerated); } - private BoundExpression MakeBadExpressionForObjectCreation(SyntaxNode node, TypeSymbol type, AnalyzedArguments analyzedArguments, InitializerExpressionSyntax initializerOpt, SyntaxNode typeSyntax, BindingDiagnosticBag diagnostics) + /// Shouldn't be null if is not null. + private BoundExpression MakeBadExpressionForObjectCreation(SyntaxNode node, TypeSymbol type, AnalyzedArguments analyzedArguments, InitializerExpressionSyntax? initializerOpt, SyntaxNode? typeSyntax, BindingDiagnosticBag diagnostics, bool wasCompilerGenerated = false) { var children = ArrayBuilder.GetInstance(); children.AddRange(BuildArgumentsForErrorRecovery(analyzedArguments)); if (initializerOpt != null) { + Debug.Assert(typeSyntax is not null); var boundInitializer = BindInitializerExpression(syntax: initializerOpt, type: type, typeSyntax: typeSyntax, @@ -4433,8 +4486,9 @@ private BoundExpression MakeBadExpressionForObjectCreation(SyntaxNode node, Type children.Add(boundInitializer); } - return new BoundBadExpression(node, LookupResultKind.NotCreatable, ImmutableArray.Create(type), children.ToImmutableAndFree(), type); + return new BoundBadExpression(node, LookupResultKind.NotCreatable, ImmutableArray.Create(type), children.ToImmutableAndFree(), type) { WasCompilerGenerated = wasCompilerGenerated }; } +#nullable disable private BoundObjectInitializerExpressionBase BindInitializerExpression( InitializerExpressionSyntax syntax, @@ -5242,19 +5296,17 @@ protected BoundExpression BindClassCreationExpression( } } - MemberResolutionResult memberResolutionResult; - ImmutableArray candidateConstructors; if (TryPerformConstructorOverloadResolution( - type, - analyzedArguments, - typeName, - typeNode.Location, - hasErrors, //don't cascade in these cases - diagnostics, - out memberResolutionResult, - out candidateConstructors, - allowProtectedConstructorsOfBaseType: false)) + type, + analyzedArguments, + typeName, + typeNode.Location, + hasErrors, //don't cascade in these cases + diagnostics, + out MemberResolutionResult memberResolutionResult, + out ImmutableArray candidateConstructors, + allowProtectedConstructorsOfBaseType: false)) { var method = memberResolutionResult.Member; @@ -5577,7 +5629,7 @@ private BoundExpression BindTypeParameterCreationExpression(SyntaxNode node, Typ /// of this method (i.e. not populating a pre-existing ). /// Presently, rationalizing this behavior is not worthwhile. /// - private bool TryPerformConstructorOverloadResolution( + internal bool TryPerformConstructorOverloadResolution( NamedTypeSymbol typeContainingConstructors, AnalyzedArguments analyzedArguments, string errorName, @@ -5638,7 +5690,7 @@ private bool TryPerformConstructorOverloadResolution( if (succeededIgnoringAccessibility) { - this.CoerceArguments(result.ValidResult, analyzedArguments.Arguments, diagnostics); + this.CoerceArguments(result.ValidResult, analyzedArguments.Arguments, diagnostics, receiverType: null, receiverRefKind: null, receiverEscapeScope: Binder.ExternalScope); } // Fill in the out parameter with the result, if there was one; it might be inaccessible. @@ -6328,7 +6380,8 @@ private BoundExpression BindInstanceMemberAccess( ImmutableArray typeArgumentsWithAnnotations, bool invoked, bool indexed, - BindingDiagnosticBag diagnostics) + BindingDiagnosticBag diagnostics, + bool searchExtensionMethodsIfNecessary = true) { Debug.Assert(rightArity == (typeArgumentsWithAnnotations.IsDefault ? 0 : typeArgumentsWithAnnotations.Length)); var leftType = boundLeft.Type; @@ -6359,7 +6412,7 @@ private BoundExpression BindInstanceMemberAccess( // SPEC: Otherwise, an attempt is made to process E.I as an extension method invocation. // SPEC: If this fails, E.I is an invalid member reference, and a binding-time error occurs. - var searchExtensionMethodsIfNecessary = !leftIsBaseReference; + searchExtensionMethodsIfNecessary = searchExtensionMethodsIfNecessary && !leftIsBaseReference; BoundMethodGroupFlags flags = 0; if (searchExtensionMethodsIfNecessary) @@ -7875,7 +7928,11 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( { MemberResolutionResult resolutionResult = overloadResolutionResult.ValidResult; PropertySymbol property = resolutionResult.Member; - this.CoerceArguments(resolutionResult, analyzedArguments.Arguments, diagnostics); + RefKind? receiverRefKind = receiverOpt?.GetRefKind(); + uint receiverEscapeScope = property.RequiresInstanceReceiver && receiverOpt != null + ? receiverRefKind?.IsWritableReference() == true ? GetRefEscape(receiverOpt, LocalScopeDepth) : GetValEscape(receiverOpt, LocalScopeDepth) + : Binder.ExternalScope; + this.CoerceArguments(resolutionResult, analyzedArguments.Arguments, diagnostics, receiverOpt?.Type, receiverRefKind, receiverEscapeScope); var isExpanded = resolutionResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; var argsToParams = resolutionResult.Result.ArgsToParamsOpt; @@ -8106,7 +8163,7 @@ method.OriginalDefinition is var original && argIsRange ? ErrorCode.ERR_ImplicitRangeIndexerWithName : ErrorCode.ERR_ImplicitIndexIndexerWithName, - arguments.Names[0].GetLocation()); + arguments.Names[0].GetValueOrDefault().Location); } return true; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs index 963438c5abdf7..14f0a90213d07 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.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. +using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -25,7 +28,6 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta } else { - var objectType = GetSpecialType(SpecialType.System_Object, diagnostics, node); var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, node); foreach (var content in node.Contents) { @@ -35,15 +37,6 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta { var interpolation = (InterpolationSyntax)content; var value = BindValue(interpolation.Expression, diagnostics, BindValueKind.RValue); - if (value.Type is null) - { - value = GenerateConversionForAssignment(objectType, value, diagnostics); - } - else - { - value = BindToNaturalType(value, diagnostics); - _ = GenerateConversionForAssignment(objectType, value, diagnostics); - } // We need to ensure the argument is not a lambda, method group, etc. It isn't nice to wait until lowering, // when we perform overload resolution, to report a problem. So we do that check by calling @@ -93,7 +86,7 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta format = new BoundLiteral(interpolation.FormatClause, ConstantValue.Create(text), stringType, hasErrors); } - builder.Add(new BoundStringInsert(interpolation, value, alignment, format, null)); + builder.Add(new BoundStringInsert(interpolation, value, alignment, format, isInterpolatedStringHandlerAppendCall: false)); if (!isResultConstant || value.ConstantValue == null || !(interpolation is { FormatClause: null, AlignmentClause: null }) || @@ -134,5 +127,637 @@ private BoundExpression BindInterpolatedString(InterpolatedStringExpressionSynta Debug.Assert(isResultConstant == (resultConstant != null)); return new BoundUnconvertedInterpolatedString(node, builder.ToImmutableAndFree(), resultConstant, stringType); } + + private BoundInterpolatedString BindUnconvertedInterpolatedStringToString(BoundUnconvertedInterpolatedString unconvertedInterpolatedString, BindingDiagnosticBag diagnostics) + { + // We have 4 possible lowering strategies, dependent on the contents of the string, in this order: + // 1. The string is a constant value. We can just use the final value. + // 2. The string is composed of 4 or fewer components that are all strings, we can lower to a call to string.Concat without a + // params array. This is very efficient as the runtime can allocate a buffer for the string with exactly the correct length and + // make no intermediate allocations. + // 3. The WellKnownType DefaultInterpolatedStringHandler is available, and none of the interpolation holes contain an await expression. + // The builder is a ref struct, and we can guarantee the lifetime won't outlive the stack if the string doesn't contain any + // awaits, but if it does we cannot use it. This builder is the only way that ref structs can be directly used as interpolation + // hole components, which means that ref structs components and await expressions cannot be combined. It is already illegal for + // the user to use ref structs in an async method today, but if that were to ever change, this would still need to be respected. + // We also cannot use this method if the interpolated string appears within a catch filter, as the builder is disposable and we + // cannot put a try/finally inside a filter block. + // 4. The string is composed of more than 4 components that are all strings themselves. We can turn this into a single + // call to string.Concat. We prefer the builder over this because the builder can use pooling to avoid new allocations, while this + // call will need to allocate a param array. + // 5. The string has heterogeneous data and either InterpolatedStringHandler is unavailable, or one of the holes contains an await + // expression. This is turned into a call to string.Format. + // + // We need to do the determination of 1, 2, 3, or 4/5 up front, rather than in lowering, as it affects diagnostics (ref structs not being + // able to be used, for example). However, between 4 and 5, we don't need to know at this point, so that logic is deferred for lowering. + + if (unconvertedInterpolatedString.ConstantValue is not null) + { + // Case 1 + Debug.Assert(unconvertedInterpolatedString.Parts.All(static part => part.Type is null or { SpecialType: SpecialType.System_String })); + return constructWithData(BindInterpolatedStringParts(unconvertedInterpolatedString, diagnostics), data: null); + } + + // Case 2. Attempt to see if all parts are strings. + if (unconvertedInterpolatedString.Parts.Length <= 4 && + unconvertedInterpolatedString.Parts.All(p => p is BoundLiteral + or BoundStringInsert { Value: { Type: { SpecialType: SpecialType.System_String } }, Alignment: null, Format: null })) + { + return constructWithData(BindInterpolatedStringParts(unconvertedInterpolatedString, diagnostics), data: null); + } + + if (tryBindAsHandlerType(out var result)) + { + // Case 3 + return result; + } + + // The specifics of 4 vs 5 aren't necessary for this stage of binding. The only thing that matters is that every part needs to be convertible + // object. + return constructWithData(BindInterpolatedStringParts(unconvertedInterpolatedString, diagnostics), data: null); + + BoundInterpolatedString constructWithData(ImmutableArray parts, InterpolatedStringHandlerData? data) + => new BoundInterpolatedString( + unconvertedInterpolatedString.Syntax, + data, + parts, + unconvertedInterpolatedString.ConstantValue, + unconvertedInterpolatedString.Type, + unconvertedInterpolatedString.HasErrors); + + bool tryBindAsHandlerType([NotNullWhen(true)] out BoundInterpolatedString? result) + { + result = null; + if (unconvertedInterpolatedString.Parts.ContainsAwaitExpression()) + { + return false; + } + + var interpolatedStringHandlerType = Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler); + if (interpolatedStringHandlerType is MissingMetadataTypeSymbol) + { + return false; + } + + result = BindUnconvertedInterpolatedStringToHandlerType(unconvertedInterpolatedString, interpolatedStringHandlerType, diagnostics, isHandlerConversion: false); + + return true; + } + } + + private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( + BoundUnconvertedInterpolatedString unconvertedInterpolatedString, + NamedTypeSymbol interpolatedStringHandlerType, + BindingDiagnosticBag diagnostics, + bool isHandlerConversion, + ImmutableArray additionalConstructorArguments = default, + ImmutableArray additionalConstructorRefKinds = default) + { + Debug.Assert(additionalConstructorArguments.IsDefault + ? additionalConstructorRefKinds.IsDefault + : additionalConstructorArguments.Length == additionalConstructorRefKinds.Length); + additionalConstructorArguments = additionalConstructorArguments.NullToEmpty(); + additionalConstructorRefKinds = additionalConstructorRefKinds.NullToEmpty(); + + ReportUseSite(interpolatedStringHandlerType, diagnostics, unconvertedInterpolatedString.Syntax); + + // We satisfy the conditions for using an interpolated string builder. Bind all the builder calls unconditionally, so that if + // there are errors we get better diagnostics than "could not convert to object." + var implicitBuilderReceiver = new BoundInterpolatedStringHandlerPlaceholder(unconvertedInterpolatedString.Syntax, interpolatedStringHandlerType) { WasCompilerGenerated = true }; + var (appendCalls, usesBoolReturn, positionInfo, baseStringLength, numFormatHoles) = BindInterpolatedStringAppendCalls(unconvertedInterpolatedString, implicitBuilderReceiver, diagnostics); + + // Prior to C# 10, all types in an interpolated string expression needed to be convertible to `object`. After 10, some types + // (such as Span) that are not convertible to `object` are permissible as interpolated string components, provided there + // is an applicable AppendFormatted method that accepts them. To preserve langversion, we therefore make sure all components + // are convertible to object if the current langversion is lower than the interpolation feature and we're converting this + // interpolation into an actual string. + bool needToCheckConversionToObject = false; + if (isHandlerConversion) + { + CheckFeatureAvailability(unconvertedInterpolatedString.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); + } + else if (!Compilation.IsFeatureEnabled(MessageID.IDS_FeatureImprovedInterpolatedStrings) && diagnostics.AccumulatesDiagnostics) + { + needToCheckConversionToObject = true; + } + + Debug.Assert(appendCalls.Length == unconvertedInterpolatedString.Parts.Length); + Debug.Assert(appendCalls.All(a => a is { HasErrors: true } or BoundCall { Arguments: { Length: > 0 } } or BoundDynamicInvocation)); + + if (needToCheckConversionToObject) + { + TypeSymbol objectType = GetSpecialType(SpecialType.System_Object, diagnostics, unconvertedInterpolatedString.Syntax); + BindingDiagnosticBag conversionDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); + foreach (var currentPart in unconvertedInterpolatedString.Parts) + { + if (currentPart is BoundStringInsert insert) + { + var value = insert.Value; + bool reported = false; + if (value.Type is not null) + { + value = BindToNaturalType(value, conversionDiagnostics); + if (conversionDiagnostics.HasAnyErrors()) + { + CheckFeatureAvailability(value.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); + reported = true; + } + } + + if (!reported) + { + _ = GenerateConversionForAssignment(objectType, value, conversionDiagnostics); + if (conversionDiagnostics.HasAnyErrors()) + { + CheckFeatureAvailability(value.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); + } + } + + conversionDiagnostics.Clear(); + } + } + + conversionDiagnostics.Free(); + } + + + var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, unconvertedInterpolatedString.Syntax); + int constructorArgumentLength = 3 + additionalConstructorArguments.Length; + var argumentsBuilder = ArrayBuilder.GetInstance(constructorArgumentLength); + + var refKindsBuilder = ArrayBuilder.GetInstance(constructorArgumentLength); + refKindsBuilder.Add(RefKind.None); + refKindsBuilder.Add(RefKind.None); + refKindsBuilder.AddRange(additionalConstructorRefKinds); + + // Add the trailing out validity parameter for the first attempt.Note that we intentionally use `diagnostics` for resolving System.Boolean, + // because we want to track that we're using the type no matter what. + var boolType = GetSpecialType(SpecialType.System_Boolean, diagnostics, unconvertedInterpolatedString.Syntax); + var trailingConstructorValidityPlaceholder = new BoundInterpolatedStringArgumentPlaceholder(unconvertedInterpolatedString.Syntax, BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter, valSafeToEscape: LocalScopeDepth, boolType); + var outConstructorAdditionalArguments = additionalConstructorArguments.Add(trailingConstructorValidityPlaceholder); + refKindsBuilder.Add(RefKind.Out); + populateArguments(unconvertedInterpolatedString.Syntax, outConstructorAdditionalArguments, baseStringLength, numFormatHoles, intType, argumentsBuilder); + + BoundExpression constructorCall; + var outConstructorDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: diagnostics.AccumulatesDependencies); + var outConstructorCall = MakeConstructorInvocation(interpolatedStringHandlerType, argumentsBuilder, refKindsBuilder, unconvertedInterpolatedString.Syntax, outConstructorDiagnostics); + if (outConstructorCall is not BoundObjectCreationExpression { ResultKind: LookupResultKind.Viable }) + { + // MakeConstructorInvocation can call CoerceArguments on the builder if overload resolution succeeded ignoring accessibility, which + // could still end up not succeeding, and that would end up changing the arguments. So we want to clear and repopulate. + argumentsBuilder.Clear(); + + // Try again without an out parameter. + populateArguments(unconvertedInterpolatedString.Syntax, additionalConstructorArguments, baseStringLength, numFormatHoles, intType, argumentsBuilder); + refKindsBuilder.RemoveLast(); + + var nonOutConstructorDiagnostics = BindingDiagnosticBag.GetInstance(template: outConstructorDiagnostics); + BoundExpression nonOutConstructorCall = MakeConstructorInvocation(interpolatedStringHandlerType, argumentsBuilder, refKindsBuilder, unconvertedInterpolatedString.Syntax, nonOutConstructorDiagnostics); + + if (nonOutConstructorCall is BoundObjectCreationExpression { ResultKind: LookupResultKind.Viable }) + { + // We successfully bound the out version, so set all the final data based on that binding + constructorCall = nonOutConstructorCall; + diagnostics.AddRangeAndFree(nonOutConstructorDiagnostics); + outConstructorDiagnostics.Free(); + } + else + { + // We'll attempt to figure out which failure was "best" by looking to see if one failed to bind because it couldn't find + // a constructor with the correct number of arguments. We presume that, if one failed for this reason and the other failed + // for a different reason, that different reason is the one the user will want to know about. If both or neither failed + // because of this error, we'll report everything. + + // https://github.com/dotnet/roslyn/issues/54396 Instead of inspecting errors, we should be capturing the results of overload + // resolution and attempting to determine which method considered was the best to report errors for. + + var nonOutConstructorHasArityError = nonOutConstructorDiagnostics.DiagnosticBag?.AsEnumerableWithoutResolution().Any(d => (ErrorCode)d.Code == ErrorCode.ERR_BadCtorArgCount) ?? false; + var outConstructorHasArityError = outConstructorDiagnostics.DiagnosticBag?.AsEnumerableWithoutResolution().Any(d => (ErrorCode)d.Code == ErrorCode.ERR_BadCtorArgCount) ?? false; + + switch ((nonOutConstructorHasArityError, outConstructorHasArityError)) + { + case (true, false): + constructorCall = outConstructorCall; + additionalConstructorArguments = outConstructorAdditionalArguments; + diagnostics.AddRangeAndFree(outConstructorDiagnostics); + nonOutConstructorDiagnostics.Free(); + break; + case (false, true): + constructorCall = nonOutConstructorCall; + diagnostics.AddRangeAndFree(nonOutConstructorDiagnostics); + outConstructorDiagnostics.Free(); + break; + default: + // For the final output binding info, we'll go with the shorter constructor in the absence of any tiebreaker, + // but we'll report all diagnostics + constructorCall = nonOutConstructorCall; + diagnostics.AddRangeAndFree(nonOutConstructorDiagnostics); + diagnostics.AddRangeAndFree(outConstructorDiagnostics); + break; + } + } + } + else + { + diagnostics.AddRangeAndFree(outConstructorDiagnostics); + constructorCall = outConstructorCall; + additionalConstructorArguments = outConstructorAdditionalArguments; + } + + argumentsBuilder.Free(); + refKindsBuilder.Free(); + + Debug.Assert(constructorCall.HasErrors || constructorCall is BoundObjectCreationExpression or BoundDynamicObjectCreationExpression); + + if (constructorCall is BoundDynamicObjectCreationExpression) + { + // An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, unconvertedInterpolatedString.Syntax.Location, interpolatedStringHandlerType.Name); + } + + return new BoundInterpolatedString( + unconvertedInterpolatedString.Syntax, + new InterpolatedStringHandlerData( + interpolatedStringHandlerType, + constructorCall, + usesBoolReturn, + LocalScopeDepth, + additionalConstructorArguments.NullToEmpty(), + positionInfo, + implicitBuilderReceiver), + appendCalls, + unconvertedInterpolatedString.ConstantValue, + unconvertedInterpolatedString.Type, + unconvertedInterpolatedString.HasErrors); + + static void populateArguments(SyntaxNode syntax, ImmutableArray additionalConstructorArguments, int baseStringLength, int numFormatHoles, NamedTypeSymbol intType, ArrayBuilder argumentsBuilder) + { + // literalLength + argumentsBuilder.Add(new BoundLiteral(syntax, ConstantValue.Create(baseStringLength), intType) { WasCompilerGenerated = true }); + // formattedCount + argumentsBuilder.Add(new BoundLiteral(syntax, ConstantValue.Create(numFormatHoles), intType) { WasCompilerGenerated = true }); + // Any other arguments from the call site + argumentsBuilder.AddRange(additionalConstructorArguments); + } + } + + private ImmutableArray BindInterpolatedStringParts(BoundUnconvertedInterpolatedString unconvertedInterpolatedString, BindingDiagnosticBag diagnostics) + { + ArrayBuilder? partsBuilder = null; + var objectType = GetSpecialType(SpecialType.System_Object, diagnostics, unconvertedInterpolatedString.Syntax); + for (int i = 0; i < unconvertedInterpolatedString.Parts.Length; i++) + { + var part = unconvertedInterpolatedString.Parts[i]; + if (part is BoundStringInsert insert) + { + BoundExpression newValue; + if (insert.Value.Type is null) + { + newValue = GenerateConversionForAssignment(objectType, insert.Value, diagnostics); + } + else + { + newValue = BindToNaturalType(insert.Value, diagnostics); + _ = GenerateConversionForAssignment(objectType, insert.Value, diagnostics); + } + + if (insert.Value != newValue) + { + if (partsBuilder is null) + { + partsBuilder = ArrayBuilder.GetInstance(unconvertedInterpolatedString.Parts.Length); + partsBuilder.AddRange(unconvertedInterpolatedString.Parts, i); + } + + partsBuilder.Add(insert.Update(newValue, insert.Alignment, insert.Format, isInterpolatedStringHandlerAppendCall: false)); + } + else + { + partsBuilder?.Add(part); + } + } + else + { + Debug.Assert(part is BoundLiteral { Type: { SpecialType: SpecialType.System_String } }); + partsBuilder?.Add(part); + } + } + + return partsBuilder?.ToImmutableAndFree() ?? unconvertedInterpolatedString.Parts; + } + + private (ImmutableArray AppendFormatCalls, bool UsesBoolReturn, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>, int BaseStringLength, int NumFormatHoles) BindInterpolatedStringAppendCalls( + BoundUnconvertedInterpolatedString source, + BoundInterpolatedStringHandlerPlaceholder implicitBuilderReceiver, + BindingDiagnosticBag diagnostics) + { + if (source.Parts.IsEmpty) + { + return (ImmutableArray.Empty, false, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>.Empty, 0, 0); + } + + bool? builderPatternExpectsBool = null; + var builderAppendCalls = ArrayBuilder.GetInstance(source.Parts.Length); + var positionInfo = ArrayBuilder<(bool IsLiteral, bool HasAlignment, bool HasFormat)>.GetInstance(source.Parts.Length); + var argumentsBuilder = ArrayBuilder.GetInstance(3); + var parameterNamesAndLocationsBuilder = ArrayBuilder<(string, Location)?>.GetInstance(3); + int baseStringLength = 0; + int numFormatHoles = 0; + + foreach (var part in source.Parts) + { + Debug.Assert(part is BoundLiteral or BoundStringInsert); + string methodName; + bool isLiteral; + bool hasAlignment; + bool hasFormat; + + if (part is BoundStringInsert insert) + { + methodName = "AppendFormatted"; + argumentsBuilder.Add(insert.Value); + parameterNamesAndLocationsBuilder.Add(null); + isLiteral = false; + hasAlignment = false; + hasFormat = false; + + if (insert.Alignment is not null) + { + hasAlignment = true; + argumentsBuilder.Add(insert.Alignment); + parameterNamesAndLocationsBuilder.Add(("alignment", insert.Alignment.Syntax.Location)); + } + if (insert.Format is not null) + { + hasFormat = true; + argumentsBuilder.Add(insert.Format); + parameterNamesAndLocationsBuilder.Add(("format", insert.Format.Syntax.Location)); + } + numFormatHoles++; + } + else + { + var boundLiteral = (BoundLiteral)part; + Debug.Assert(boundLiteral.ConstantValue != null && boundLiteral.ConstantValue.IsString); + var literalText = ConstantValueUtils.UnescapeInterpolatedStringLiteral(boundLiteral.ConstantValue.StringValue); + methodName = "AppendLiteral"; + argumentsBuilder.Add(boundLiteral.Update(ConstantValue.Create(literalText), boundLiteral.Type)); + isLiteral = true; + hasAlignment = false; + hasFormat = false; + baseStringLength += literalText.Length; + } + + var arguments = argumentsBuilder.ToImmutableAndClear(); + ImmutableArray<(string, Location)?> parameterNamesAndLocations; + if (parameterNamesAndLocationsBuilder.Count > 1) + { + parameterNamesAndLocations = parameterNamesAndLocationsBuilder.ToImmutableAndClear(); + } + else + { + Debug.Assert(parameterNamesAndLocationsBuilder.Count == 0 || parameterNamesAndLocationsBuilder[0] == null); + parameterNamesAndLocations = default; + parameterNamesAndLocationsBuilder.Clear(); + } + + var call = MakeInvocationExpression(part.Syntax, implicitBuilderReceiver, methodName, arguments, diagnostics, names: parameterNamesAndLocations, searchExtensionMethodsIfNecessary: false); + builderAppendCalls.Add(call); + positionInfo.Add((isLiteral, hasAlignment, hasFormat)); + + Debug.Assert(call is BoundCall or BoundDynamicInvocation or { HasErrors: true }); + + // We just assume that dynamic is going to do the right thing, and runtime will fail if it does not. If there are only dynamic calls, we assume that + // void is returned. + if (call is BoundCall { Method: { ReturnType: var returnType } method }) + { + bool methodReturnsBool = returnType.SpecialType == SpecialType.System_Boolean; + if (!methodReturnsBool && returnType.SpecialType != SpecialType.System_Void) + { + // Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, part.Syntax.Location, method); + } + else if (builderPatternExpectsBool == null) + { + builderPatternExpectsBool = methodReturnsBool; + } + else if (builderPatternExpectsBool != methodReturnsBool) + { + // Interpolated string handler method '{0}' has inconsistent return types. Expected to return '{1}'. + var expected = builderPatternExpectsBool == true ? Compilation.GetSpecialType(SpecialType.System_Boolean) : Compilation.GetSpecialType(SpecialType.System_Void); + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, part.Syntax.Location, method, expected); + } + } + } + + argumentsBuilder.Free(); + parameterNamesAndLocationsBuilder.Free(); + return (builderAppendCalls.ToImmutableAndFree(), builderPatternExpectsBool ?? false, positionInfo.ToImmutableAndFree(), baseStringLength, numFormatHoles); + } + + private BoundExpression BindInterpolatedStringHandlerInMemberCall( + BoundUnconvertedInterpolatedString unconvertedString, + ArrayBuilder arguments, + ImmutableArray parameters, + ref MemberAnalysisResult memberAnalysisResult, + int interpolatedStringArgNum, + TypeSymbol? receiverType, + RefKind? receiverRefKind, + uint receiverEscapeScope, + BindingDiagnosticBag diagnostics) + { + var interpolatedStringConversion = memberAnalysisResult.ConversionForArg(interpolatedStringArgNum); + Debug.Assert(interpolatedStringConversion.IsInterpolatedStringHandler); + var interpolatedStringParameter = GetCorrespondingParameter(ref memberAnalysisResult, parameters, interpolatedStringArgNum); + Debug.Assert(interpolatedStringParameter.Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); + + if (interpolatedStringParameter.HasInterpolatedStringHandlerArgumentError) + { + // The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, unconvertedString.Syntax.Location, interpolatedStringParameter, interpolatedStringParameter.Type); + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + wasCompilerGenerated: false, + interpolatedStringParameter.Type, + diagnostics, + hasErrors: true); + } + + var handlerParameterIndexes = interpolatedStringParameter.InterpolatedStringHandlerArgumentIndexes; + if (handlerParameterIndexes.IsEmpty) + { + // No arguments, fall back to the standard conversion steps. + return CreateConversion( + unconvertedString.Syntax, + unconvertedString, + interpolatedStringConversion, + isCast: false, + conversionGroupOpt: null, + interpolatedStringParameter.Type, + diagnostics); + } + + Debug.Assert(handlerParameterIndexes.All((index, paramLength) => index >= BoundInterpolatedStringArgumentPlaceholder.InstanceParameter && index < paramLength, + parameters.Length)); + + // We need to find the appropriate argument expression for every expected parameter, and error on any that occur after the current parameter + + ImmutableArray handlerArgumentIndexes; + + if (memberAnalysisResult.ArgsToParamsOpt.IsDefault && arguments.Count == parameters.Length) + { + // No parameters are missing and no remapped indexes, we can just use the original indexes + handlerArgumentIndexes = handlerParameterIndexes; + } + else + { + // Args and parameters were reordered via named parameters, or parameters are missing. Find the correct argument index for each parameter. + var handlerArgumentIndexesBuilder = ArrayBuilder.GetInstance(handlerParameterIndexes.Length, fillWithValue: BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); + for (int handlerParameterIndex = 0; handlerParameterIndex < handlerParameterIndexes.Length; handlerParameterIndex++) + { + int handlerParameter = handlerParameterIndexes[handlerParameterIndex]; + Debug.Assert(handlerArgumentIndexesBuilder[handlerParameterIndex] is BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter); + + if (handlerParameter == BoundInterpolatedStringArgumentPlaceholder.InstanceParameter) + { + handlerArgumentIndexesBuilder[handlerParameterIndex] = handlerParameter; + continue; + } + + for (int argumentIndex = 0; argumentIndex < arguments.Count; argumentIndex++) + { + // The index in the original parameter list we're looking to match up. + int argumentParameterIndex = memberAnalysisResult.ParameterFromArgument(argumentIndex); + // Is the original parameter index of the current argument the parameter index that was specified in the attribute? + if (argumentParameterIndex == handlerParameter) + { + // We can't just bail out on the first match: users can duplicate parameters in attributes, causing the same value to be passed twice. + handlerArgumentIndexesBuilder[handlerParameterIndex] = argumentIndex; + } + } + } + + handlerArgumentIndexes = handlerArgumentIndexesBuilder.ToImmutableAndFree(); + } + + var argumentPlaceholdersBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); + var argumentRefKindsBuilder = ArrayBuilder.GetInstance(handlerArgumentIndexes.Length); + bool hasErrors = false; + + // Now, go through all the specified arguments and see if any were specified _after_ the interpolated string, and construct + // a set of placeholders for overload resolution. + for (int i = 0; i < handlerArgumentIndexes.Length; i++) + { + int argumentIndex = handlerArgumentIndexes[i]; + Debug.Assert(argumentIndex != interpolatedStringArgNum); + + RefKind refKind; + TypeSymbol placeholderType; + switch (argumentIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + Debug.Assert(receiverRefKind != null && receiverType is not null); + refKind = receiverRefKind.GetValueOrDefault(); + placeholderType = receiverType; + break; + case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: + { + // Don't error if the parameter isn't optional or params: the user will already have an error for missing an optional parameter or overload resolution failed. + // If it is optional, then they could otherwise not specify the parameter and that's an error + var originalParameterIndex = handlerParameterIndexes[i]; + var parameter = parameters[originalParameterIndex]; + if (parameter.IsOptional || (originalParameterIndex + 1 == parameters.Length && OverloadResolution.IsValidParamsParameter(parameter))) + { + // Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + diagnostics.Add( + ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, + unconvertedString.Syntax.Location, + parameter.Name, + interpolatedStringParameter.Name); + hasErrors = true; + } + + refKind = parameter.RefKind; + placeholderType = parameter.Type; + } + break; + default: + { + var originalParameterIndex = handlerParameterIndexes[i]; + var parameter = parameters[originalParameterIndex]; + if (argumentIndex > interpolatedStringArgNum) + { + // Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + diagnostics.Add( + ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, + arguments[argumentIndex].Syntax.Location, + parameter.Name, + interpolatedStringParameter.Name); + hasErrors = true; + } + + refKind = parameter.RefKind; + placeholderType = parameter.Type; + } + break; + } + + SyntaxNode placeholderSyntax; + uint valSafeToEscapeScope; + + switch (argumentIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + placeholderSyntax = unconvertedString.Syntax; + valSafeToEscapeScope = receiverEscapeScope; + break; + case BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter: + placeholderSyntax = unconvertedString.Syntax; + valSafeToEscapeScope = Binder.ExternalScope; + break; + case >= 0: + placeholderSyntax = arguments[argumentIndex].Syntax; + valSafeToEscapeScope = GetValEscape(arguments[argumentIndex], LocalScopeDepth); + break; + default: + throw ExceptionUtilities.UnexpectedValue(argumentIndex); + } + + argumentPlaceholdersBuilder.Add( + new BoundInterpolatedStringArgumentPlaceholder( + placeholderSyntax, + argumentIndex, + valSafeToEscapeScope, + placeholderType, + hasErrors: argumentIndex == BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter)); + // We use the parameter refkind, rather than what the argument was actually passed with, because that will suppress duplicated errors + // about arguments being passed with the wrong RefKind. The user will have already gotten an error about mismatched RefKinds or it will + // be a place where refkinds are allowed to differ + argumentRefKindsBuilder.Add(refKind); + } + + var interpolatedString = BindUnconvertedInterpolatedStringToHandlerType( + unconvertedString, + (NamedTypeSymbol)interpolatedStringParameter.Type, + diagnostics, + isHandlerConversion: true, + additionalConstructorArguments: argumentPlaceholdersBuilder.ToImmutableAndFree(), + additionalConstructorRefKinds: argumentRefKindsBuilder.ToImmutableAndFree()); + + return new BoundConversion( + interpolatedString.Syntax, + interpolatedString, + interpolatedStringConversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: false, + conversionGroupOpt: null, + constantValueOpt: null, + interpolatedStringParameter.Type, + hasErrors || interpolatedString.HasErrors); + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index b20ae3248d918..3246c6af5a463 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -82,14 +82,17 @@ internal BoundExpression MakeInvocationExpression( BindingDiagnosticBag diagnostics, SeparatedSyntaxList typeArgsSyntax = default(SeparatedSyntaxList), ImmutableArray typeArgs = default(ImmutableArray), + ImmutableArray<(string Name, Location Location)?> names = default, CSharpSyntaxNode? queryClause = null, bool allowFieldsAndProperties = false, - bool allowUnexpandedForm = true) + bool allowUnexpandedForm = true, + bool searchExtensionMethodsIfNecessary = true) { Debug.Assert(receiver != null); + Debug.Assert(names.IsDefault || names.Length == args.Length); receiver = BindToNaturalType(receiver, diagnostics); - var boundExpression = BindInstanceMemberAccess(node, node, receiver, methodName, typeArgs.NullToEmpty().Length, typeArgsSyntax, typeArgs, invoked: true, indexed: false, diagnostics); + var boundExpression = BindInstanceMemberAccess(node, node, receiver, methodName, typeArgs.NullToEmpty().Length, typeArgsSyntax, typeArgs, invoked: true, indexed: false, diagnostics, searchExtensionMethodsIfNecessary); // The other consumers of this helper (await and collection initializers) require the target member to be a method. if (!allowFieldsAndProperties && (boundExpression.Kind == BoundKind.FieldAccess || boundExpression.Kind == BoundKind.PropertyAccess)) @@ -114,7 +117,7 @@ internal BoundExpression MakeInvocationExpression( msgId.Localize(), MessageID.IDS_SK_METHOD.Localize()); - return BadExpression(node, LookupResultKind.Empty, ImmutableArray.Create(symbol), args.Add(receiver)); + return BadExpression(node, LookupResultKind.Empty, ImmutableArray.Create(symbol), args.Add(receiver), wasCompilerGenerated: true); } boundExpression = CheckValue(boundExpression, BindValueKind.RValueOrMethodGroup, diagnostics); @@ -125,6 +128,12 @@ internal BoundExpression MakeInvocationExpression( e.Kind == BoundKind.OutDeconstructVarPendingInference || e.Kind == BoundKind.DiscardExpression && !e.HasExpressionType())); analyzedArguments.Arguments.AddRange(args); + + if (!names.IsDefault) + { + analyzedArguments.Names.AddRange(names); + } + BoundExpression result = BindInvocationExpression( node, node, methodName, boundExpression, analyzedArguments, diagnostics, queryClause, allowUnexpandedForm: allowUnexpandedForm); @@ -1000,13 +1009,7 @@ private BoundCall BindInvocationExpressionContinued( var methodResult = result.ValidResult; var returnType = methodResult.Member.ReturnType; - this.CoerceArguments(methodResult, analyzedArguments.Arguments, diagnostics); - var method = methodResult.Member; - var expanded = methodResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - var argsToParams = methodResult.Result.ArgsToParamsOpt; - - BindDefaultArguments(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, ref argsToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); // It is possible that overload resolution succeeded, but we have chosen an // instance method and we're in a static method. A careful reading of the @@ -1017,6 +1020,17 @@ private BoundCall BindInvocationExpressionContinued( var receiver = ReplaceTypeOrValueReceiver(methodGroup.Receiver, !method.RequiresInstanceReceiver && !invokedAsExtensionMethod, diagnostics); + var receiverRefKind = receiver?.GetRefKind(); + uint receiverValEscapeScope = method.RequiresInstanceReceiver && receiver != null + ? receiverRefKind?.IsWritableReference() == true ? GetRefEscape(receiver, LocalScopeDepth) : GetValEscape(receiver, LocalScopeDepth) + : Binder.ExternalScope; + this.CoerceArguments(methodResult, analyzedArguments.Arguments, diagnostics, receiver?.Type, receiverRefKind, receiverValEscapeScope); + + var expanded = methodResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; + var argsToParams = methodResult.Result.ArgsToParamsOpt; + + BindDefaultArguments(node, method.Parameters, analyzedArguments.Arguments, analyzedArguments.RefKinds, ref argsToParams, out var defaultArguments, expanded, enableCallerInfo: true, diagnostics); + // Note: we specifically want to do final validation (7.6.5.1) without checking delegate compatibility (15.2), // so we're calling MethodGroupFinalValidation directly, rather than via MethodGroupConversionHasErrors. // Note: final validation wants the receiver that corresponds to the source representation @@ -2002,7 +2016,10 @@ private BoundFunctionPointerInvocation BindFunctionPointerInvocation(SyntaxNode CoerceArguments( methodResult, analyzedArguments.Arguments, - diagnostics); + diagnostics, + receiverType: null, + receiverRefKind: null, + receiverEscapeScope: Binder.ExternalScope); var args = analyzedArguments.Arguments.ToImmutable(); var refKinds = analyzedArguments.RefKinds.ToImmutableOrNull(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index afcffedc0426b..082400b2b10c1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -199,6 +199,7 @@ private static void AssertTrivialConversion(ConversionKind kind) case ConversionKind.ImplicitDynamic: case ConversionKind.ExplicitDynamic: case ConversionKind.InterpolatedString: + case ConversionKind.InterpolatedStringHandler: isTrivial = true; break; @@ -242,6 +243,7 @@ internal static Conversion GetTrivialConversion(ConversionKind kind) internal static Conversion ImplicitDynamic => new Conversion(ConversionKind.ImplicitDynamic); internal static Conversion ExplicitDynamic => new Conversion(ConversionKind.ExplicitDynamic); internal static Conversion InterpolatedString => new Conversion(ConversionKind.InterpolatedString); + internal static Conversion InterpolatedStringHandler => new Conversion(ConversionKind.InterpolatedStringHandler); internal static Conversion Deconstruction => new Conversion(ConversionKind.Deconstruction); internal static Conversion PinnedObjectToPointer => new Conversion(ConversionKind.PinnedObjectToPointer); internal static Conversion ImplicitPointer => new Conversion(ConversionKind.ImplicitPointer); @@ -598,6 +600,17 @@ public bool IsInterpolatedString } } + /// + /// Returns true if the conversion is an interpolated string builder conversion. + /// + public bool IsInterpolatedStringHandler + { + get + { + return Kind == ConversionKind.InterpolatedStringHandler; + } + } + /// /// Returns true if the conversion is an implicit nullable conversion or explicit nullable conversion. /// diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs index 4985822d4e668..8d82824d3d9a5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs @@ -2,12 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - namespace Microsoft.CodeAnalysis.CSharp { internal enum ConversionKind : byte @@ -63,5 +57,7 @@ internal enum ConversionKind : byte DefaultLiteral, // a conversion from a `default` literal to any type ObjectCreation, // a conversion from a `new()` expression to any type + + InterpolatedStringHandler, // A conversion from an interpolated string literal to a type attributed with InterpolatedStringBuilderAttribute } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs index 63cb1e27253ce..09555f2ce90c0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs @@ -48,6 +48,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind) case ImplicitPointerToVoid: case ImplicitNullToPointer: case InterpolatedString: + case InterpolatedStringHandler: case SwitchExpression: case ConditionalExpression: case Deconstruction: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 74963ad00b10a..4d3ae007d3af5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -7,7 +7,10 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -81,10 +84,15 @@ public override Conversion GetMethodGroupFunctionPointerConversion(BoundMethodGr protected override Conversion GetInterpolatedStringConversion(BoundUnconvertedInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { + if (destination is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }) + { + return Conversion.InterpolatedStringHandler; + } + // An interpolated string expression may be converted to the types // System.IFormattable and System.FormattableString - return (TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_IFormattable), TypeCompareKind.ConsiderEverything2) || - TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_FormattableString), TypeCompareKind.ConsiderEverything2)) + return (TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_IFormattable), TypeCompareKind.ConsiderEverything) || + TypeSymbol.Equals(destination, Compilation.GetWellKnownType(WellKnownType.System_FormattableString), TypeCompareKind.ConsiderEverything)) ? Conversion.InterpolatedString : Conversion.NoConversion; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index ac97e512467e1..e40db74c57369 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -4,10 +4,9 @@ #nullable disable -using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs index c3906596164d8..3599c567ad5f4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs @@ -6,9 +6,9 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Roslyn.Utilities; -using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.CodeAnalysis.CSharp { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs index 2ac4f6a1d8ecc..84cfad7da7317 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs @@ -2,13 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -16,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp internal sealed class AnalyzedArguments { public readonly ArrayBuilder Arguments; - public readonly ArrayBuilder Names; + public readonly ArrayBuilder<(string Name, Location Location)?> Names; public readonly ArrayBuilder RefKinds; public bool IsExtensionMethodInvocation; private ThreeState _lazyHasDynamicArgument; @@ -24,7 +21,7 @@ internal sealed class AnalyzedArguments internal AnalyzedArguments() { this.Arguments = new ArrayBuilder(32); - this.Names = new ArrayBuilder(32); + this.Names = new ArrayBuilder<(string, Location)?>(32); this.RefKinds = new ArrayBuilder(32); } @@ -42,18 +39,23 @@ public BoundExpression Argument(int i) return Arguments[i]; } - public string Name(int i) + public void AddName(IdentifierNameSyntax name) + { + Names.Add((name.Identifier.ValueText, name.Location)); + } + + public string? Name(int i) { if (Names.Count == 0) { return null; } - IdentifierNameSyntax syntax = Names[i]; - return syntax == null ? null : syntax.Identifier.ValueText; + var nameAndLocation = Names[i]; + return nameAndLocation?.Name; } - public ImmutableArray GetNames() + public ImmutableArray GetNames() { int count = this.Names.Count; @@ -62,7 +64,7 @@ public ImmutableArray GetNames() return default; } - var builder = ArrayBuilder.GetInstance(this.Names.Count); + var builder = ArrayBuilder.GetInstance(this.Names.Count); for (int i = 0; i < this.Names.Count; ++i) { builder.Add(Name(i)); @@ -96,7 +98,7 @@ public bool HasDynamicArgument var argument = Arguments[i]; // By-ref dynamic arguments don't make the invocation dynamic. - if ((object)argument.Type != null && argument.Type.IsDynamic() && (!hasRefKinds || RefKinds[i] == Microsoft.CodeAnalysis.RefKind.None)) + if ((object?)argument.Type != null && argument.Type.IsDynamic() && (!hasRefKinds || RefKinds[i] == Microsoft.CodeAnalysis.RefKind.None)) { _lazyHasDynamicArgument = ThreeState.True; return true; @@ -145,7 +147,7 @@ public static AnalyzedArguments GetInstance(AnalyzedArguments original) public static AnalyzedArguments GetInstance( ImmutableArray arguments, ImmutableArray argumentRefKindsOpt, - ImmutableArray argumentNamesOpt) + ImmutableArray<(string, Location)?> argumentNamesOpt) { var instance = GetInstance(); instance.Arguments.AddRange(arguments); @@ -174,7 +176,7 @@ public void Free() private static ObjectPool CreatePool() { - ObjectPool pool = null; + ObjectPool? pool = null; pool = new ObjectPool(() => new AnalyzedArguments(), 10); return pool; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 56dac18fef3df..52d777bb3f25b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -1025,9 +1025,13 @@ public static bool IsValidParams(Symbol member) return false; } - // Note: we need to confirm the "arrayness" on the original definition because - // it's possible that the type becomes an array as a result of substitution. ParameterSymbol final = member.GetParameters().Last(); + return IsValidParamsParameter(final); + } + + public static bool IsValidParamsParameter(ParameterSymbol final) + { + Debug.Assert((object)final == final.ContainingSymbol.GetParameters().Last()); return final.IsParams && ((ParameterSymbol)final.OriginalDefinition).Type.IsSZArray(); } @@ -1965,7 +1969,7 @@ private BetterResult BetterFunctionMember( } } - return PreferValOverInParameters(arguments, m1, m1LeastOverriddenParameters, m2, m2LeastOverriddenParameters); + return PreferValOverInOrRefInterpolatedHandlerParameters(arguments, m1, m1LeastOverriddenParameters, m2, m2LeastOverriddenParameters); } // If MP is a non-generic method and MQ is a generic method, then MP is better than MQ. @@ -2105,11 +2109,11 @@ private BetterResult BetterFunctionMember( return (m1ModifierCount < m2ModifierCount) ? BetterResult.Left : BetterResult.Right; } - // Otherwise, prefer methods with 'val' parameters over 'in' parameters. - return PreferValOverInParameters(arguments, m1, m1LeastOverriddenParameters, m2, m2LeastOverriddenParameters); + // Otherwise, prefer methods with 'val' parameters over 'in' parameters and over 'ref' parameters when the argument is an interpolated string handler. + return PreferValOverInOrRefInterpolatedHandlerParameters(arguments, m1, m1LeastOverriddenParameters, m2, m2LeastOverriddenParameters); } - private static BetterResult PreferValOverInParameters( + private static BetterResult PreferValOverInOrRefInterpolatedHandlerParameters( ArrayBuilder arguments, MemberResolutionResult m1, ImmutableArray parameters1, @@ -2117,7 +2121,7 @@ private static BetterResult PreferValOverInParameters( ImmutableArray parameters2) where TMember : Symbol { - BetterResult valOverInPreference = BetterResult.Neither; + BetterResult valOverInOrRefInterpolatedHandlerPreference = BetterResult.Neither; for (int i = 0; i < arguments.Count; ++i) { @@ -2126,32 +2130,51 @@ private static BetterResult PreferValOverInParameters( var p1 = GetParameter(i, m1.Result, parameters1); var p2 = GetParameter(i, m2.Result, parameters2); - if (p1.RefKind == RefKind.None && p2.RefKind == RefKind.In) + bool isInterpolatedStringHandlerConversion = false; + + if (m1.IsValid && m2.IsValid) + { + var c1 = m1.Result.ConversionForArg(i); + var c2 = m2.Result.ConversionForArg(i); + + isInterpolatedStringHandlerConversion = c1.IsInterpolatedStringHandler && c2.IsInterpolatedStringHandler; + Debug.Assert(!isInterpolatedStringHandlerConversion || arguments[i].Kind == BoundKind.UnconvertedInterpolatedString); + } + + if (p1.RefKind == RefKind.None && isAcceptableRefMismatch(p2.RefKind, isInterpolatedStringHandlerConversion)) { - if (valOverInPreference == BetterResult.Right) + if (valOverInOrRefInterpolatedHandlerPreference == BetterResult.Right) { return BetterResult.Neither; } else { - valOverInPreference = BetterResult.Left; + valOverInOrRefInterpolatedHandlerPreference = BetterResult.Left; } } - else if (p2.RefKind == RefKind.None && p1.RefKind == RefKind.In) + else if (p2.RefKind == RefKind.None && isAcceptableRefMismatch(p1.RefKind, isInterpolatedStringHandlerConversion)) { - if (valOverInPreference == BetterResult.Left) + if (valOverInOrRefInterpolatedHandlerPreference == BetterResult.Left) { return BetterResult.Neither; } else { - valOverInPreference = BetterResult.Right; + valOverInOrRefInterpolatedHandlerPreference = BetterResult.Right; } } } } - return valOverInPreference; + return valOverInOrRefInterpolatedHandlerPreference; + + static bool isAcceptableRefMismatch(RefKind refKind, bool isInterpolatedStringHandlerConversion) + => refKind switch + { + RefKind.In => true, + RefKind.Ref when isInterpolatedStringHandlerConversion => true, + _ => false + }; } private static void GetParameterCounts(MemberResolutionResult m, ArrayBuilder arguments, out int declaredParameterCount, out int parametersUsedIncludingExpansionAndOptional) where TMember : Symbol @@ -2401,6 +2424,26 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy return BetterResult.Neither; } + // C# 10 added interpolated string handler conversions, with the following rule: + // Given an implicit conversion C1 that converts from an expression E to a type T1, + // and an implicit conversion C2 that converts from an expression E to a type T2, + // C1 is a better conversion than C2 if E is a non-constant interpolated string expression, C1 + // is an interpolated string handler conversion, and C2 is not an interpolated string + // handler conversion + // https://github.com/dotnet/roslyn/issues/54584 Handle binary operators composed only of added interpolated strings + if (node is BoundUnconvertedInterpolatedString { ConstantValueOpt: null }) + { + switch ((conv1.Kind, conv2.Kind)) + { + case (ConversionKind.InterpolatedStringHandler, ConversionKind.InterpolatedStringHandler): + return BetterResult.Neither; + case (ConversionKind.InterpolatedStringHandler, _): + return BetterResult.Left; + case (_, ConversionKind.InterpolatedStringHandler): + return BetterResult.Right; + } + } + // Given an implicit conversion C1 that converts from an expression E to a type T1, // and an implicit conversion C2 that converts from an expression E to a type T2, // C1 is a better conversion than C2 if E does not exactly match T2 and one of the following holds: @@ -3589,6 +3632,18 @@ private MemberAnalysisResult IsApplicable( } } + bool hasInterpolatedStringRefMismatch = false; + if (argument.Kind == BoundKind.UnconvertedInterpolatedString + && parameterRefKind == RefKind.Ref + && parameters.ParameterTypes[argumentPosition].Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true, IsValueType: true }) + { + // For interpolated strings handlers, we allow an interpolated string expression to be passed as if `ref` was specified + // in the source when the handler type is a value type. + // https://github.com/dotnet/roslyn/issues/54584 allow binary additions of interpolated strings to match as well. + hasInterpolatedStringRefMismatch = true; + argumentRefKind = parameterRefKind; + } + conversion = CheckArgumentForApplicability( candidate, argument, @@ -3597,7 +3652,8 @@ private MemberAnalysisResult IsApplicable( parameterRefKind, ignoreOpenTypes, ref useSiteInfo, - forExtensionMethodThisArg); + forExtensionMethodThisArg, + hasInterpolatedStringRefMismatch); if (forExtensionMethodThisArg && !Conversions.IsValidExtensionMethodThisArgConversion(conversion)) { @@ -3614,7 +3670,7 @@ private MemberAnalysisResult IsApplicable( if (!conversion.Exists) { - badArguments = badArguments ?? ArrayBuilder.GetInstance(); + badArguments ??= ArrayBuilder.GetInstance(); badArguments.Add(argumentPosition); } } @@ -3658,7 +3714,8 @@ private Conversion CheckArgumentForApplicability( RefKind parRefKind, bool ignoreOpenTypes, ref CompoundUseSiteInfo useSiteInfo, - bool forExtensionMethodThisArg) + bool forExtensionMethodThisArg, + bool hasInterpolatedStringRefMismatch) { // Spec 7.5.3.1 // For each argument in A, the parameter passing mode of the argument (i.e., value, ref, or out) is identical @@ -3699,12 +3756,20 @@ private Conversion CheckArgumentForApplicability( return Conversion.Identity; } - if (argRefKind == RefKind.None) + if (argRefKind == RefKind.None || hasInterpolatedStringRefMismatch) { var conversion = forExtensionMethodThisArg ? Conversions.ClassifyImplicitExtensionMethodThisArgConversion(argument, argument.Type, parameterType, ref useSiteInfo) : Conversions.ClassifyImplicitConversionFromExpression(argument, parameterType, ref useSiteInfo); Debug.Assert((!conversion.Exists) || conversion.IsImplicit, "ClassifyImplicitConversion should only return implicit conversions"); + + if (hasInterpolatedStringRefMismatch && !conversion.IsInterpolatedStringHandler) + { + // We allowed a ref mismatch under the assumption the conversion would be an interpolated string handler conversion. If it's not, then there was + // actually no conversion because of the refkind mismatch. + return Conversion.NoConversion; + } + return conversion; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs index db382a0cad9f1..3297c38524090 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs @@ -476,8 +476,9 @@ internal void ReportDiagnostics( && firstSupported.Result.Kind == MemberResolutionKind.NoCorrespondingNamedParameter) { int badArg = firstSupported.Result.BadArgumentsOpt[0]; - IdentifierNameSyntax badName = arguments.Names[badArg]; - diagnostics.Add(ErrorCode.ERR_FunctionPointersCannotBeCalledWithNamedArguments, badName.Location); + Debug.Assert(arguments.Names[badArg].HasValue); + Location badName = arguments.Names[badArg].GetValueOrDefault().Location; + diagnostics.Add(ErrorCode.ERR_FunctionPointersCannotBeCalledWithNamedArguments, badName); return; } // If there are multiple supported candidates, we don't have a good way to choose the best @@ -773,15 +774,14 @@ private static void ReportNameUsedForPositional( int badArg = bad.Result.BadArgumentsOpt[0]; // We would not have gotten this error had there not been a named argument. Debug.Assert(arguments.Names.Count > badArg); - IdentifierNameSyntax badName = arguments.Names[badArg]; + Debug.Assert(arguments.Names[badArg].HasValue); + (string badName, Location location) = arguments.Names[badArg].GetValueOrDefault(); Debug.Assert(badName != null); // Named argument 'x' specifies a parameter for which a positional argument has already been given - Location location = new SourceLocation(badName); - diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_NamedArgumentUsedInPositional, - new object[] { badName.Identifier.ValueText }, + new object[] { badName }, symbols), location); } @@ -794,26 +794,26 @@ private static void ReportBadNonTrailingNamedArgument( int badArg = bad.Result.BadArgumentsOpt[0]; // We would not have gotten this error had there not been a named argument. Debug.Assert(arguments.Names.Count > badArg); - IdentifierNameSyntax badName = arguments.Names[badArg]; + Debug.Assert(arguments.Names[badArg].HasValue); + (string badName, Location location) = arguments.Names[badArg].GetValueOrDefault(); Debug.Assert(badName != null); // Named argument 'x' is used out-of-position but is followed by an unnamed argument. - Location location = new SourceLocation(badName); - diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_BadNonTrailingNamedArgument, - new object[] { badName.Identifier.ValueText }, + new object[] { badName }, symbols), location); } private static void ReportDuplicateNamedArgument(MemberResolutionResult result, BindingDiagnosticBag diagnostics, AnalyzedArguments arguments) { Debug.Assert(result.Result.BadArgumentsOpt.Length == 1); - IdentifierNameSyntax name = arguments.Names[result.Result.BadArgumentsOpt[0]]; + Debug.Assert(arguments.Names[result.Result.BadArgumentsOpt[0]].HasValue); + (string name, Location location) = arguments.Names[result.Result.BadArgumentsOpt[0]].GetValueOrDefault(); Debug.Assert(name != null); // CS: Named argument '{0}' cannot be specified multiple times - diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_DuplicateNamedArgument, name.Identifier.Text), name.Location); + diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_DuplicateNamedArgument, name), location); } private static void ReportNoCorrespondingNamedParameter( @@ -834,14 +834,13 @@ private static void ReportNoCorrespondingNamedParameter( int badArg = bad.Result.BadArgumentsOpt[0]; // We would not have gotten this error had there not been a named argument. Debug.Assert(arguments.Names.Count > badArg); - IdentifierNameSyntax badName = arguments.Names[badArg]; + Debug.Assert(arguments.Names[badArg].HasValue); + (string badName, Location location) = arguments.Names[badArg].GetValueOrDefault(); Debug.Assert(badName != null); // error CS1739: The best overload for 'M' does not have a parameter named 'x' // Error CS1746: The delegate 'D' does not have a parameter named 'x' - Location location = new SourceLocation(badName); - ErrorCode code = (object)delegateTypeBeingInvoked != null ? ErrorCode.ERR_BadNamedArgumentForDelegateInvoke : ErrorCode.ERR_BadNamedArgument; @@ -850,7 +849,7 @@ private static void ReportNoCorrespondingNamedParameter( diagnostics.Add(new DiagnosticInfoWithSymbols( code, - new object[] { obj, badName.Identifier.ValueText }, + new object[] { obj, badName }, symbols), location); } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs index 42a2797387613..41ce9c824011c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs @@ -312,12 +312,13 @@ private static ArgumentAnalysisResult AnalyzeArguments( // SPEC VIOLATION: parameter array and allow the candidate to be applicable in its // SPEC VIOLATION: expanded form. - var name = arguments.Names[argumentPosition]; + Debug.Assert(arguments.Names[argumentPosition].HasValue); + var name = arguments.Names[argumentPosition].GetValueOrDefault().Name; for (int p = 0; p < memberParameters.Length; ++p) { // p is initialized to zero; it is ok for a named argument to "correspond" to // _any_ parameter (not just the parameters past the point of positional arguments) - if (memberParameters[p].Name == name.Identifier.ValueText) + if (memberParameters[p].Name == name) { if (isValidParams && p == memberParameters.Length - 1) { diff --git a/src/Compilers/CSharp/Portable/Binder/WithExternAliasesBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithExternAliasesBinder.cs index afcd6d04cc841..c3c00349f6659 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithExternAliasesBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithExternAliasesBinder.cs @@ -90,7 +90,7 @@ private sealed class FromSyntax : WithExternAliasesBinder internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next) : base(next) { - Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration)); + Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration); _declaringSymbol = declaringSymbol; _declarationSyntax = declarationSyntax; } diff --git a/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs index 8c941649433af..e9be64e2660ac 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithExternAndUsingAliasesBinder.cs @@ -117,7 +117,7 @@ private sealed class FromSyntax : WithExternAndUsingAliasesBinder internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, WithUsingNamespacesAndTypesBinder next) : base(next) { - Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration)); + Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration); _declaringSymbol = declaringSymbol; _declarationSyntax = declarationSyntax; } @@ -186,7 +186,7 @@ internal override QuickAttributeChecker QuickAttributeChecker usingDirectives = compilationUnit.Usings; break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: usingDirectives = namespaceDecl.Usings; break; @@ -207,7 +207,7 @@ protected override ImportChain BuildImportChain() { var previous = Next!.ImportChain; - if (_declarationSyntax is NamespaceDeclarationSyntax namespaceDecl) + if (_declarationSyntax is BaseNamespaceDeclarationSyntax namespaceDecl) { // For each dotted name add an empty entry in the chain var name = namespaceDecl.Name; diff --git a/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs index 8ca9aa8054f37..7f0159118b522 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs @@ -276,7 +276,7 @@ private sealed class FromSyntax : WithUsingNamespacesAndTypesBinder internal FromSyntax(SourceNamespaceSymbol declaringSymbol, CSharpSyntaxNode declarationSyntax, Binder next, bool withImportChainEntry) : base(next, withImportChainEntry) { - Debug.Assert(declarationSyntax.IsKind(SyntaxKind.CompilationUnit) || declarationSyntax.IsKind(SyntaxKind.NamespaceDeclaration)); + Debug.Assert(declarationSyntax.Kind() is SyntaxKind.CompilationUnit or SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration); _declaringSymbol = declaringSymbol; _declarationSyntax = declarationSyntax; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs new file mode 100644 index 0000000000000..56043a700a00c --- /dev/null +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.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. + +namespace Microsoft.CodeAnalysis.CSharp +{ + internal partial class BoundInterpolatedStringArgumentPlaceholder + { + public const int InstanceParameter = -1; + public const int TrailingConstructorValidityParameter = -2; + public const int UnspecifiedParameter = -3; + } +} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs index 1444f798772fc..e328875b0d463 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodeExtensions.cs @@ -66,5 +66,33 @@ public static T MakeCompilerGenerated(this T node) where T : BoundNode node.WasCompilerGenerated = true; return node; } + + public static bool ContainsAwaitExpression(this ImmutableArray expressions) + { + var visitor = new ContainsAwaitVisitor(); + foreach (var expression in expressions) + { + visitor.Visit(expression); + if (visitor.ContainsAwait) + { + return true; + } + } + + return false; + } + + private class ContainsAwaitVisitor : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator + { + public bool ContainsAwait = false; + + public override BoundNode? Visit(BoundNode? node) => ContainsAwait ? null : base.Visit(node); + + public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node) + { + ContainsAwait = true; + return null; + } + } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index cef075a396654..866232c11182f 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -34,6 +34,7 @@ + @@ -1617,7 +1618,7 @@ - + @@ -1632,7 +1633,7 @@ - + @@ -2009,8 +2010,8 @@ + from the literal parts of the input. The odd numbered positions are the string inserts. If the interpolated + string has been bound using the builder pattern, literals are replaced with calls. --> @@ -2019,12 +2020,28 @@ + + + + + + + + + + + + + @@ -682,7 +682,7 @@ '{0}': no suitable method found to override - A namespace cannot directly contain members such as fields or methods + A namespace cannot directly contain members such as fields, methods or statements '{0}' does not contain a definition for '{1}' @@ -3740,6 +3740,9 @@ Give the compiler some way to differentiate the methods. For example, you can gi The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T> + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + Cannot return an expression of type 'void' @@ -4172,9 +4175,6 @@ You should consider suppressing the warning only if you're sure that you don't w The best overloaded Add method '{0}' for the collection initializer element is obsolete. {1} - - Yield statements may not appear at the top level in interactive code. - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. @@ -5317,9 +5317,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ For type '{0}' to be used as an AsyncMethodBuilder for type '{1}', its Task property should return type '{1}' instead of type '{2}'. - - Attributes are not allowed on local function parameters or type parameters - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. @@ -6613,6 +6610,18 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A function pointer cannot be called with named arguments. + + file-scoped namespace + + + Source file can only contain one file-scoped namespace declaration. + + + Source file can not contain both file-scoped and normal namespace declarations. + + + File-scoped namespace must precede all other members in a file. + Parameter '{0}' is unread. Did you forget to use it to initialize the property with that name? @@ -6658,6 +6667,16 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The positional member '{0}' found corresponding to this parameter is hidden. + + interpolated string handlers + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Identifier or a simple member access expected. @@ -6673,6 +6692,44 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A global using directive must precede all non-global using directives. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + + '{0}' is not a valid parameter name from '{1}'. + + + '{0}' is not an interpolated string handler type. + + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + Parameter to interpolated string handler conversion occurs after handler parameter + + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + An expression tree may not contain an interpolated string handler conversion. + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + static abstract members in interfaces diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index af32453b912f2..844bf5feb66eb 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -2895,6 +2895,15 @@ internal Conversion ClassifyConversionForCast(int position, ExpressionSyntax exp /// The namespace symbol that was declared by the namespace declaration. public abstract INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Given a namespace declaration syntax node, get the corresponding namespace symbol for + /// the declaration assembly. + /// + /// The syntax node that declares a namespace. + /// The cancellation token. + /// The namespace symbol that was declared by the namespace declaration. + public abstract INamespaceSymbol GetDeclaredSymbol(FileScopedNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)); + /// /// Given a type declaration, get the corresponding type symbol. /// @@ -5041,6 +5050,8 @@ protected sealed override ISymbol GetDeclaredSymbolCore(SyntaxNode node, Cancell return this.GetDeclaredSymbol((TupleElementSyntax)node, cancellationToken); case SyntaxKind.NamespaceDeclaration: return this.GetDeclaredSymbol((NamespaceDeclarationSyntax)node, cancellationToken); + case SyntaxKind.FileScopedNamespaceDeclaration: + return this.GetDeclaredSymbol((FileScopedNamespaceDeclarationSyntax)node, cancellationToken); case SyntaxKind.Parameter: return this.GetDeclaredSymbol((ParameterSyntax)node, cancellationToken); case SyntaxKind.TypeParameter: diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs index a106610962e31..1f1acde07e68b 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs @@ -580,6 +580,12 @@ private static BoundNode GetLowerBoundNode(ImmutableArray boundNodes) return null; } + public override INamespaceSymbol GetDeclaredSymbol(FileScopedNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) + { + // Can't defined namespace inside a member. + return null; + } + public override INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) { // Can't define type inside a member. diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 7177c2780e9dd..8214ff908929e 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -1330,13 +1330,7 @@ private bool IsRegularCSharp #region "GetDeclaredSymbol overloads for MemberDeclarationSyntax and its subtypes" - /// - /// Given a namespace declaration syntax node, get the corresponding namespace symbol for the declaration - /// assembly. - /// - /// The syntax node that declares a namespace. - /// The cancellation token. - /// The namespace symbol that was declared by the namespace declaration. + /// public override INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)) { CheckSyntaxNode(declarationSyntax); @@ -1344,7 +1338,15 @@ private bool IsRegularCSharp return GetDeclaredNamespace(declarationSyntax).GetPublicSymbol(); } - private NamespaceSymbol GetDeclaredNamespace(NamespaceDeclarationSyntax declarationSyntax) + /// + public override INamespaceSymbol GetDeclaredSymbol(FileScopedNamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default) + { + CheckSyntaxNode(declarationSyntax); + + return GetDeclaredNamespace(declarationSyntax).GetPublicSymbol(); + } + + private NamespaceSymbol GetDeclaredNamespace(BaseNamespaceDeclarationSyntax declarationSyntax) { Debug.Assert(declarationSyntax != null); @@ -1429,7 +1431,7 @@ private NamedTypeSymbol GetDeclaredNamedType(CSharpSyntaxNode declarationSyntax, private NamespaceOrTypeSymbol GetDeclaredNamespaceOrType(CSharpSyntaxNode declarationSyntax) { - var namespaceDeclarationSyntax = declarationSyntax as NamespaceDeclarationSyntax; + var namespaceDeclarationSyntax = declarationSyntax as BaseNamespaceDeclarationSyntax; if (namespaceDeclarationSyntax != null) { return GetDeclaredNamespace(namespaceDeclarationSyntax); @@ -2226,7 +2228,15 @@ private void ValidateStatementRange(StatementSyntax firstStatement, StatementSyn throw new ArgumentException("statements not within tree"); } - if (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent) + // Global statements don't have their parent in common, but should belong to the same compilation unit + bool isGlobalStatement = firstStatement.Parent is GlobalStatementSyntax; + if (isGlobalStatement && (lastStatement.Parent is not GlobalStatementSyntax || firstStatement.Parent.Parent != lastStatement.Parent.Parent)) + { + throw new ArgumentException("global statements not within the same compilation unit"); + } + + // Non-global statements, the parents should be the same + if (!isGlobalStatement && (firstStatement.Parent == null || firstStatement.Parent != lastStatement.Parent)) { throw new ArgumentException("statements not within the same statement list"); } @@ -2273,7 +2283,7 @@ private NamespaceOrTypeSymbol GetDeclaredTypeMemberContainer(CSharpSyntaxNode me if (memberDeclaration.Parent.Kind() == SyntaxKind.CompilationUnit) { // top-level namespace: - if (memberDeclaration.Kind() == SyntaxKind.NamespaceDeclaration) + if (memberDeclaration.Kind() is SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration) { return _compilation.Assembly.GlobalNamespace; } @@ -2304,7 +2314,8 @@ private NamespaceOrTypeSymbol GetDeclaredTypeMemberContainer(CSharpSyntaxNode me } // a namespace or a type in an explicitly declared namespace: - if (memberDeclaration.Kind() == SyntaxKind.NamespaceDeclaration || SyntaxFacts.IsTypeDeclaration(memberDeclaration.Kind())) + if (memberDeclaration.Kind() is SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration + || SyntaxFacts.IsTypeDeclaration(memberDeclaration.Kind())) { return container; } diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs index 67a98d7fceb9f..0268495646d04 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs @@ -36,7 +36,9 @@ internal static DeclarationKind ToDeclarationKind(this SyntaxKind kind) case SyntaxKind.ClassDeclaration: return DeclarationKind.Class; case SyntaxKind.InterfaceDeclaration: return DeclarationKind.Interface; case SyntaxKind.StructDeclaration: return DeclarationKind.Struct; - case SyntaxKind.NamespaceDeclaration: return DeclarationKind.Namespace; + case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: + return DeclarationKind.Namespace; case SyntaxKind.EnumDeclaration: return DeclarationKind.Enum; case SyntaxKind.DelegateDeclaration: return DeclarationKind.Delegate; case SyntaxKind.RecordDeclaration: return DeclarationKind.Record; diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index 4164baf567b67..29b002c315b7a 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -44,7 +44,9 @@ private ImmutableArray VisitNamespaceChildren( SyntaxList members, CoreInternalSyntax.SyntaxList internalMembers) { - Debug.Assert(node.Kind() == SyntaxKind.NamespaceDeclaration || (node.Kind() == SyntaxKind.CompilationUnit && _syntaxTree.Options.Kind == SourceCodeKind.Regular)); + Debug.Assert( + node.Kind() is SyntaxKind.NamespaceDeclaration or SyntaxKind.FileScopedNamespaceDeclaration || + (node.Kind() == SyntaxKind.CompilationUnit && _syntaxTree.Options.Kind == SourceCodeKind.Regular)); if (members.Count == 0) { @@ -310,9 +312,15 @@ private RootSingleNamespaceDeclaration CreateRootSingleNamespaceDeclaration(Comp diagnostics: diagnostics.ToReadOnlyAndFree()); } + public override SingleNamespaceOrTypeDeclaration VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) + => this.VisitBaseNamespaceDeclaration(node); + public override SingleNamespaceOrTypeDeclaration VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) + => this.VisitBaseNamespaceDeclaration(node); + + private SingleNamespaceDeclaration VisitBaseNamespaceDeclaration(BaseNamespaceDeclarationSyntax node) { - var children = VisitNamespaceChildren(node, node.Members, node.Green.Members); + var children = VisitNamespaceChildren(node, node.Members, ((Syntax.InternalSyntax.BaseNamespaceDeclarationSyntax)node.Green).Members); bool hasUsings = node.Usings.Any(); bool hasExterns = node.Externs.Any(); @@ -338,6 +346,57 @@ public override SingleNamespaceOrTypeDeclaration VisitNamespaceDeclaration(Names } var diagnostics = DiagnosticBag.GetInstance(); + + if (node is FileScopedNamespaceDeclarationSyntax) + { + if (node.Parent is FileScopedNamespaceDeclarationSyntax) + { + // Happens when user writes: + // namespace A.B; + // namespace X.Y; + diagnostics.Add(ErrorCode.ERR_MultipleFileScopedNamespace, node.Name.GetLocation()); + } + else if (node.Parent is NamespaceDeclarationSyntax) + { + // Happens with: + // + // namespace A.B + // { + // namespace X.Y; + diagnostics.Add(ErrorCode.ERR_FileScopedAndNormalNamespace, node.Name.GetLocation()); + } + else + { + // Happens with cases like: + // + // namespace A.B { } + // namespace X.Y; + // + // or even + // + // class C { } + // namespace X.Y; + + Debug.Assert(node.Parent is CompilationUnitSyntax); + var compilationUnit = (CompilationUnitSyntax)node.Parent; + if (node != compilationUnit.Members[0]) + { + diagnostics.Add(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, node.Name.GetLocation()); + } + } + } + else + { + Debug.Assert(node is NamespaceDeclarationSyntax); + + // namespace X.Y; + // namespace A.B { } + if (node.Parent is FileScopedNamespaceDeclarationSyntax) + { + diagnostics.Add(ErrorCode.ERR_FileScopedAndNormalNamespace, node.Name.GetLocation()); + } + } + if (ContainsGeneric(node.Name)) { // We're not allowed to have generics. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 4fa4ab7024e84..38d21fdfa5b58 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1207,7 +1207,7 @@ internal enum ErrorCode //WRN_PDBConstantStringValueTooLong = 7063, gave up on this warning ERR_CantOpenIcon = 7064, ERR_ErrorBuildingWin32Resources = 7065, - ERR_IteratorInInteractive = 7066, + // ERR_IteratorInInteractive = 7066, ERR_BadAttributeParamDefaultArgument = 7067, ERR_MissingTypeInSource = 7068, ERR_MissingTypeInAssembly = 7069, @@ -1468,7 +1468,7 @@ internal enum ErrorCode ERR_PublicSignNetModule = 8202, ERR_BadAssemblyName = 8203, ERR_BadAsyncMethodBuilderTaskProperty = 8204, - ERR_AttributesInLocalFuncDecl = 8205, + // ERR_AttributesInLocalFuncDecl = 8205, ERR_TypeForwardedToMultipleAssemblies = 8206, ERR_ExpressionTreeContainsDiscard = 8207, ERR_PatternDynamicType = 8208, @@ -1962,8 +1962,25 @@ internal enum ErrorCode ERR_SimpleProgramIsEmpty = 8937, ERR_LineSpanDirectiveInvalidValue = 8938, ERR_LineSpanDirectiveEndLessThanStart = 8939, - - ERR_AttrTypeArgCannotBeTypeVar = 8940, + ERR_WrongArityAsyncReturn = 8940, + + ERR_InterpolatedStringHandlerMethodReturnMalformed = 8941, + ERR_InterpolatedStringHandlerMethodReturnInconsistent = 8942, + ERR_NullInvalidInterpolatedStringHandlerArgumentName = 8943, + ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName = 8944, + ERR_InvalidInterpolatedStringHandlerArgumentName = 8945, + ERR_TypeIsNotAnInterpolatedStringHandlerType = 8946, + WRN_ParameterOccursAfterInterpolatedStringHandlerParameter = 8947, + ERR_CannotUseSelfAsInterpolatedStringHandlerArgument = 8948, + ERR_InterpolatedStringHandlerArgumentAttributeMalformed = 8949, + ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString = 8950, + ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified = 8951, + ERR_ExpressionTreeContainsInterpolatedStringHandlerConversion = 8952, + ERR_InterpolatedStringHandlerCreationCannotUseDynamic = 8953, + ERR_MultipleFileScopedNamespace = 8954, + ERR_FileScopedAndNormalNamespace = 8955, + ERR_FileScopedNamespaceNotBeforeAllMembers = 8956, + ERR_AttrTypeArgCannotBeTypeVar = 8957, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 92526b2b489db..368793794dcf3 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -476,6 +476,7 @@ internal static int GetWarningLevel(ErrorCode code) case ErrorCode.WRN_AnalyzerReferencesFramework: case ErrorCode.WRN_UnreadRecordParameter: case ErrorCode.WRN_DoNotCompareFunctionPointers: + case ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter: return 1; default: return 0; diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index d066f32cb78d3..b3273752e7006 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -223,6 +223,7 @@ internal enum MessageID IDS_FeatureGlobalUsing = MessageBase + 12798, IDS_FeatureInferredDelegateType = MessageBase + 12799, IDS_FeatureLambdaAttributes = MessageBase + 12800, + IDS_FeatureWithOnAnonymousTypes = MessageBase + 12801, IDS_FeatureExtendedPropertyPatterns = MessageBase + 12802, IDS_FeatureStaticAbstractMembersInInterfaces = MessageBase + 12803, @@ -230,7 +231,9 @@ internal enum MessageID IDS_AsyncMethodBuilderOverride = MessageBase + 12805, IDS_FeatureImplicitImplementationOfNonPublicMemebers = MessageBase + 12806, IDS_FeatureLineSpanDirective = MessageBase + 12807, - IDS_FeatureGenericAttributes = MessageBase + 12808, + IDS_FeatureImprovedInterpolatedStrings = MessageBase + 12808, + IDS_FeatureFileScopedNamespace = MessageBase + 12809, + IDS_FeatureGenericAttributes = MessageBase + 12810, } // Message IDs may refer to strings that need to be localized. @@ -344,6 +347,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) // C# 10.0 features. case MessageID.IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction: // semantic check case MessageID.IDS_FeatureSealedToStringInRecord: // semantic check + case MessageID.IDS_FeatureImprovedInterpolatedStrings: // semantic check case MessageID.IDS_FeatureRecordStructs: case MessageID.IDS_FeatureWithOnStructs: // semantic check case MessageID.IDS_FeatureWithOnAnonymousTypes: // semantic check @@ -357,6 +361,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureConstantInterpolatedStrings: // semantic check case MessageID.IDS_FeatureImplicitImplementationOfNonPublicMemebers: // semantic check case MessageID.IDS_FeatureLineSpanDirective: + case MessageID.IDS_FeatureFileScopedNamespace: // syntax check case MessageID.IDS_FeatureGenericAttributes: // semantic check return LanguageVersion.CSharp10; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index df4d53bdf8607..f74aab6458d45 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -1149,23 +1150,85 @@ public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node) return null; } - protected BoundNode VisitInterpolatedStringBase(BoundInterpolatedStringBase node) + protected BoundNode VisitInterpolatedStringBase(BoundInterpolatedStringBase node, InterpolatedStringHandlerData? data) { - foreach (var expr in node.Parts) + // If there can be any branching, then we need to treat the expressions + // as optionally evaluated. Otherwise, we treat them as always evaluated + switch (data) { - VisitRvalue(expr); + case null: + visitParts(); + break; + case { HasTrailingHandlerValidityParameter: false, UsesBoolReturns: false, Construction: var construction }: + VisitRvalue(construction); + visitParts(); + break; + case { UsesBoolReturns: var usesBoolReturns, HasTrailingHandlerValidityParameter: var hasTrailingValidityParameter, Construction: var construction }: + VisitRvalue(construction); + + if (node.Parts.IsEmpty) + { + break; + } + + TLocalState beforePartsState; + ReadOnlySpan remainingParts; + + if (hasTrailingValidityParameter) + { + beforePartsState = State.Clone(); + remainingParts = node.Parts.AsSpan(); + } + else + { + Visit(node.Parts[0]); + beforePartsState = State.Clone(); + remainingParts = node.Parts.AsSpan()[1..]; + } + + foreach (var expr in remainingParts) + { + VisitRvalue(expr); + if (usesBoolReturns) + { + Join(ref beforePartsState, ref State); + } + } + + if (usesBoolReturns) + { + // Already been joined after the last part, just assign + State = beforePartsState; + } + else + { + Debug.Assert(hasTrailingValidityParameter); + Join(ref State, ref beforePartsState); + } + + break; } + return null; + + void visitParts() + { + foreach (var expr in node.Parts) + { + VisitRvalue(expr); + } + } } public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) { - return VisitInterpolatedStringBase(node); + return VisitInterpolatedStringBase(node, node.InterpolationData); } public override BoundNode VisitUnconvertedInterpolatedString(BoundUnconvertedInterpolatedString node) { - return VisitInterpolatedStringBase(node); + // If the node is unconverted, we'll just treat it as if the contents are always evaluated + return VisitInterpolatedStringBase(node, data: null); } public override BoundNode VisitStringInsert(BoundStringInsert node) @@ -1184,6 +1247,16 @@ public override BoundNode VisitStringInsert(BoundStringInsert node) return null; } + public override BoundNode VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) + { + return null; + } + + public override BoundNode VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) + { + return null; + } + public override BoundNode VisitArgList(BoundArgList node) { // The "__arglist" expression that is legal inside a varargs method has no diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index 2ee0728aaca87..d10c77498e301 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -243,6 +243,16 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperatorBase node) // info is exposed to consumers. return null; } + + public override BoundNode? VisitInterpolatedString(BoundInterpolatedString node) + { + if (node.InterpolationData is { Construction: var construction }) + { + Visit(construction); + } + base.VisitInterpolatedString(node); + return null; + } } #endif } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 6cad61e9557d2..73507e79b189c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6983,6 +6983,11 @@ private TypeWithState VisitConversion( resultState = NullableFlowState.NotNull; break; + case ConversionKind.InterpolatedStringHandler: + // https://github.com/dotnet/roslyn/issues/54583 Handle + resultState = NullableFlowState.NotNull; + break; + case ConversionKind.ObjectCreation: case ConversionKind.SwitchExpression: case ConversionKind.ConditionalExpression: @@ -9764,6 +9769,8 @@ private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2) public override BoundNode? VisitInterpolatedString(BoundInterpolatedString node) { + // https://github.com/dotnet/roslyn/issues/54583 + // Better handle the constructor propogation var result = base.VisitInterpolatedString(node); SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.NotNull)); return result; @@ -9784,6 +9791,18 @@ private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2) return result; } + public override BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) + { + SetNotNullResult(node); + return null; + } + + public override BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) + { + SetNotNullResult(node); + return null; + } + public override BoundNode? VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression node) { var result = base.VisitConvertedStackAllocExpression(node); diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index b6df30a1a81c0..fa2e11b51b2df 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -201,6 +201,8 @@ internal enum BoundKind : byte NameOfOperator, UnconvertedInterpolatedString, InterpolatedString, + InterpolatedStringHandlerPlaceholder, + InterpolatedStringArgumentPlaceholder, StringInsert, IsPatternExpression, ConstantPattern, @@ -239,6 +241,7 @@ internal enum BoundKind : byte + internal abstract partial class BoundInitializer : BoundNode { protected BoundInitializer(BoundKind kind, SyntaxNode syntax, bool hasErrors) @@ -5775,7 +5778,7 @@ public BoundEventAssignmentOperator Update(EventSymbol @event, bool isAddition, internal sealed partial class BoundAttribute : BoundExpression { - public BoundAttribute(SyntaxNode syntax, MethodSymbol? constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray constructorArgumentsToParamsOpt, bool constructorExpanded, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) + public BoundAttribute(SyntaxNode syntax, MethodSymbol? constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray constructorArgumentsToParamsOpt, bool constructorExpanded, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) : base(BoundKind.Attribute, syntax, type, hasErrors || constructorArguments.HasErrors() || namedArguments.HasErrors()) { @@ -5799,7 +5802,7 @@ public BoundAttribute(SyntaxNode syntax, MethodSymbol? constructor, ImmutableArr public ImmutableArray ConstructorArguments { get; } - public ImmutableArray ConstructorArgumentNamesOpt { get; } + public ImmutableArray ConstructorArgumentNamesOpt { get; } public ImmutableArray ConstructorArgumentsToParamsOpt { get; } @@ -5812,7 +5815,7 @@ public BoundAttribute(SyntaxNode syntax, MethodSymbol? constructor, ImmutableArr [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitAttribute(this); - public BoundAttribute Update(MethodSymbol? constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray constructorArgumentsToParamsOpt, bool constructorExpanded, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type) + public BoundAttribute Update(MethodSymbol? constructor, ImmutableArray constructorArguments, ImmutableArray constructorArgumentNamesOpt, ImmutableArray constructorArgumentsToParamsOpt, bool constructorExpanded, ImmutableArray namedArguments, LookupResultKind resultKind, TypeSymbol type) { if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(constructor, this.Constructor) || constructorArguments != this.ConstructorArguments || constructorArgumentNamesOpt != this.ConstructorArgumentNamesOpt || constructorArgumentsToParamsOpt != this.ConstructorArgumentsToParamsOpt || constructorExpanded != this.ConstructorExpanded || namedArguments != this.NamedArguments || resultKind != this.ResultKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { @@ -5826,7 +5829,7 @@ public BoundAttribute Update(MethodSymbol? constructor, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, InitializerExpressionSyntax? initializerOpt, bool hasErrors = false) + public BoundUnconvertedObjectCreationExpression(SyntaxNode syntax, ImmutableArray arguments, ImmutableArray<(string Name, Location Location)?> argumentNamesOpt, ImmutableArray argumentRefKindsOpt, InitializerExpressionSyntax? initializerOpt, bool hasErrors = false) : base(BoundKind.UnconvertedObjectCreationExpression, syntax, null, hasErrors || arguments.HasErrors()) { @@ -5843,7 +5846,7 @@ public BoundUnconvertedObjectCreationExpression(SyntaxNode syntax, ImmutableArra public ImmutableArray Arguments { get; } - public ImmutableArray ArgumentNamesOpt { get; } + public ImmutableArray<(string Name, Location Location)?> ArgumentNamesOpt { get; } public ImmutableArray ArgumentRefKindsOpt { get; } @@ -5851,7 +5854,7 @@ public BoundUnconvertedObjectCreationExpression(SyntaxNode syntax, ImmutableArra [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitUnconvertedObjectCreationExpression(this); - public BoundUnconvertedObjectCreationExpression Update(ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, InitializerExpressionSyntax? initializerOpt) + public BoundUnconvertedObjectCreationExpression Update(ImmutableArray arguments, ImmutableArray<(string Name, Location Location)?> argumentNamesOpt, ImmutableArray argumentRefKindsOpt, InitializerExpressionSyntax? initializerOpt) { if (arguments != this.Arguments || argumentNamesOpt != this.ArgumentNamesOpt || argumentRefKindsOpt != this.ArgumentRefKindsOpt || initializerOpt != this.InitializerOpt) { @@ -7227,22 +7230,95 @@ public BoundUnconvertedInterpolatedString Update(ImmutableArray internal sealed partial class BoundInterpolatedString : BoundInterpolatedStringBase { - public BoundInterpolatedString(SyntaxNode syntax, ImmutableArray parts, ConstantValue? constantValueOpt, TypeSymbol? type, bool hasErrors = false) + public BoundInterpolatedString(SyntaxNode syntax, InterpolatedStringHandlerData? interpolationData, ImmutableArray parts, ConstantValue? constantValueOpt, TypeSymbol? type, bool hasErrors = false) : base(BoundKind.InterpolatedString, syntax, parts, constantValueOpt, type, hasErrors || parts.HasErrors()) { RoslynDebug.Assert(!parts.IsDefault, "Field 'parts' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); + this.InterpolationData = interpolationData; } + + public InterpolatedStringHandlerData? InterpolationData { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitInterpolatedString(this); - public BoundInterpolatedString Update(ImmutableArray parts, ConstantValue? constantValueOpt, TypeSymbol? type) + public BoundInterpolatedString Update(InterpolatedStringHandlerData? interpolationData, ImmutableArray parts, ConstantValue? constantValueOpt, TypeSymbol? type) { - if (parts != this.Parts || constantValueOpt != this.ConstantValueOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (interpolationData.Equals(this.InterpolationData) || parts != this.Parts || constantValueOpt != this.ConstantValueOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundInterpolatedString(this.Syntax, interpolationData, parts, constantValueOpt, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + + internal sealed partial class BoundInterpolatedStringHandlerPlaceholder : BoundValuePlaceholderBase + { + public BoundInterpolatedStringHandlerPlaceholder(SyntaxNode syntax, TypeSymbol? type, bool hasErrors) + : base(BoundKind.InterpolatedStringHandlerPlaceholder, syntax, type, hasErrors) + { + } + + public BoundInterpolatedStringHandlerPlaceholder(SyntaxNode syntax, TypeSymbol? type) + : base(BoundKind.InterpolatedStringHandlerPlaceholder, syntax, type) + { + } + + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitInterpolatedStringHandlerPlaceholder(this); + + public BoundInterpolatedStringHandlerPlaceholder Update(TypeSymbol? type) + { + if (!TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundInterpolatedString(this.Syntax, parts, constantValueOpt, type, this.HasErrors); + var result = new BoundInterpolatedStringHandlerPlaceholder(this.Syntax, type, this.HasErrors); + result.CopyAttributes(this); + return result; + } + return this; + } + } + + internal sealed partial class BoundInterpolatedStringArgumentPlaceholder : BoundValuePlaceholderBase + { + public BoundInterpolatedStringArgumentPlaceholder(SyntaxNode syntax, int argumentIndex, uint valSafeToEscape, TypeSymbol type, bool hasErrors) + : base(BoundKind.InterpolatedStringArgumentPlaceholder, syntax, type, hasErrors) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ArgumentIndex = argumentIndex; + this.ValSafeToEscape = valSafeToEscape; + } + + public BoundInterpolatedStringArgumentPlaceholder(SyntaxNode syntax, int argumentIndex, uint valSafeToEscape, TypeSymbol type) + : base(BoundKind.InterpolatedStringArgumentPlaceholder, syntax, type) + { + + RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + + this.ArgumentIndex = argumentIndex; + this.ValSafeToEscape = valSafeToEscape; + } + + + public new TypeSymbol Type => base.Type!; + + public int ArgumentIndex { get; } + + public uint ValSafeToEscape { get; } + [DebuggerStepThrough] + public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitInterpolatedStringArgumentPlaceholder(this); + + public BoundInterpolatedStringArgumentPlaceholder Update(int argumentIndex, uint valSafeToEscape, TypeSymbol type) + { + if (argumentIndex != this.ArgumentIndex || valSafeToEscape != this.ValSafeToEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + { + var result = new BoundInterpolatedStringArgumentPlaceholder(this.Syntax, argumentIndex, valSafeToEscape, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7252,8 +7328,8 @@ public BoundInterpolatedString Update(ImmutableArray parts, Con internal sealed partial class BoundStringInsert : BoundExpression { - public BoundStringInsert(SyntaxNode syntax, BoundExpression value, BoundExpression? alignment, BoundLiteral? format, TypeSymbol? type, bool hasErrors = false) - : base(BoundKind.StringInsert, syntax, type, hasErrors || value.HasErrors() || alignment.HasErrors() || format.HasErrors()) + public BoundStringInsert(SyntaxNode syntax, BoundExpression value, BoundExpression? alignment, BoundLiteral? format, bool isInterpolatedStringHandlerAppendCall, bool hasErrors = false) + : base(BoundKind.StringInsert, syntax, null, hasErrors || value.HasErrors() || alignment.HasErrors() || format.HasErrors()) { RoslynDebug.Assert(value is object, "Field 'value' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -7261,22 +7337,27 @@ public BoundStringInsert(SyntaxNode syntax, BoundExpression value, BoundExpressi this.Value = value; this.Alignment = alignment; this.Format = format; + this.IsInterpolatedStringHandlerAppendCall = isInterpolatedStringHandlerAppendCall; } + public new TypeSymbol? Type => base.Type; + public BoundExpression Value { get; } public BoundExpression? Alignment { get; } public BoundLiteral? Format { get; } + + public bool IsInterpolatedStringHandlerAppendCall { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitStringInsert(this); - public BoundStringInsert Update(BoundExpression value, BoundExpression? alignment, BoundLiteral? format, TypeSymbol? type) + public BoundStringInsert Update(BoundExpression value, BoundExpression? alignment, BoundLiteral? format, bool isInterpolatedStringHandlerAppendCall) { - if (value != this.Value || alignment != this.Alignment || format != this.Format || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (value != this.Value || alignment != this.Alignment || format != this.Format || isInterpolatedStringHandlerAppendCall != this.IsInterpolatedStringHandlerAppendCall) { - var result = new BoundStringInsert(this.Syntax, value, alignment, format, type, this.HasErrors); + var result = new BoundStringInsert(this.Syntax, value, alignment, format, isInterpolatedStringHandlerAppendCall, this.HasErrors); result.CopyAttributes(this); return result; } @@ -8455,6 +8536,10 @@ internal R VisitInternal(BoundNode node, A arg) return VisitUnconvertedInterpolatedString((BoundUnconvertedInterpolatedString)node, arg); case BoundKind.InterpolatedString: return VisitInterpolatedString((BoundInterpolatedString)node, arg); + case BoundKind.InterpolatedStringHandlerPlaceholder: + return VisitInterpolatedStringHandlerPlaceholder((BoundInterpolatedStringHandlerPlaceholder)node, arg); + case BoundKind.InterpolatedStringArgumentPlaceholder: + return VisitInterpolatedStringArgumentPlaceholder((BoundInterpolatedStringArgumentPlaceholder)node, arg); case BoundKind.StringInsert: return VisitStringInsert((BoundStringInsert)node, arg); case BoundKind.IsPatternExpression: @@ -8690,6 +8775,8 @@ internal abstract partial class BoundTreeVisitor public virtual R VisitNameOfOperator(BoundNameOfOperator node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitUnconvertedInterpolatedString(BoundUnconvertedInterpolatedString node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitInterpolatedString(BoundInterpolatedString node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node, A arg) => this.DefaultVisit(node, arg); + public virtual R VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitStringInsert(BoundStringInsert node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitIsPatternExpression(BoundIsPatternExpression node, A arg) => this.DefaultVisit(node, arg); public virtual R VisitConstantPattern(BoundConstantPattern node, A arg) => this.DefaultVisit(node, arg); @@ -8898,6 +8985,8 @@ internal abstract partial class BoundTreeVisitor public virtual BoundNode? VisitNameOfOperator(BoundNameOfOperator node) => this.DefaultVisit(node); public virtual BoundNode? VisitUnconvertedInterpolatedString(BoundUnconvertedInterpolatedString node) => this.DefaultVisit(node); public virtual BoundNode? VisitInterpolatedString(BoundInterpolatedString node) => this.DefaultVisit(node); + public virtual BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) => this.DefaultVisit(node); + public virtual BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) => this.DefaultVisit(node); public virtual BoundNode? VisitStringInsert(BoundStringInsert node) => this.DefaultVisit(node); public virtual BoundNode? VisitIsPatternExpression(BoundIsPatternExpression node) => this.DefaultVisit(node); public virtual BoundNode? VisitConstantPattern(BoundConstantPattern node) => this.DefaultVisit(node); @@ -9739,6 +9828,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor this.VisitList(node.Parts); return null; } + public override BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) => null; + public override BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) => null; public override BoundNode? VisitStringInsert(BoundStringInsert node) { this.Visit(node.Value); @@ -10917,7 +11008,17 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { ImmutableArray parts = this.VisitList(node.Parts); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(parts, node.ConstantValueOpt, type); + return node.Update(node.InterpolationData, parts, node.ConstantValueOpt, type); + } + public override BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(type); + } + public override BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) + { + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(node.ArgumentIndex, node.ValSafeToEscape, type); } public override BoundNode? VisitStringInsert(BoundStringInsert node) { @@ -10925,7 +11026,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? alignment = (BoundExpression?)this.Visit(node.Alignment); BoundLiteral? format = (BoundLiteral?)this.Visit(node.Format); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(value, alignment, format, type); + return node.Update(value, alignment, format, node.IsInterpolatedStringHandlerAppendCall); } public override BoundNode? VisitIsPatternExpression(BoundIsPatternExpression node) { @@ -13203,16 +13304,40 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("interpolatedString", null, new TreeDumperNode[] { + new TreeDumperNode("interpolationData", node.InterpolationData, null), new TreeDumperNode("parts", null, from x in node.Parts select Visit(x, null)), new TreeDumperNode("constantValueOpt", node.ConstantValueOpt, null), new TreeDumperNode("type", node.Type, null), @@ -15143,11 +15269,28 @@ private BoundTreeDumperNodeProducer() new TreeDumperNode("hasErrors", node.HasErrors, null) } ); + public override TreeDumperNode VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node, object? arg) => new TreeDumperNode("interpolatedStringHandlerPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); + public override TreeDumperNode VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node, object? arg) => new TreeDumperNode("interpolatedStringArgumentPlaceholder", null, new TreeDumperNode[] + { + new TreeDumperNode("argumentIndex", node.ArgumentIndex, null), + new TreeDumperNode("valSafeToEscape", node.ValSafeToEscape, null), + new TreeDumperNode("type", node.Type, null), + new TreeDumperNode("isSuppressed", node.IsSuppressed, null), + new TreeDumperNode("hasErrors", node.HasErrors, null) + } + ); public override TreeDumperNode VisitStringInsert(BoundStringInsert node, object? arg) => new TreeDumperNode("stringInsert", null, new TreeDumperNode[] { new TreeDumperNode("value", null, new TreeDumperNode[] { Visit(node.Value, null) }), new TreeDumperNode("alignment", null, new TreeDumperNode[] { Visit(node.Alignment, null) }), new TreeDumperNode("format", null, new TreeDumperNode[] { Visit(node.Format, null) }), + new TreeDumperNode("isInterpolatedStringHandlerAppendCall", node.IsInterpolatedStringHandlerAppendCall, null), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) diff --git a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 index 9450e00d62f58..331e0220160a7 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 +++ b/src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 @@ -76,13 +76,13 @@ name_colon member_declaration : base_field_declaration | base_method_declaration + | base_namespace_declaration | base_property_declaration | base_type_declaration | delegate_declaration | enum_member_declaration | global_statement | incomplete_member - | namespace_declaration ; base_field_declaration @@ -232,6 +232,19 @@ operator_declaration : attribute_list* modifier* type explicit_interface_specifier? 'operator' ('+' | '-' | '!' | '~' | '++' | '--' | '*' | '/' | '%' | '<<' | '>>' | '|' | '&' | '^' | '==' | '!=' | '<' | '<=' | '>' | '>=' | 'false' | 'true' | 'is') parameter_list (block | (arrow_expression_clause ';')) ; +base_namespace_declaration + : file_scoped_namespace_declaration + | namespace_declaration + ; + +file_scoped_namespace_declaration + : attribute_list* modifier* 'namespace' name ';' extern_alias_directive* using_directive* member_declaration* + ; + +namespace_declaration + : attribute_list* modifier* 'namespace' name '{' extern_alias_directive* using_directive* member_declaration* '}' ';'? + ; + base_property_declaration : event_declaration | indexer_declaration @@ -327,10 +340,6 @@ incomplete_member : attribute_list* modifier* type? ; -namespace_declaration - : attribute_list* modifier* 'namespace' name '{' extern_alias_directive* using_directive* member_declaration* '}' ';'? - ; - type : array_type | function_pointer_type diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs index 091928df6b8dc..c8656985fd5e1 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs @@ -19505,7 +19505,35 @@ protected MemberDeclarationSyntax(ObjectReader reader) public abstract Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Modifiers { get; } } - internal sealed partial class NamespaceDeclarationSyntax : MemberDeclarationSyntax + internal abstract partial class BaseNamespaceDeclarationSyntax : MemberDeclarationSyntax + { + internal BaseNamespaceDeclarationSyntax(SyntaxKind kind, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + } + + internal BaseNamespaceDeclarationSyntax(SyntaxKind kind) + : base(kind) + { + } + + protected BaseNamespaceDeclarationSyntax(ObjectReader reader) + : base(reader) + { + } + + public abstract SyntaxToken NamespaceKeyword { get; } + + public abstract NameSyntax Name { get; } + + public abstract Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Externs { get; } + + public abstract Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Usings { get; } + + public abstract Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Members { get; } + } + + internal sealed partial class NamespaceDeclarationSyntax : BaseNamespaceDeclarationSyntax { internal readonly GreenNode? attributeLists; internal readonly GreenNode? modifiers; @@ -19653,12 +19681,12 @@ internal NamespaceDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList AttributeLists => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.attributeLists); public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Modifiers => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.modifiers); - public SyntaxToken NamespaceKeyword => this.namespaceKeyword; - public NameSyntax Name => this.name; + public override SyntaxToken NamespaceKeyword => this.namespaceKeyword; + public override NameSyntax Name => this.name; public SyntaxToken OpenBraceToken => this.openBraceToken; - public Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Externs => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.externs); - public Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Usings => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.usings); - public Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Members => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.members); + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Externs => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.externs); + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Usings => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.usings); + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Members => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.members); public SyntaxToken CloseBraceToken => this.closeBraceToken; /// Gets the optional semicolon token. public SyntaxToken? SemicolonToken => this.semicolonToken; @@ -19782,6 +19810,244 @@ static NamespaceDeclarationSyntax() } } + internal sealed partial class FileScopedNamespaceDeclarationSyntax : BaseNamespaceDeclarationSyntax + { + internal readonly GreenNode? attributeLists; + internal readonly GreenNode? modifiers; + internal readonly SyntaxToken namespaceKeyword; + internal readonly NameSyntax name; + internal readonly SyntaxToken semicolonToken; + internal readonly GreenNode? externs; + internal readonly GreenNode? usings; + internal readonly GreenNode? members; + + internal FileScopedNamespaceDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, GreenNode? externs, GreenNode? usings, GreenNode? members, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 8; + if (attributeLists != null) + { + this.AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + if (modifiers != null) + { + this.AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + this.AdjustFlagsAndWidth(namespaceKeyword); + this.namespaceKeyword = namespaceKeyword; + this.AdjustFlagsAndWidth(name); + this.name = name; + this.AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + if (externs != null) + { + this.AdjustFlagsAndWidth(externs); + this.externs = externs; + } + if (usings != null) + { + this.AdjustFlagsAndWidth(usings); + this.usings = usings; + } + if (members != null) + { + this.AdjustFlagsAndWidth(members); + this.members = members; + } + } + + internal FileScopedNamespaceDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, GreenNode? externs, GreenNode? usings, GreenNode? members, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 8; + if (attributeLists != null) + { + this.AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + if (modifiers != null) + { + this.AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + this.AdjustFlagsAndWidth(namespaceKeyword); + this.namespaceKeyword = namespaceKeyword; + this.AdjustFlagsAndWidth(name); + this.name = name; + this.AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + if (externs != null) + { + this.AdjustFlagsAndWidth(externs); + this.externs = externs; + } + if (usings != null) + { + this.AdjustFlagsAndWidth(usings); + this.usings = usings; + } + if (members != null) + { + this.AdjustFlagsAndWidth(members); + this.members = members; + } + } + + internal FileScopedNamespaceDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, GreenNode? externs, GreenNode? usings, GreenNode? members) + : base(kind) + { + this.SlotCount = 8; + if (attributeLists != null) + { + this.AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + if (modifiers != null) + { + this.AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + this.AdjustFlagsAndWidth(namespaceKeyword); + this.namespaceKeyword = namespaceKeyword; + this.AdjustFlagsAndWidth(name); + this.name = name; + this.AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + if (externs != null) + { + this.AdjustFlagsAndWidth(externs); + this.externs = externs; + } + if (usings != null) + { + this.AdjustFlagsAndWidth(usings); + this.usings = usings; + } + if (members != null) + { + this.AdjustFlagsAndWidth(members); + this.members = members; + } + } + + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList AttributeLists => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.attributeLists); + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Modifiers => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.modifiers); + public override SyntaxToken NamespaceKeyword => this.namespaceKeyword; + public override NameSyntax Name => this.name; + public SyntaxToken SemicolonToken => this.semicolonToken; + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Externs => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.externs); + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Usings => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.usings); + public override Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList Members => new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(this.members); + + internal override GreenNode? GetSlot(int index) + => index switch + { + 0 => this.attributeLists, + 1 => this.modifiers, + 2 => this.namespaceKeyword, + 3 => this.name, + 4 => this.semicolonToken, + 5 => this.externs, + 6 => this.usings, + 7 => this.members, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.FileScopedNamespaceDeclarationSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitFileScopedNamespaceDeclaration(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitFileScopedNamespaceDeclaration(this); + + public FileScopedNamespaceDeclarationSyntax Update(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList externs, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList usings, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList members) + { + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || namespaceKeyword != this.NamespaceKeyword || name != this.Name || semicolonToken != this.SemicolonToken || externs != this.Externs || usings != this.Usings || members != this.Members) + { + var newNode = SyntaxFactory.FileScopedNamespaceDeclaration(attributeLists, modifiers, namespaceKeyword, name, semicolonToken, externs, usings, members); + 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 FileScopedNamespaceDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.namespaceKeyword, this.name, this.semicolonToken, this.externs, this.usings, this.members, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new FileScopedNamespaceDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.namespaceKeyword, this.name, this.semicolonToken, this.externs, this.usings, this.members, GetDiagnostics(), annotations); + + internal FileScopedNamespaceDeclarationSyntax(ObjectReader reader) + : base(reader) + { + this.SlotCount = 8; + var attributeLists = (GreenNode?)reader.ReadValue(); + if (attributeLists != null) + { + AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + var modifiers = (GreenNode?)reader.ReadValue(); + if (modifiers != null) + { + AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + var namespaceKeyword = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(namespaceKeyword); + this.namespaceKeyword = namespaceKeyword; + var name = (NameSyntax)reader.ReadValue(); + AdjustFlagsAndWidth(name); + this.name = name; + var semicolonToken = (SyntaxToken)reader.ReadValue(); + AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + var externs = (GreenNode?)reader.ReadValue(); + if (externs != null) + { + AdjustFlagsAndWidth(externs); + this.externs = externs; + } + var usings = (GreenNode?)reader.ReadValue(); + if (usings != null) + { + AdjustFlagsAndWidth(usings); + this.usings = usings; + } + var members = (GreenNode?)reader.ReadValue(); + if (members != null) + { + AdjustFlagsAndWidth(members); + this.members = members; + } + } + + internal override void WriteTo(ObjectWriter writer) + { + base.WriteTo(writer); + writer.WriteValue(this.attributeLists); + writer.WriteValue(this.modifiers); + writer.WriteValue(this.namespaceKeyword); + writer.WriteValue(this.name); + writer.WriteValue(this.semicolonToken); + writer.WriteValue(this.externs); + writer.WriteValue(this.usings); + writer.WriteValue(this.members); + } + + static FileScopedNamespaceDeclarationSyntax() + { + ObjectBinder.RegisterTypeReader(typeof(FileScopedNamespaceDeclarationSyntax), r => new FileScopedNamespaceDeclarationSyntax(r)); + } + } + /// Class representing one or more attributes applied to a language construct. internal sealed partial class AttributeListSyntax : CSharpSyntaxNode { @@ -33760,6 +34026,7 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitExternAliasDirective(ExternAliasDirectiveSyntax node) => this.DefaultVisit(node); public virtual TResult VisitUsingDirective(UsingDirectiveSyntax node) => this.DefaultVisit(node); public virtual TResult VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) => this.DefaultVisit(node); public virtual TResult VisitAttributeList(AttributeListSyntax node) => this.DefaultVisit(node); public virtual TResult VisitAttributeTargetSpecifier(AttributeTargetSpecifierSyntax node) => this.DefaultVisit(node); public virtual TResult VisitAttribute(AttributeSyntax node) => this.DefaultVisit(node); @@ -33998,6 +34265,7 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitExternAliasDirective(ExternAliasDirectiveSyntax node) => this.DefaultVisit(node); public virtual void VisitUsingDirective(UsingDirectiveSyntax node) => this.DefaultVisit(node); public virtual void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) => this.DefaultVisit(node); + public virtual void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) => this.DefaultVisit(node); public virtual void VisitAttributeList(AttributeListSyntax node) => this.DefaultVisit(node); public virtual void VisitAttributeTargetSpecifier(AttributeTargetSpecifierSyntax node) => this.DefaultVisit(node); public virtual void VisitAttribute(AttributeSyntax node) => this.DefaultVisit(node); @@ -34530,6 +34798,9 @@ public override CSharpSyntaxNode VisitUsingDirective(UsingDirectiveSyntax node) public override CSharpSyntaxNode VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.NamespaceKeyword), (NameSyntax)Visit(node.Name), (SyntaxToken)Visit(node.OpenBraceToken), VisitList(node.Externs), VisitList(node.Usings), VisitList(node.Members), (SyntaxToken)Visit(node.CloseBraceToken), (SyntaxToken)Visit(node.SemicolonToken)); + public override CSharpSyntaxNode VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.NamespaceKeyword), (NameSyntax)Visit(node.Name), (SyntaxToken)Visit(node.SemicolonToken), VisitList(node.Externs), VisitList(node.Usings), VisitList(node.Members)); + public override CSharpSyntaxNode VisitAttributeList(AttributeListSyntax node) => node.Update((SyntaxToken)Visit(node.OpenBracketToken), (AttributeTargetSpecifierSyntax)Visit(node.Target), VisitList(node.Attributes), (SyntaxToken)Visit(node.CloseBracketToken)); @@ -37920,6 +38191,19 @@ public NamespaceDeclarationSyntax NamespaceDeclaration(Microsoft.CodeAnalysis.Sy return new NamespaceDeclarationSyntax(SyntaxKind.NamespaceDeclaration, attributeLists.Node, modifiers.Node, namespaceKeyword, name, openBraceToken, externs.Node, usings.Node, members.Node, closeBraceToken, semicolonToken, this.context); } + public FileScopedNamespaceDeclarationSyntax FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList externs, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList usings, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList members) + { +#if DEBUG + if (namespaceKeyword == null) throw new ArgumentNullException(nameof(namespaceKeyword)); + if (namespaceKeyword.Kind != SyntaxKind.NamespaceKeyword) throw new ArgumentException(nameof(namespaceKeyword)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (semicolonToken == null) throw new ArgumentNullException(nameof(semicolonToken)); + if (semicolonToken.Kind != SyntaxKind.SemicolonToken) throw new ArgumentException(nameof(semicolonToken)); +#endif + + return new FileScopedNamespaceDeclarationSyntax(SyntaxKind.FileScopedNamespaceDeclaration, attributeLists.Node, modifiers.Node, namespaceKeyword, name, semicolonToken, externs.Node, usings.Node, members.Node, this.context); + } + public AttributeListSyntax AttributeList(SyntaxToken openBracketToken, AttributeTargetSpecifierSyntax? target, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList attributes, SyntaxToken closeBracketToken) { #if DEBUG @@ -42880,6 +43164,19 @@ public static NamespaceDeclarationSyntax NamespaceDeclaration(Microsoft.CodeAnal return new NamespaceDeclarationSyntax(SyntaxKind.NamespaceDeclaration, attributeLists.Node, modifiers.Node, namespaceKeyword, name, openBraceToken, externs.Node, usings.Node, members.Node, closeBraceToken, semicolonToken); } + public static FileScopedNamespaceDeclarationSyntax FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList attributeLists, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList externs, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList usings, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList members) + { +#if DEBUG + if (namespaceKeyword == null) throw new ArgumentNullException(nameof(namespaceKeyword)); + if (namespaceKeyword.Kind != SyntaxKind.NamespaceKeyword) throw new ArgumentException(nameof(namespaceKeyword)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (semicolonToken == null) throw new ArgumentNullException(nameof(semicolonToken)); + if (semicolonToken.Kind != SyntaxKind.SemicolonToken) throw new ArgumentException(nameof(semicolonToken)); +#endif + + return new FileScopedNamespaceDeclarationSyntax(SyntaxKind.FileScopedNamespaceDeclaration, attributeLists.Node, modifiers.Node, namespaceKeyword, name, semicolonToken, externs.Node, usings.Node, members.Node); + } + public static AttributeListSyntax AttributeList(SyntaxToken openBracketToken, AttributeTargetSpecifierSyntax? target, Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList attributes, SyntaxToken closeBracketToken) { #if DEBUG @@ -44866,6 +45163,7 @@ internal static IEnumerable GetNodeTypes() typeof(ExternAliasDirectiveSyntax), typeof(UsingDirectiveSyntax), typeof(NamespaceDeclarationSyntax), + typeof(FileScopedNamespaceDeclarationSyntax), typeof(AttributeListSyntax), typeof(AttributeTargetSpecifierSyntax), typeof(AttributeSyntax), diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs index 055f9fbf7187b..f5000e8684bfd 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs @@ -456,6 +456,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a NamespaceDeclarationSyntax node. public virtual TResult? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a FileScopedNamespaceDeclarationSyntax node. + public virtual TResult? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a AttributeListSyntax node. public virtual TResult? VisitAttributeList(AttributeListSyntax node) => this.DefaultVisit(node); @@ -1161,6 +1164,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a NamespaceDeclarationSyntax node. public virtual void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a FileScopedNamespaceDeclarationSyntax node. + public virtual void VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a AttributeListSyntax node. public virtual void VisitAttributeList(AttributeListSyntax node) => this.DefaultVisit(node); @@ -1866,6 +1872,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor public override SyntaxNode? VisitNamespaceDeclaration(NamespaceDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.NamespaceKeyword), (NameSyntax?)Visit(node.Name) ?? throw new ArgumentNullException("name"), VisitToken(node.OpenBraceToken), VisitList(node.Externs), VisitList(node.Usings), VisitList(node.Members), VisitToken(node.CloseBraceToken), VisitToken(node.SemicolonToken)); + public override SyntaxNode? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.NamespaceKeyword), (NameSyntax?)Visit(node.Name) ?? throw new ArgumentNullException("name"), VisitToken(node.SemicolonToken), VisitList(node.Externs), VisitList(node.Usings), VisitList(node.Members)); + public override SyntaxNode? VisitAttributeList(AttributeListSyntax node) => node.Update(VisitToken(node.OpenBracketToken), (AttributeTargetSpecifierSyntax?)Visit(node.Target), VisitList(node.Attributes), VisitToken(node.CloseBracketToken)); @@ -4638,6 +4647,23 @@ public static NamespaceDeclarationSyntax NamespaceDeclaration(SyntaxList SyntaxFactory.NamespaceDeclaration(default, default(SyntaxTokenList), SyntaxFactory.Token(SyntaxKind.NamespaceKeyword), name, SyntaxFactory.Token(SyntaxKind.OpenBraceToken), default, default, default, SyntaxFactory.Token(SyntaxKind.CloseBraceToken), default); + /// Creates a new FileScopedNamespaceDeclarationSyntax instance. + public static FileScopedNamespaceDeclarationSyntax FileScopedNamespaceDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, SyntaxList externs, SyntaxList usings, SyntaxList members) + { + if (namespaceKeyword.Kind() != SyntaxKind.NamespaceKeyword) throw new ArgumentException(nameof(namespaceKeyword)); + if (name == null) throw new ArgumentNullException(nameof(name)); + if (semicolonToken.Kind() != SyntaxKind.SemicolonToken) throw new ArgumentException(nameof(semicolonToken)); + return (FileScopedNamespaceDeclarationSyntax)Syntax.InternalSyntax.SyntaxFactory.FileScopedNamespaceDeclaration(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.SyntaxToken)namespaceKeyword.Node!, (Syntax.InternalSyntax.NameSyntax)name.Green, (Syntax.InternalSyntax.SyntaxToken)semicolonToken.Node!, externs.Node.ToGreenList(), usings.Node.ToGreenList(), members.Node.ToGreenList()).CreateRed(); + } + + /// Creates a new FileScopedNamespaceDeclarationSyntax instance. + public static FileScopedNamespaceDeclarationSyntax FileScopedNamespaceDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, NameSyntax name, SyntaxList externs, SyntaxList usings, SyntaxList members) + => SyntaxFactory.FileScopedNamespaceDeclaration(attributeLists, modifiers, SyntaxFactory.Token(SyntaxKind.NamespaceKeyword), name, SyntaxFactory.Token(SyntaxKind.SemicolonToken), externs, usings, members); + + /// Creates a new FileScopedNamespaceDeclarationSyntax instance. + public static FileScopedNamespaceDeclarationSyntax FileScopedNamespaceDeclaration(NameSyntax name) + => SyntaxFactory.FileScopedNamespaceDeclaration(default, default(SyntaxTokenList), SyntaxFactory.Token(SyntaxKind.NamespaceKeyword), name, SyntaxFactory.Token(SyntaxKind.SemicolonToken), default, default, default); + /// Creates a new AttributeListSyntax instance. public static AttributeListSyntax AttributeList(SyntaxToken openBracketToken, AttributeTargetSpecifierSyntax? target, SeparatedSyntaxList attributes, SyntaxToken closeBracketToken) { diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs index 042ac762357c0..556e2b1b7e098 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs @@ -9128,13 +9128,57 @@ internal MemberDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNo internal abstract MemberDeclarationSyntax AddModifiersCore(params SyntaxToken[] items); } + public abstract partial class BaseNamespaceDeclarationSyntax : MemberDeclarationSyntax + { + internal BaseNamespaceDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public abstract SyntaxToken NamespaceKeyword { get; } + public BaseNamespaceDeclarationSyntax WithNamespaceKeyword(SyntaxToken namespaceKeyword) => WithNamespaceKeywordCore(namespaceKeyword); + internal abstract BaseNamespaceDeclarationSyntax WithNamespaceKeywordCore(SyntaxToken namespaceKeyword); + + public abstract NameSyntax Name { get; } + public BaseNamespaceDeclarationSyntax WithName(NameSyntax name) => WithNameCore(name); + internal abstract BaseNamespaceDeclarationSyntax WithNameCore(NameSyntax name); + + public abstract SyntaxList Externs { get; } + public BaseNamespaceDeclarationSyntax WithExterns(SyntaxList externs) => WithExternsCore(externs); + internal abstract BaseNamespaceDeclarationSyntax WithExternsCore(SyntaxList externs); + + public BaseNamespaceDeclarationSyntax AddExterns(params ExternAliasDirectiveSyntax[] items) => AddExternsCore(items); + internal abstract BaseNamespaceDeclarationSyntax AddExternsCore(params ExternAliasDirectiveSyntax[] items); + + public abstract SyntaxList Usings { get; } + public BaseNamespaceDeclarationSyntax WithUsings(SyntaxList usings) => WithUsingsCore(usings); + internal abstract BaseNamespaceDeclarationSyntax WithUsingsCore(SyntaxList usings); + + public BaseNamespaceDeclarationSyntax AddUsings(params UsingDirectiveSyntax[] items) => AddUsingsCore(items); + internal abstract BaseNamespaceDeclarationSyntax AddUsingsCore(params UsingDirectiveSyntax[] items); + + public abstract SyntaxList Members { get; } + public BaseNamespaceDeclarationSyntax WithMembers(SyntaxList members) => WithMembersCore(members); + internal abstract BaseNamespaceDeclarationSyntax WithMembersCore(SyntaxList members); + + public BaseNamespaceDeclarationSyntax AddMembers(params MemberDeclarationSyntax[] items) => AddMembersCore(items); + internal abstract BaseNamespaceDeclarationSyntax AddMembersCore(params MemberDeclarationSyntax[] items); + + public new BaseNamespaceDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => (BaseNamespaceDeclarationSyntax)WithAttributeListsCore(attributeLists); + public new BaseNamespaceDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => (BaseNamespaceDeclarationSyntax)WithModifiersCore(modifiers); + + public new BaseNamespaceDeclarationSyntax AddAttributeLists(params AttributeListSyntax[] items) => (BaseNamespaceDeclarationSyntax)AddAttributeListsCore(items); + + public new BaseNamespaceDeclarationSyntax AddModifiers(params SyntaxToken[] items) => (BaseNamespaceDeclarationSyntax)AddModifiersCore(items); + } + /// /// This node is associated with the following syntax kinds: /// /// /// /// - public sealed partial class NamespaceDeclarationSyntax : MemberDeclarationSyntax + public sealed partial class NamespaceDeclarationSyntax : BaseNamespaceDeclarationSyntax { private SyntaxNode? attributeLists; private NameSyntax? name; @@ -9158,17 +9202,17 @@ public override SyntaxTokenList Modifiers } } - public SyntaxToken NamespaceKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.NamespaceDeclarationSyntax)this.Green).namespaceKeyword, GetChildPosition(2), GetChildIndex(2)); + public override SyntaxToken NamespaceKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.NamespaceDeclarationSyntax)this.Green).namespaceKeyword, GetChildPosition(2), GetChildIndex(2)); - public NameSyntax Name => GetRed(ref this.name, 3)!; + public override NameSyntax Name => GetRed(ref this.name, 3)!; public SyntaxToken OpenBraceToken => new SyntaxToken(this, ((Syntax.InternalSyntax.NamespaceDeclarationSyntax)this.Green).openBraceToken, GetChildPosition(4), GetChildIndex(4)); - public SyntaxList Externs => new SyntaxList(GetRed(ref this.externs, 5)); + public override SyntaxList Externs => new SyntaxList(GetRed(ref this.externs, 5)); - public SyntaxList Usings => new SyntaxList(GetRed(ref this.usings, 6)); + public override SyntaxList Usings => new SyntaxList(GetRed(ref this.usings, 6)); - public SyntaxList Members => new SyntaxList(GetRed(ref this.members, 7)); + public override SyntaxList Members => new SyntaxList(GetRed(ref this.members, 7)); public SyntaxToken CloseBraceToken => new SyntaxToken(this, ((Syntax.InternalSyntax.NamespaceDeclarationSyntax)this.Green).closeBraceToken, GetChildPosition(8), GetChildIndex(8)); @@ -9223,12 +9267,17 @@ public NamespaceDeclarationSyntax Update(SyntaxList attribu public new NamespaceDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); internal override MemberDeclarationSyntax WithModifiersCore(SyntaxTokenList modifiers) => WithModifiers(modifiers); public new NamespaceDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); - public NamespaceDeclarationSyntax WithNamespaceKeyword(SyntaxToken namespaceKeyword) => Update(this.AttributeLists, this.Modifiers, namespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); - public NamespaceDeclarationSyntax WithName(NameSyntax name) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseNamespaceDeclarationSyntax WithNamespaceKeywordCore(SyntaxToken namespaceKeyword) => WithNamespaceKeyword(namespaceKeyword); + public new NamespaceDeclarationSyntax WithNamespaceKeyword(SyntaxToken namespaceKeyword) => Update(this.AttributeLists, this.Modifiers, namespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseNamespaceDeclarationSyntax WithNameCore(NameSyntax name) => WithName(name); + public new NamespaceDeclarationSyntax WithName(NameSyntax name) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); public NamespaceDeclarationSyntax WithOpenBraceToken(SyntaxToken openBraceToken) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, openBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); - public NamespaceDeclarationSyntax WithExterns(SyntaxList externs) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); - public NamespaceDeclarationSyntax WithUsings(SyntaxList usings) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, usings, this.Members, this.CloseBraceToken, this.SemicolonToken); - public NamespaceDeclarationSyntax WithMembers(SyntaxList members) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseNamespaceDeclarationSyntax WithExternsCore(SyntaxList externs) => WithExterns(externs); + public new NamespaceDeclarationSyntax WithExterns(SyntaxList externs) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, externs, this.Usings, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseNamespaceDeclarationSyntax WithUsingsCore(SyntaxList usings) => WithUsings(usings); + public new NamespaceDeclarationSyntax WithUsings(SyntaxList usings) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, usings, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseNamespaceDeclarationSyntax WithMembersCore(SyntaxList members) => WithMembers(members); + public new NamespaceDeclarationSyntax WithMembers(SyntaxList members) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, members, this.CloseBraceToken, this.SemicolonToken); public NamespaceDeclarationSyntax WithCloseBraceToken(SyntaxToken closeBraceToken) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, closeBraceToken, this.SemicolonToken); public NamespaceDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.OpenBraceToken, this.Externs, this.Usings, this.Members, this.CloseBraceToken, semicolonToken); @@ -9236,9 +9285,119 @@ public NamespaceDeclarationSyntax Update(SyntaxList attribu public new NamespaceDeclarationSyntax AddAttributeLists(params AttributeListSyntax[] items) => WithAttributeLists(this.AttributeLists.AddRange(items)); internal override MemberDeclarationSyntax AddModifiersCore(params SyntaxToken[] items) => AddModifiers(items); public new NamespaceDeclarationSyntax AddModifiers(params SyntaxToken[] items) => WithModifiers(this.Modifiers.AddRange(items)); - public NamespaceDeclarationSyntax AddExterns(params ExternAliasDirectiveSyntax[] items) => WithExterns(this.Externs.AddRange(items)); - public NamespaceDeclarationSyntax AddUsings(params UsingDirectiveSyntax[] items) => WithUsings(this.Usings.AddRange(items)); - public NamespaceDeclarationSyntax AddMembers(params MemberDeclarationSyntax[] items) => WithMembers(this.Members.AddRange(items)); + internal override BaseNamespaceDeclarationSyntax AddExternsCore(params ExternAliasDirectiveSyntax[] items) => AddExterns(items); + public new NamespaceDeclarationSyntax AddExterns(params ExternAliasDirectiveSyntax[] items) => WithExterns(this.Externs.AddRange(items)); + internal override BaseNamespaceDeclarationSyntax AddUsingsCore(params UsingDirectiveSyntax[] items) => AddUsings(items); + public new NamespaceDeclarationSyntax AddUsings(params UsingDirectiveSyntax[] items) => WithUsings(this.Usings.AddRange(items)); + internal override BaseNamespaceDeclarationSyntax AddMembersCore(params MemberDeclarationSyntax[] items) => AddMembers(items); + public new NamespaceDeclarationSyntax AddMembers(params MemberDeclarationSyntax[] items) => WithMembers(this.Members.AddRange(items)); + } + + /// + /// This node is associated with the following syntax kinds: + /// + /// + /// + /// + public sealed partial class FileScopedNamespaceDeclarationSyntax : BaseNamespaceDeclarationSyntax + { + private SyntaxNode? attributeLists; + private NameSyntax? name; + private SyntaxNode? externs; + private SyntaxNode? usings; + private SyntaxNode? members; + + internal FileScopedNamespaceDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public override SyntaxList AttributeLists => new SyntaxList(GetRed(ref this.attributeLists, 0)); + + public override SyntaxTokenList Modifiers + { + get + { + var slot = this.Green.GetSlot(1); + return slot != null ? new SyntaxTokenList(this, slot, GetChildPosition(1), GetChildIndex(1)) : default; + } + } + + public override SyntaxToken NamespaceKeyword => new SyntaxToken(this, ((Syntax.InternalSyntax.FileScopedNamespaceDeclarationSyntax)this.Green).namespaceKeyword, GetChildPosition(2), GetChildIndex(2)); + + public override NameSyntax Name => GetRed(ref this.name, 3)!; + + public SyntaxToken SemicolonToken => new SyntaxToken(this, ((Syntax.InternalSyntax.FileScopedNamespaceDeclarationSyntax)this.Green).semicolonToken, GetChildPosition(4), GetChildIndex(4)); + + public override SyntaxList Externs => new SyntaxList(GetRed(ref this.externs, 5)); + + public override SyntaxList Usings => new SyntaxList(GetRed(ref this.usings, 6)); + + public override SyntaxList Members => new SyntaxList(GetRed(ref this.members, 7)); + + internal override SyntaxNode? GetNodeSlot(int index) + => index switch + { + 0 => GetRedAtZero(ref this.attributeLists)!, + 3 => GetRed(ref this.name, 3)!, + 5 => GetRed(ref this.externs, 5)!, + 6 => GetRed(ref this.usings, 6)!, + 7 => GetRed(ref this.members, 7)!, + _ => null, + }; + + internal override SyntaxNode? GetCachedSlot(int index) + => index switch + { + 0 => this.attributeLists, + 3 => this.name, + 5 => this.externs, + 6 => this.usings, + 7 => this.members, + _ => null, + }; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitFileScopedNamespaceDeclaration(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitFileScopedNamespaceDeclaration(this); + + public FileScopedNamespaceDeclarationSyntax Update(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken namespaceKeyword, NameSyntax name, SyntaxToken semicolonToken, SyntaxList externs, SyntaxList usings, SyntaxList members) + { + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || namespaceKeyword != this.NamespaceKeyword || name != this.Name || semicolonToken != this.SemicolonToken || externs != this.Externs || usings != this.Usings || members != this.Members) + { + var newNode = SyntaxFactory.FileScopedNamespaceDeclaration(attributeLists, modifiers, namespaceKeyword, name, semicolonToken, externs, usings, members); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + internal override MemberDeclarationSyntax WithAttributeListsCore(SyntaxList attributeLists) => WithAttributeLists(attributeLists); + public new FileScopedNamespaceDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.SemicolonToken, this.Externs, this.Usings, this.Members); + internal override MemberDeclarationSyntax WithModifiersCore(SyntaxTokenList modifiers) => WithModifiers(modifiers); + public new FileScopedNamespaceDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.NamespaceKeyword, this.Name, this.SemicolonToken, this.Externs, this.Usings, this.Members); + internal override BaseNamespaceDeclarationSyntax WithNamespaceKeywordCore(SyntaxToken namespaceKeyword) => WithNamespaceKeyword(namespaceKeyword); + public new FileScopedNamespaceDeclarationSyntax WithNamespaceKeyword(SyntaxToken namespaceKeyword) => Update(this.AttributeLists, this.Modifiers, namespaceKeyword, this.Name, this.SemicolonToken, this.Externs, this.Usings, this.Members); + internal override BaseNamespaceDeclarationSyntax WithNameCore(NameSyntax name) => WithName(name); + public new FileScopedNamespaceDeclarationSyntax WithName(NameSyntax name) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, name, this.SemicolonToken, this.Externs, this.Usings, this.Members); + public FileScopedNamespaceDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, semicolonToken, this.Externs, this.Usings, this.Members); + internal override BaseNamespaceDeclarationSyntax WithExternsCore(SyntaxList externs) => WithExterns(externs); + public new FileScopedNamespaceDeclarationSyntax WithExterns(SyntaxList externs) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.SemicolonToken, externs, this.Usings, this.Members); + internal override BaseNamespaceDeclarationSyntax WithUsingsCore(SyntaxList usings) => WithUsings(usings); + public new FileScopedNamespaceDeclarationSyntax WithUsings(SyntaxList usings) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.SemicolonToken, this.Externs, usings, this.Members); + internal override BaseNamespaceDeclarationSyntax WithMembersCore(SyntaxList members) => WithMembers(members); + public new FileScopedNamespaceDeclarationSyntax WithMembers(SyntaxList members) => Update(this.AttributeLists, this.Modifiers, this.NamespaceKeyword, this.Name, this.SemicolonToken, this.Externs, this.Usings, members); + + internal override MemberDeclarationSyntax AddAttributeListsCore(params AttributeListSyntax[] items) => AddAttributeLists(items); + public new FileScopedNamespaceDeclarationSyntax AddAttributeLists(params AttributeListSyntax[] items) => WithAttributeLists(this.AttributeLists.AddRange(items)); + internal override MemberDeclarationSyntax AddModifiersCore(params SyntaxToken[] items) => AddModifiers(items); + public new FileScopedNamespaceDeclarationSyntax AddModifiers(params SyntaxToken[] items) => WithModifiers(this.Modifiers.AddRange(items)); + internal override BaseNamespaceDeclarationSyntax AddExternsCore(params ExternAliasDirectiveSyntax[] items) => AddExterns(items); + public new FileScopedNamespaceDeclarationSyntax AddExterns(params ExternAliasDirectiveSyntax[] items) => WithExterns(this.Externs.AddRange(items)); + internal override BaseNamespaceDeclarationSyntax AddUsingsCore(params UsingDirectiveSyntax[] items) => AddUsings(items); + public new FileScopedNamespaceDeclarationSyntax AddUsings(params UsingDirectiveSyntax[] items) => WithUsings(this.Usings.AddRange(items)); + internal override BaseNamespaceDeclarationSyntax AddMembersCore(params MemberDeclarationSyntax[] items) => AddMembers(items); + public new FileScopedNamespaceDeclarationSyntax AddMembers(params MemberDeclarationSyntax[] items) => WithMembers(this.Members.AddRange(items)); } /// Class representing one or more attributes applied to a language construct. diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index cc5c864c1dff7..025c4df32f81b 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -269,6 +269,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_ReturnTypeIsStaticClass: case ErrorCode.WRN_UnreadRecordParameter: case ErrorCode.WRN_DoNotCompareFunctionPointers: + case ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter: return true; default: return false; diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs index 5ee14ef41bb43..088bf7dc188a1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodBuilderMemberCollection.cs @@ -345,10 +345,20 @@ private static NamedTypeSymbol ValidateBuilderType(SyntheticBoundNodeFactory F, !builderType.IsVoidType() && (forMethodLevelBuilder || builderType.DeclaredAccessibility == desiredAccessibility)) { - bool isArityOk = isGeneric - ? builderType.IsUnboundGenericType && builderType.ContainingType?.IsGenericType != true && builderType.Arity == 1 - : !builderType.IsGenericType; - if (isArityOk) + if (isGeneric) + { + if (builderType.IsUnboundGenericType && builderType.ContainingType?.IsGenericType != true && builderType.Arity == 1) + { + return builderType; + } + else + { + F.Diagnostics.Add(ErrorCode.ERR_WrongArityAsyncReturn, F.Syntax.Location, builderType); + return null; + } + } + + if (!builderType.IsGenericType) { return builderType; } diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index d018884a9ba9d..28c3aee2fa628 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -738,6 +738,13 @@ public override BoundNode VisitConversion(BoundConversion node) } break; + case ConversionKind.InterpolatedStringHandler: + if (_inExpressionLambda) + { + Error(ErrorCode.ERR_ExpressionTreeContainsInterpolatedStringHandlerConversion, node); + } + break; + default: if (_inExpressionLambda && node.Conversion.Method is MethodSymbol method && method.IsAbstract && method.IsStatic) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 9d628989bfcb2..1421c5338784e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.RuntimeMembers; using Roslyn.Utilities; @@ -391,6 +390,12 @@ public override BoundNode VisitObjectOrCollectionValuePlaceholder(BoundObjectOrC return PlaceholderReplacement(node); } + public override BoundNode VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) + => PlaceholderReplacement(node); + + public override BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) + => PlaceholderReplacement(node); + /// /// Returns substitution currently used by the rewriter for a placeholder node. /// Each occurrence of the placeholder node is replaced with the node returned. @@ -789,8 +794,8 @@ private static bool ShouldOptimizeOutInitializer(BoundStatement initializer) return rhs.IsDefaultValue(); } - // There are two situations in which the language permits passing rvalues by reference. - // (technically there are 4, but we can ignore COM and dynamic here, since that results in byval semantics regardless of the parameter ref kind) + // There are three situations in which the language permits passing rvalues by reference. + // (technically there are 5, but we can ignore COM and dynamic here, since that results in byval semantics regardless of the parameter ref kind) // // #1: Receiver of a struct/generic method call. // @@ -851,6 +856,16 @@ private static bool ShouldOptimizeOutInitializer(BoundStatement initializer) // Console.WriteLine(y); // } // + // #3: Ordinary byval interpolated string expression passed to a "ref" interpolated string handler value type. + // + // Interpolated string expressions passed to a builder type are lowered into a handler form. When the handler type + // is a value type (struct, or type parameter constrained to struct (though the latter will fail to bind today because + // there's no constructor)), the final handler instance type is passed by reference if the parameter is by reference. + // + // Example: + // M($""); // Language lowers this to a sequence of creating CustomHandler, appending all values, and evaluting to the builder + // static void M(ref CustomHandler c) { } + // // NB: The readonliness is not considered here. // We only care about possible introduction of aliasing. I.E. RValue->LValue change. // Even if we start with a readonly variable, it cannot be lowered into a writeable one, @@ -881,6 +896,18 @@ internal static bool CanBePassedByReference(BoundExpression expr) // the assumption should be confirmed or changed when https://github.com/dotnet/roslyn/issues/24160 is fixed return true; + case BoundKind.InterpolatedStringArgumentPlaceholder: + // An argument placeholder is always a reference to some type of temp local, + // either representing a user-typed expression that went through this path + // itself when it was originally visited, or the trailing out parameter that + // is passed by out. + return true; + + case BoundKind.InterpolatedStringHandlerPlaceholder: + // A handler placeholder is the receiver of the interpolated string AppendLiteral + // or AppendFormatted calls, and should never be defensively copied. + return true; + case BoundKind.EventAccess: var eventAccess = (BoundEventAccess)expr; if (eventAccess.IsUsableAsField) @@ -931,6 +958,10 @@ internal static bool CanBePassedByReference(BoundExpression expr) _ => throw ExceptionUtilities.UnexpectedValue(patternIndexer.PatternSymbol) }; return refKind != RefKind.None; + + case BoundKind.Conversion: + var conversion = ((BoundConversion)expr); + return expr is BoundConversion { Conversion: { IsInterpolatedStringHandler: true }, Type: { IsValueType: true } }; } return false; @@ -1007,6 +1038,18 @@ public static void Validate(BoundNode node) return null; } + public override BoundNode? VisitInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder node) + { + Fail(node); + return null; + } + + public override BoundNode? VisitInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder node) + { + Fail(node); + return null; + } + private void Fail(BoundNode node) { Debug.Assert(false, $"Bound nodes of kind {node.Kind} should not survive past local rewriting"); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AnonymousObjectCreation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AnonymousObjectCreation.cs index ed4142a16fa53..5ddc1010d5860 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AnonymousObjectCreation.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AnonymousObjectCreation.cs @@ -14,6 +14,9 @@ internal sealed partial class LocalRewriter { public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node) { + // We should never encounter an interpolated string handler conversion that was implicitly inferred, because + // there are no target types for an anonymous object creation. + AssertNoImplicitInterpolatedStringHandlerConversions(node.Arguments); // Rewrite the arguments. var rewrittenArguments = VisitList(node.Arguments); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index efa75a094f203..3de6c9485b9b7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -65,6 +64,8 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo // dyn[args] = expr var indexerAccess = (BoundDynamicIndexerAccess)left; var loweredReceiver = VisitExpression(indexerAccess.Receiver); + // Dynamic can't have created handler conversions because we don't know target types. + AssertNoImplicitInterpolatedStringHandlerConversions(indexerAccess.Arguments); var loweredArguments = VisitList(indexerAccess.Arguments); return MakeDynamicSetIndex( indexerAccess, @@ -203,14 +204,14 @@ private BoundExpression MakeStaticAssignmentOperator( Debug.Assert(!isRef); BoundIndexerAccess indexerAccess = (BoundIndexerAccess)rewrittenLeft; BoundExpression? rewrittenReceiver = indexerAccess.ReceiverOpt; - ImmutableArray rewrittenArguments = indexerAccess.Arguments; + ImmutableArray arguments = indexerAccess.Arguments; PropertySymbol indexer = indexerAccess.Indexer; Debug.Assert(indexer.IsIndexer || indexer.IsIndexedProperty); return MakePropertyAssignment( syntax, rewrittenReceiver, indexer, - rewrittenArguments, + arguments, indexerAccess.ArgumentRefKindsOpt, indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, @@ -247,7 +248,8 @@ private BoundExpression MakeStaticAssignmentOperator( } case BoundKind.Sequence: - // An Index or Range pattern-based indexer produces a sequence with a nested + // An Index or Range pattern-based indexer, or an interpolated string handler conversion + // that uses an indexer argument, produces a sequence with a nested // BoundIndexerAccess. We need to lower the final expression and produce an // update sequence var sequence = (BoundSequence)rewrittenLeft; @@ -285,7 +287,7 @@ private BoundExpression MakePropertyAssignment( SyntaxNode syntax, BoundExpression? rewrittenReceiver, PropertySymbol property, - ImmutableArray rewrittenArguments, + ImmutableArray arguments, ImmutableArray argumentRefKindsOpt, bool expanded, ImmutableArray argsToParamsOpt, @@ -309,20 +311,27 @@ private BoundExpression MakePropertyAssignment( rewrittenRight); } - // We have already lowered each argument, but we may need some additional rewriting for the arguments, - // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. - ImmutableArray argTemps; - rewrittenArguments = MakeArguments( + arguments = VisitArguments( + arguments, + property, + argsToParamsOpt, + argumentRefKindsOpt, + ref rewrittenReceiver, + out ArrayBuilder? argTempsBuilder); + + arguments = MakeArguments( syntax, - rewrittenArguments, + arguments, property, expanded, argsToParamsOpt, ref argumentRefKindsOpt, - out argTemps, + ref argTempsBuilder, invokedAsExtensionMethod: false, enableCallerInfo: ThreeState.True); + var argTemps = argTempsBuilder.ToImmutableAndFree(); + if (used) { // Save expression value to a temporary before calling the @@ -345,7 +354,7 @@ private BoundExpression MakePropertyAssignment( syntax, rewrittenReceiver, setMethod, - AppendToPossibleNull(rewrittenArguments, rhsAssignment)); + AppendToPossibleNull(arguments, rhsAssignment)); return new BoundSequence( syntax, @@ -360,7 +369,7 @@ private BoundExpression MakePropertyAssignment( syntax, rewrittenReceiver, setMethod, - AppendToPossibleNull(rewrittenArguments, rewrittenRight)); + AppendToPossibleNull(arguments, rewrittenRight)); if (argTemps.IsDefaultOrEmpty) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index f3e0e9c240aba..57df57698e359 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Operations; using Roslyn.Utilities; @@ -24,6 +22,8 @@ public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node) public BoundExpression VisitDynamicInvocation(BoundDynamicInvocation node, bool resultDiscarded) { + // Dynamic can't have created handler conversions because we don't know target types. + AssertNoImplicitInterpolatedStringHandlerConversions(node.Arguments); var loweredArguments = VisitList(node.Arguments); bool hasImplicitReceiver; @@ -138,53 +138,56 @@ public override BoundNode VisitCall(BoundCall node) // Rewrite the receiver BoundExpression? rewrittenReceiver = VisitExpression(node.ReceiverOpt); + var argRefKindsOpt = node.ArgumentRefKindsOpt; - // Rewrite the arguments. - // NOTE: We may need additional argument rewriting such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. - // NOTE: This is done later by MakeArguments, for now we just lower each argument. - var rewrittenArguments = VisitList(node.Arguments); + var rewrittenArguments = VisitArguments( + node.Arguments, + node.Method, + node.ArgsToParamsOpt, + argRefKindsOpt, + ref rewrittenReceiver, + out ArrayBuilder? temps); - return MakeCall( + return MakeArgumentsAndCall( syntax: node.Syntax, rewrittenReceiver: rewrittenReceiver, method: node.Method, - rewrittenArguments: rewrittenArguments, - argumentRefKindsOpt: node.ArgumentRefKindsOpt, + arguments: rewrittenArguments, + argumentRefKindsOpt: argRefKindsOpt, expanded: node.Expanded, invokedAsExtensionMethod: node.InvokedAsExtensionMethod, argsToParamsOpt: node.ArgsToParamsOpt, resultKind: node.ResultKind, type: node.Type, + temps, nodeOpt: node); } - private BoundExpression MakeCall( + private BoundExpression MakeArgumentsAndCall( SyntaxNode syntax, BoundExpression? rewrittenReceiver, MethodSymbol method, - ImmutableArray rewrittenArguments, + ImmutableArray arguments, ImmutableArray argumentRefKindsOpt, bool expanded, bool invokedAsExtensionMethod, ImmutableArray argsToParamsOpt, LookupResultKind resultKind, TypeSymbol type, + ArrayBuilder? temps, BoundCall? nodeOpt = null) { - // We have already lowered each argument, but we may need some additional rewriting for the arguments, - // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. - ImmutableArray temps; - rewrittenArguments = MakeArguments( + arguments = MakeArguments( syntax, - rewrittenArguments, + arguments, method, expanded, argsToParamsOpt, ref argumentRefKindsOpt, - out temps, + ref temps, invokedAsExtensionMethod); - return MakeCall(nodeOpt, syntax, rewrittenReceiver, method, rewrittenArguments, argumentRefKindsOpt, invokedAsExtensionMethod, resultKind, type, temps); + return MakeCall(nodeOpt, syntax, rewrittenReceiver, method, arguments, argumentRefKindsOpt, invokedAsExtensionMethod, resultKind, type, temps.ToImmutableAndFree()); } private BoundExpression MakeCall( @@ -197,7 +200,7 @@ private BoundExpression MakeCall( bool invokedAsExtensionMethod, LookupResultKind resultKind, TypeSymbol type, - ImmutableArray temps = default(ImmutableArray)) + ImmutableArray temps) { BoundExpression rewrittenBoundCall; @@ -281,7 +284,8 @@ private BoundExpression MakeCall(SyntaxNode syntax, BoundExpression? rewrittenRe argumentRefKinds: default(ImmutableArray), invokedAsExtensionMethod: false, resultKind: LookupResultKind.Viable, - type: type); + type: type, + temps: default); } private static bool IsSafeForReordering(BoundExpression expression, RefKind kind) @@ -367,6 +371,165 @@ private static bool IsSafeForReordering(BoundExpression expression, RefKind kind } } + /// + /// Visits all arguments of a method, doing any necessary rewriting for interpolated string handler conversions that + /// might be present in the arguments and creating temps for any discard parameters. + /// + private ImmutableArray VisitArguments( + ImmutableArray arguments, + Symbol methodOrIndexer, + ImmutableArray argsToParamsOpt, + ImmutableArray argumentRefKindsOpt, + [NotNullIfNotNull("rewrittenReceiver")] ref BoundExpression? rewrittenReceiver, + out ArrayBuilder? temps) + { + Debug.Assert(argumentRefKindsOpt.IsDefault || argumentRefKindsOpt.Length == arguments.Length); + var requiresInstanceReceiver = methodOrIndexer.RequiresInstanceReceiver() && methodOrIndexer is not MethodSymbol { MethodKind: MethodKind.Constructor } and not FunctionPointerMethodSymbol; + Debug.Assert(!requiresInstanceReceiver || rewrittenReceiver != null || _inExpressionLambda); + temps = null; + var argumentsAssignedToTemp = BitVector.Null; + + if (arguments.IsEmpty) + { + return arguments; + } + + var visitedArgumentsBuilder = ArrayBuilder.GetInstance(arguments.Length); + var parameters = methodOrIndexer.GetParameters(); + var receiverAssignedToTemp = false; + + for (int i = 0; i < arguments.Length; i++) + { + var argument = arguments[i]; + if (argument is BoundDiscardExpression discard) + { + ensureTempTrackingSetup(ref temps, ref argumentsAssignedToTemp); + visitedArgumentsBuilder.Add(_factory.MakeTempForDiscard(discard, temps)); + argumentsAssignedToTemp[i] = true; + continue; + } + + ImmutableArray argumentPlaceholders = addInterpolationPlaceholderReplacements( + i, + ref receiverAssignedToTemp, + ref rewrittenReceiver, + ref temps, + ref argumentsAssignedToTemp); + + visitedArgumentsBuilder.Add(VisitExpression(arguments[i])); + + foreach (var placeholder in argumentPlaceholders) + { + // We didn't set this one up, so we can't remove it. + if (placeholder.ArgumentIndex == BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter) + { + continue; + } + + RemovePlaceholderReplacement(placeholder); + } + } + + Debug.Assert(temps?.Count is null or > 0); + return visitedArgumentsBuilder.ToImmutableAndFree(); + + void ensureTempTrackingSetup([NotNull] ref ArrayBuilder? temps, ref BitVector positionsAssignedToTemp) + { + if (temps == null) + { + temps = ArrayBuilder.GetInstance(); + positionsAssignedToTemp = BitVector.Create(arguments.Length); + } + } + + ImmutableArray addInterpolationPlaceholderReplacements( + int argumentIndex, + ref bool receiverAssignedToTemp, + ref BoundExpression? visitedReceiver, + ref ArrayBuilder? temps, + ref BitVector argumentsAssignedToTemp) + { + var argument = arguments[argumentIndex]; + + if (argument is BoundConversion { ConversionKind: ConversionKind.InterpolatedStringHandler, Operand: BoundInterpolatedString operand }) + { + // Handler conversions are not supported in expression lambdas. + Debug.Assert(!_inExpressionLambda); + var interpolationData = operand.InterpolationData.GetValueOrDefault(); + var creation = (BoundObjectCreationExpression)interpolationData.Construction; + + if (creation.Arguments.Length > (interpolationData.HasTrailingHandlerValidityParameter ? 3 : 2)) + { + Debug.Assert(!((BoundConversion)argument).ExplicitCastInCode); + + // We have an interpolated string handler conversion that needs context from the surrounding arguments. We need to store + // all arguments up to and including the last argument needed by this interpolated string conversion into temps, in order + // to ensure we're keeping lexical ordering of side effects. + ensureTempTrackingSetup(ref temps, ref argumentsAssignedToTemp); + Debug.Assert(!argumentsAssignedToTemp.IsNull); + + foreach (var placeholder in interpolationData.ArgumentPlaceholders) + { + // Replace each needed placeholder with a sequence of store and evaluate the temp. + var argIndex = placeholder.ArgumentIndex; + Debug.Assert(argIndex < argumentIndex); + + BoundLocal local; + switch (argIndex) + { + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter when receiverAssignedToTemp: + Debug.Assert(visitedReceiver != null && requiresInstanceReceiver); + local = (BoundLocal)((BoundSequence)visitedReceiver).Value; + break; + + case BoundInterpolatedStringArgumentPlaceholder.InstanceParameter: + Debug.Assert(visitedReceiver != null && requiresInstanceReceiver); + local = _factory.StoreToTemp(visitedReceiver, out var store, refKind: visitedReceiver.GetRefKind()); + temps.Add(local.LocalSymbol); + visitedReceiver = _factory.Sequence(ImmutableArray.Empty, ImmutableArray.Create(store), local); + receiverAssignedToTemp = true; + break; + + case >= 0 when argumentsAssignedToTemp[argIndex]: + local = visitedArgumentsBuilder[argIndex] switch + { + BoundSequence { Value: BoundLocal l } => l, + BoundLocal l => l, // Can happen for discard arguments + var u => throw ExceptionUtilities.UnexpectedValue(u.Kind) + }; + break; + + case >= 0: + Debug.Assert(visitedArgumentsBuilder[argIndex] != null); + var paramIndex = argsToParamsOpt.IsDefault ? argIndex : argsToParamsOpt[argIndex]; + RefKind argRefKind = argumentRefKindsOpt.RefKinds(argIndex); + RefKind paramRefKind = parameters[paramIndex].RefKind; + var visitedArgument = visitedArgumentsBuilder[argIndex]; + local = _factory.StoreToTemp(visitedArgument, out store, refKind: paramRefKind == RefKind.In ? RefKind.In : argRefKind); + temps.Add(local.LocalSymbol); + visitedArgumentsBuilder[argIndex] = _factory.Sequence(ImmutableArray.Empty, ImmutableArray.Create(store), local); + argumentsAssignedToTemp[argIndex] = true; + break; + + case BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter: + // Visiting the interpolated string itself will allocate the temp for this one. + continue; + + default: + throw ExceptionUtilities.UnexpectedValue(argIndex); + } + + AddPlaceholderReplacement(placeholder, local); + } + + return interpolationData.ArgumentPlaceholders; + } + } + + return ImmutableArray.Empty; + } + } + /// /// Rewrites arguments of an invocation according to the receiving method or indexer. /// It is assumed that each argument has already been lowered, but we may need @@ -380,7 +543,7 @@ private ImmutableArray MakeArguments( bool expanded, ImmutableArray argsToParamsOpt, ref ImmutableArray argumentRefKindsOpt, - out ImmutableArray temps, + [NotNull] ref ArrayBuilder? temps, bool invokedAsExtensionMethod = false, ThreeState enableCallerInfo = ThreeState.Unknown) { @@ -393,13 +556,12 @@ private ImmutableArray MakeArguments( // // If none of those are the case then we can just take an early out. - ArrayBuilder temporariesBuilder = ArrayBuilder.GetInstance(); - rewrittenArguments = _factory.MakeTempsForDiscardArguments(rewrittenArguments, temporariesBuilder); + Debug.Assert(rewrittenArguments.All(arg => arg is not BoundDiscardExpression), "Discards should have been substituted by VisitArguments"); + temps ??= ArrayBuilder.GetInstance(); ImmutableArray parameters = methodOrIndexer.GetParameters(); if (CanSkipRewriting(rewrittenArguments, methodOrIndexer, expanded, argsToParamsOpt, invokedAsExtensionMethod, false, out var isComReceiver)) { - temps = temporariesBuilder.ToImmutableAndFree(); argumentRefKindsOpt = GetEffectiveArgumentRefKinds(argumentRefKindsOpt, parameters); return rewrittenArguments; @@ -467,17 +629,18 @@ private ImmutableArray MakeArguments( parameters, argumentRefKindsOpt, rewrittenArguments, - forceLambdaSpilling: false, // lambda conversions can be re-orderd in calls without side affects + forceLambdaSpilling: false, // lambda conversions can be re-ordered in calls without side affects actualArguments, refKinds, storesToTemps); - // all the formal arguments, except missing optionals, are now in place. // Optimize away unnecessary temporaries. - // Necessary temporaries have their store instructions merged into the appropriate + // Necessary temporaries have their store instructions merged into the appropriate // argument expression. - OptimizeTemporaries(actualArguments, storesToTemps, temporariesBuilder); + OptimizeTemporaries(actualArguments, storesToTemps, temps); + + storesToTemps.Free(); // Step two: If we have a params array, build the array and fill in the argument. if (expanded) @@ -487,12 +650,9 @@ private ImmutableArray MakeArguments( if (isComReceiver) { - RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temporariesBuilder); + RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps); } - temps = temporariesBuilder.ToImmutableAndFree(); - storesToTemps.Free(); - // * The refkind map is now filled out to match the arguments. // * The list of parameter names is now null because the arguments have been reordered. // * The args-to-params map is now null because every argument exactly matches its parameter. @@ -506,10 +666,12 @@ private ImmutableArray MakeArguments( } /// - /// Patch refKinds for arguments that match 'In' parameters to have effective RefKind. + /// Patch refKinds for arguments that match 'In' or 'Ref' parameters to have effective RefKind. /// For the purpose of further analysis we will mark the arguments as - /// - In if was originally passed as None /// - StrictIn if was originally passed as In + /// - Ref if the argument is an interpolated string literal subject to an interpolated string handler conversion. No other types + /// are patched here. /// Here and in the layers after the lowering we only care about None/notNone differences for the arguments /// Except for async stack spilling which needs to know whether arguments were originally passed as "In" and must obey "no copying" rule. /// @@ -522,22 +684,20 @@ private static ImmutableArray GetEffectiveArgumentRefKinds(ImmutableArr if (paramRefKind == RefKind.In) { var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; - - if (refKindsBuilder == null) + fillRefKindsBuilder(argumentRefKindsOpt, parameters, ref refKindsBuilder); + refKindsBuilder[i] = argRefKind == RefKind.None ? paramRefKind : RefKindExtensions.StrictIn; + } + else if (paramRefKind == RefKind.Ref) + { + var argRefKind = argumentRefKindsOpt.IsDefault ? RefKind.None : argumentRefKindsOpt[i]; + if (argRefKind == RefKind.None) { - if (!argumentRefKindsOpt.IsDefault) - { - Debug.Assert(!argumentRefKindsOpt.IsEmpty); - refKindsBuilder = ArrayBuilder.GetInstance(parameters.Length); - refKindsBuilder.AddRange(argumentRefKindsOpt); - } - else - { - refKindsBuilder = ArrayBuilder.GetInstance(parameters.Length, fillWithValue: RefKind.None); - } - } + // Interpolated strings used as interpolated string handlers are allowed to match ref parameters without `ref` + Debug.Assert(parameters[i].Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true, IsValueType: true }); - refKindsBuilder[i] = argRefKind == RefKind.None ? paramRefKind : RefKindExtensions.StrictIn; + fillRefKindsBuilder(argumentRefKindsOpt, parameters, ref refKindsBuilder); + refKindsBuilder[i] = RefKind.Ref; + } } } @@ -549,6 +709,23 @@ private static ImmutableArray GetEffectiveArgumentRefKinds(ImmutableArr // NOTE: we may have more arguments than parameters in a case of arglist. That is ok. Debug.Assert(argumentRefKindsOpt.IsDefault || argumentRefKindsOpt.Length >= parameters.Length); return argumentRefKindsOpt; + + static void fillRefKindsBuilder(ImmutableArray argumentRefKindsOpt, ImmutableArray parameters, [NotNull] ref ArrayBuilder? refKindsBuilder) + { + if (refKindsBuilder == null) + { + if (!argumentRefKindsOpt.IsDefault) + { + Debug.Assert(!argumentRefKindsOpt.IsEmpty); + refKindsBuilder = ArrayBuilder.GetInstance(parameters.Length); + refKindsBuilder.AddRange(argumentRefKindsOpt); + } + else + { + refKindsBuilder = ArrayBuilder.GetInstance(parameters.Length, fillWithValue: RefKind.None); + } + } + } } internal static ImmutableArray MakeArgumentsInEvaluationOrder( diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index 71a2f2b1306c4..e70fd87b6a930 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -294,13 +294,38 @@ private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAcce // tempy = Y() // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1; - ImmutableArray rewrittenArguments = VisitList(indexerAccess.Arguments); - SyntaxNode syntax = indexerAccess.Syntax; PropertySymbol indexer = indexerAccess.Indexer; + ImmutableArray argsToParamsOpt = indexerAccess.ArgsToParamsOpt; ImmutableArray argumentRefKinds = indexerAccess.ArgumentRefKindsOpt; + + ImmutableArray rewrittenArguments = VisitArguments( + indexerAccess.Arguments, + indexer, + argsToParamsOpt, + argumentRefKinds, + ref transformedReceiver!, + out ArrayBuilder? argumentTemps); + + if (argumentTemps != null) + { + temps.AddRange(argumentTemps); + argumentTemps.Free(); + } + + if (transformedReceiver is BoundSequence receiverSequence) + { + // The receiver is a store/evaluate sequence because it was used as an argument to an interpolated + // string handler conversion. + // Pick apart the sequence, add the side effects to the containing list of stores, and set the + // receiver to just be the final temp to ensure we don't double-evaluate the sequence. + + temps.AddRange(receiverSequence.Locals); + stores.AddRange(receiverSequence.SideEffects); + transformedReceiver = receiverSequence.Value; + } + bool expanded = indexerAccess.Expanded; - ImmutableArray argsToParamsOpt = indexerAccess.ArgsToParamsOpt; ImmutableArray parameters = indexer.Parameters; BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 31d93688df3da..005192bd4326f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; @@ -16,16 +15,19 @@ internal partial class LocalRewriter { public override BoundNode VisitConversion(BoundConversion node) { - if (node.ConversionKind == ConversionKind.InterpolatedString) + switch (node.ConversionKind) { - return RewriteInterpolatedStringConversion(node); - } - - if (node.ConversionKind is ConversionKind.SwitchExpression or ConversionKind.ConditionalExpression) - { - // Skip through target-typed conditionals and switches - Debug.Assert(node.Operand is BoundConditionalOperator { WasTargetTyped: true } or BoundConvertedSwitchExpression { WasTargetTyped: true }); - return Visit(node.Operand)!; + case ConversionKind.InterpolatedString: + return RewriteInterpolatedStringConversion(node); + case ConversionKind.InterpolatedStringHandler: + Debug.Assert(node.Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); + + (ArrayBuilder handlerPatternExpressions, BoundLocal handlerLocal) = RewriteToInterpolatedStringHandlerPattern((BoundInterpolatedString)node.Operand); + return _factory.Sequence(ImmutableArray.Create(handlerLocal.LocalSymbol), handlerPatternExpressions.ToImmutableAndFree(), handlerLocal); + case ConversionKind.SwitchExpression or ConversionKind.ConditionalExpression: + // Skip through target-typed conditionals and switches + Debug.Assert(node.Operand is BoundConditionalOperator { WasTargetTyped: true } or BoundConvertedSwitchExpression { WasTargetTyped: true }); + return Visit(node.Operand)!; } var rewrittenType = VisitType(node.Type); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs index 4fb484d7861b4..29d7a27abca11 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_FunctionPointerInvocation.cs @@ -2,10 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable enable - using System.Collections.Immutable; +using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp { @@ -14,10 +14,21 @@ internal sealed partial class LocalRewriter public override BoundNode? VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node) { var rewrittenExpression = VisitExpression(node.InvokedExpression); - var rewrittenArgs = VisitList(node.Arguments); + // There are target types so we can have handler conversions, but there are no attributes so contexts cannot + // be involved. + AssertNoImplicitInterpolatedStringHandlerConversions(node.Arguments, allowConversionsWithNoContext: true); MethodSymbol functionPointer = node.FunctionPointer.Signature; var argumentRefKindsOpt = node.ArgumentRefKindsOpt; + BoundExpression? discardedReceiver = null; + var rewrittenArgs = VisitArguments( + node.Arguments, + functionPointer, + argsToParamsOpt: default, + argumentRefKindsOpt: argumentRefKindsOpt, + rewrittenReceiver: ref discardedReceiver, + temps: out ArrayBuilder? temps); + rewrittenArgs = MakeArguments( node.Syntax, rewrittenArgs, @@ -25,18 +36,20 @@ internal sealed partial class LocalRewriter expanded: false, argsToParamsOpt: default, ref argumentRefKindsOpt, - out ImmutableArray temps, + ref temps, invokedAsExtensionMethod: false, enableCallerInfo: ThreeState.False); + Debug.Assert(rewrittenExpression != null); node = node.Update(rewrittenExpression, rewrittenArgs, argumentRefKindsOpt, node.ResultKind, node.Type); - if (temps.IsDefaultOrEmpty) + if (temps.Count == 0) { + temps.Free(); return node; } - return new BoundSequence(node.Syntax, temps, sideEffects: ImmutableArray.Empty, node, node.Type); + return new BoundSequence(node.Syntax, temps.ToImmutableAndFree(), sideEffects: ImmutableArray.Empty, node, node.Type); } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 4a957a16a19a9..f29f6e7c827f3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -41,6 +41,8 @@ private BoundExpression MakeDynamicIndexerAccessReceiver(BoundDynamicIndexerAcce public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { var loweredReceiver = VisitExpression(node.Receiver); + // There are no target types for dynamic expression. + AssertNoImplicitInterpolatedStringHandlerConversions(node.Arguments); var loweredArguments = VisitList(node.Arguments); return MakeDynamicGetIndex(node, loweredReceiver, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt); @@ -81,16 +83,11 @@ private BoundExpression VisitIndexerAccess(BoundIndexerAccess node, bool isLeftO BoundExpression? rewrittenReceiver = VisitExpression(node.ReceiverOpt); Debug.Assert(rewrittenReceiver is { }); - // Rewrite the arguments. - // NOTE: We may need additional argument rewriting such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. - // NOTE: This is done later by MakeArguments, for now we just lower each argument. - ImmutableArray rewrittenArguments = VisitList(node.Arguments); - return MakeIndexerAccess( node.Syntax, rewrittenReceiver, indexer, - rewrittenArguments, + node.Arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, @@ -105,7 +102,7 @@ private BoundExpression MakeIndexerAccess( SyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol indexer, - ImmutableArray rewrittenArguments, + ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool expanded, @@ -121,17 +118,22 @@ private BoundExpression MakeIndexerAccess( // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. return oldNodeOpt != null ? - oldNodeOpt.Update(rewrittenReceiver, indexer, rewrittenArguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type) : - new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type); + oldNodeOpt.Update(rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type) : + new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type); } else { var getMethod = indexer.GetOwnOrInheritedGetMethod(); Debug.Assert(getMethod is not null); - // We have already lowered each argument, but we may need some additional rewriting for the arguments, - // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. - ImmutableArray temps; + ImmutableArray rewrittenArguments = VisitArguments( + arguments, + indexer, + argsToParamsOpt, + argumentRefKindsOpt, + ref rewrittenReceiver!, + out ArrayBuilder? temps); + rewrittenArguments = MakeArguments( syntax, rewrittenArguments, @@ -139,20 +141,21 @@ private BoundExpression MakeIndexerAccess( expanded, argsToParamsOpt, ref argumentRefKindsOpt, - out temps, + ref temps, enableCallerInfo: ThreeState.True); BoundExpression call = MakePropertyGetAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, getMethod); - if (temps.IsDefaultOrEmpty) + if (temps.Count == 0) { + temps.Free(); return call; } else { return new BoundSequence( syntax, - temps, + temps.ToImmutableAndFree(), ImmutableArray.Empty, call, type); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs index 0b2fa33ff14e2..1e3c780e83afe 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -16,6 +15,8 @@ internal sealed partial class LocalRewriter { public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node) { + // There are no target types for dynamic object creation scenarios, so there should be no implicit handler conversions + AssertNoImplicitInterpolatedStringHandlerConversions(node.Arguments); var loweredArguments = VisitList(node.Arguments); var constructorInvocation = _dynamicFactory.MakeDynamicConstructorInvocation(node.Syntax, node.Type, loweredArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt).ToExpression(); @@ -35,12 +36,19 @@ public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpre // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. - var rewrittenArguments = VisitList(node.Arguments); + BoundExpression? receiverDiscard = null; + + ImmutableArray argumentRefKindsOpt = node.ArgumentRefKindsOpt; + ImmutableArray rewrittenArguments = VisitArguments( + node.Arguments, + node.Constructor, + node.ArgsToParamsOpt, + argumentRefKindsOpt, + ref receiverDiscard, + out ArrayBuilder? tempsBuilder); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, etc. - ImmutableArray temps; - ImmutableArray argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments( node.Syntax, rewrittenArguments, @@ -48,9 +56,10 @@ public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpre node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, - out temps); + ref tempsBuilder); BoundExpression rewrittenObjectCreation; + var temps = tempsBuilder.ToImmutableAndFree(); if (_inExpressionLambda) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs index 74b03b933a3e9..10f12f751529a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs @@ -166,12 +166,6 @@ private BoundExpression MakeDynamicCollectionInitializer(BoundExpression rewritt } } - var rewrittenArguments = VisitList(initializer.Arguments); - var rewrittenType = VisitType(initializer.Type); - - // We have already lowered each argument, but we may need some additional rewriting for the arguments, - // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. - ImmutableArray temps; var argumentRefKindsOpt = default(ImmutableArray); if (addMethod.Parameters[0].RefKind == RefKind.Ref) { @@ -183,7 +177,17 @@ private BoundExpression MakeDynamicCollectionInitializer(BoundExpression rewritt argumentRefKindsOpt = builder.ToImmutableAndFree(); } - rewrittenArguments = MakeArguments(syntax, rewrittenArguments, addMethod, initializer.Expanded, initializer.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps, enableCallerInfo: ThreeState.True); + // The receiver for a collection initializer is already a temp, so we don't need to preserve any additional temp stores beyond this method. + ImmutableArray rewrittenArguments = VisitArguments( + initializer.Arguments, + addMethod, + initializer.ArgsToParamsOpt, + argumentRefKindsOpt, + ref rewrittenReceiver, + out ArrayBuilder? temps); + rewrittenArguments = MakeArguments(syntax, rewrittenArguments, addMethod, initializer.Expanded, initializer.ArgsToParamsOpt, ref argumentRefKindsOpt, ref temps, enableCallerInfo: ThreeState.True); + + var rewrittenType = VisitType(initializer.Type); if (initializer.InvokedAsExtensionMethod) { @@ -195,10 +199,12 @@ private BoundExpression MakeDynamicCollectionInitializer(BoundExpression rewritt if (_inExpressionLambda) { + Debug.Assert(temps.Count == 0); + temps.Free(); return initializer.Update(addMethod, rewrittenArguments, rewrittenReceiver, expanded: false, argsToParamsOpt: default, defaultArguments: default, initializer.InvokedAsExtensionMethod, initializer.ResultKind, rewrittenType); } - return MakeCall(null, syntax, rewrittenReceiver, addMethod, rewrittenArguments, argumentRefKindsOpt, initializer.InvokedAsExtensionMethod, initializer.ResultKind, addMethod.ReturnType, temps); + return MakeCall(null, syntax, rewrittenReceiver, addMethod, rewrittenArguments, argumentRefKindsOpt, initializer.InvokedAsExtensionMethod, initializer.ResultKind, addMethod.ReturnType, temps.ToImmutableAndFree()); } // Rewrite object initializer member assignments and add them to the result. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs index 78836a245dbfc..dc156979c39e3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs @@ -2,9 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using System.Diagnostics; +using System.Linq; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -35,6 +38,129 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv return result; } + /// + /// Rewrites the given interpolated string to the set of handler creation and Append calls, returning an array builder of the append calls and the result + /// local temp. + /// + /// Caller is responsible for freeing the ArrayBuilder + private (ArrayBuilder HandlerPatternExpressions, BoundLocal Result) RewriteToInterpolatedStringHandlerPattern(BoundInterpolatedString node) + { + Debug.Assert(node.InterpolationData is { Construction: not null }); + Debug.Assert(node.Parts.All(static p => p is BoundCall or BoundDynamicInvocation or BoundDynamicMemberAccess or BoundDynamicIndexerAccess)); + var data = node.InterpolationData.Value; + var builderTempSymbol = _factory.InterpolatedStringHandlerLocal(data.BuilderType, data.ScopeOfContainingExpression, node.Syntax); + BoundLocal builderTemp = _factory.Local(builderTempSymbol); + + // var handler = new HandlerType(baseStringLength, numFormatHoles, ...InterpolatedStringHandlerArgumentAttribute parameters, out bool appendShouldProceed); + var construction = (BoundObjectCreationExpression)data.Construction; + + BoundLocal? appendShouldProceedLocal = null; + if (data.HasTrailingHandlerValidityParameter) + { + Debug.Assert(construction.ArgumentRefKindsOpt[^1] == RefKind.Out); + + BoundInterpolatedStringArgumentPlaceholder trailingParameter = data.ArgumentPlaceholders[^1]; + TypeSymbol localType = trailingParameter.Type; + Debug.Assert(localType.SpecialType == SpecialType.System_Boolean); + var outLocal = _factory.SynthesizedLocal(localType); + appendShouldProceedLocal = _factory.Local(outLocal); + + AddPlaceholderReplacement(trailingParameter, appendShouldProceedLocal); + } + + var handlerConstructionAssignment = _factory.AssignmentExpression(builderTemp, (BoundExpression)VisitObjectCreationExpression(construction)); + + AddPlaceholderReplacement(data.ReceiverPlaceholder, builderTemp); + bool usesBoolReturns = data.UsesBoolReturns; + var resultExpressions = ArrayBuilder.GetInstance(node.Parts.Length + 1); + + foreach (var part in node.Parts) + { + if (part is BoundCall call) + { + Debug.Assert(call.Type.SpecialType == SpecialType.System_Boolean == usesBoolReturns); + resultExpressions.Add((BoundExpression)VisitCall(call)); + } + else if (part is BoundDynamicInvocation dynamicInvocation) + { + resultExpressions.Add(VisitDynamicInvocation(dynamicInvocation, resultDiscarded: !usesBoolReturns)); + } + else + { + throw ExceptionUtilities.UnexpectedValue(part.Kind); + } + } + + RemovePlaceholderReplacement(data.ReceiverPlaceholder); + + if (appendShouldProceedLocal is not null) + { + RemovePlaceholderReplacement(data.ArgumentPlaceholders[^1]); + } + + if (usesBoolReturns) + { + // We assume non-bool returns if there was no parts to the string, and code below is predicated on that. + Debug.Assert(!node.Parts.IsEmpty); + // Start the sequence with appendProceedLocal, if appropriate + BoundExpression? currentExpression = appendShouldProceedLocal; + + var boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); + + foreach (var appendCall in resultExpressions) + { + var actualCall = appendCall; + if (actualCall.Type!.IsDynamic()) + { + actualCall = _dynamicFactory.MakeDynamicConversion(actualCall, isExplicit: false, isArrayIndex: false, isChecked: false, boolType).ToExpression(); + } + + // previousAppendCalls && appendCall + currentExpression = currentExpression is null + ? actualCall + : _factory.LogicalAnd(currentExpression, actualCall); + } + + resultExpressions.Clear(); + + Debug.Assert(currentExpression != null); + + var sequence = _factory.Sequence( + appendShouldProceedLocal is not null + ? ImmutableArray.Create(appendShouldProceedLocal.LocalSymbol) + : ImmutableArray.Empty, + ImmutableArray.Create(handlerConstructionAssignment), + currentExpression); + + resultExpressions.Add(sequence); + } + else if (appendShouldProceedLocal is not null && resultExpressions.Count > 0) + { + // appendCalls Sequence ending in true + var appendCallsSequence = _factory.Sequence(ImmutableArray.Empty, resultExpressions.ToImmutableAndClear(), _factory.Literal(value: true)); + + resultExpressions.Add(handlerConstructionAssignment); + + // appendShouldProceedLocal && sequence + var appendAnd = _factory.LogicalAnd(appendShouldProceedLocal, appendCallsSequence); + var result = _factory.Sequence(ImmutableArray.Create(appendShouldProceedLocal.LocalSymbol), resultExpressions.ToImmutableAndClear(), appendAnd); + + resultExpressions.Add(result); + } + else if (appendShouldProceedLocal is not null) + { + // Odd case of no append calls, but with an out param. We don't need to generate any jumps checking the local because there's + // nothing to short circuit and avoid, but we do need a sequence to hold the lifetime of the local + resultExpressions.Add(_factory.Sequence(ImmutableArray.Create(appendShouldProceedLocal.LocalSymbol), ImmutableArray.Empty, handlerConstructionAssignment)); + } + else + { + resultExpressions.Insert(0, handlerConstructionAssignment); + } + + return (resultExpressions, builderTemp); + } + private bool CanLowerToStringConcatenation(BoundInterpolatedString node) { foreach (var part in node.Parts) @@ -108,7 +234,20 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) BoundExpression? result; - if (CanLowerToStringConcatenation(node)) + if (node.InterpolationData is not null) + { + // If we can lower to the builder pattern, do so. + (ArrayBuilder handlerPatternExpressions, BoundLocal handlerTemp) = RewriteToInterpolatedStringHandlerPattern(node); + + // resultTemp = builderTemp.ToStringAndClear(); + var toStringAndClear = (MethodSymbol)Binder.GetWellKnownTypeMember(_compilation, WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear, _diagnostics, syntax: node.Syntax); + BoundExpression toStringAndClearCall = toStringAndClear is not null + ? BoundCall.Synthesized(node.Syntax, handlerTemp, toStringAndClear) + : new BoundBadExpression(node.Syntax, LookupResultKind.Empty, symbols: ImmutableArray.Empty, childBoundNodes: ImmutableArray.Empty, node.Type); + + return _factory.Sequence(ImmutableArray.Create(handlerTemp.LocalSymbol), handlerPatternExpressions.ToImmutableAndFree(), toStringAndClearCall); + } + else if (CanLowerToStringConcatenation(node)) { // All fill-ins, if any, are strings, and none of them have alignment or format specifiers. // We can lower to a more efficient string concatenation @@ -143,7 +282,10 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) _factory.Binary(BinaryOperatorKind.StringConcatenation, node.Type, result, part); } - if (length == 1) + // We need to ensure that the result of the interpolated string is not null. If the single part has a non-null constant value + // or is itself an interpolated string (which by proxy cannot be null), then there's nothing else that needs to be done. Otherwise, + // we need to test for null and ensure "" if it is. + if (length == 1 && result is not ({ Kind: BoundKind.InterpolatedString } or { ConstantValue: { IsString: true } })) { result = _factory.Coalesce(result!, _factory.StringLiteral("")); } @@ -181,5 +323,26 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) } return result; } + + [Conditional("DEBUG")] + private static void AssertNoImplicitInterpolatedStringHandlerConversions(ImmutableArray arguments, bool allowConversionsWithNoContext = false) + { + if (allowConversionsWithNoContext) + { + foreach (var arg in arguments) + { + if (arg is BoundConversion { Conversion: { Kind: ConversionKind.InterpolatedStringHandler }, ExplicitCastInCode: false, Operand: BoundInterpolatedString @string }) + { + Debug.Assert(((BoundObjectCreationExpression)@string.InterpolationData!.Value.Construction).Arguments.All( + a => a is BoundInterpolatedStringArgumentPlaceholder { ArgumentIndex: BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter } + or not BoundInterpolatedStringArgumentPlaceholder)); + } + } + } + else + { + Debug.Assert(arguments.All(arg => arg is not BoundConversion { Conversion: { IsInterpolatedStringHandler: true }, ExplicitCastInCode: false })); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs index 1f87abcf39799..05a5678e72fb8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs @@ -475,7 +475,8 @@ private BoundExpression GenerateDisposeCall( } /// - /// Synthesize a call `expression.Method()`, but with some extra smarts to handle extension methods, and to fill-in optional and params parameters. + /// Synthesize a call `expression.Method()`, but with some extra smarts to handle extension methods, and to fill-in optional and params parameters. This call expects that the + /// receiver parameter has already been visited. /// private BoundExpression MakeCallWithNoExplicitArgument(MethodArgumentInfo methodArgumentInfo, SyntaxNode syntax, BoundExpression? expression, bool assertParametersAreOptional = true) { @@ -493,9 +494,11 @@ private BoundExpression MakeCallWithNoExplicitArgument(MethodArgumentInfo method Debug.Assert(!assertParametersAreOptional || method.Parameters.All(p => p.IsOptional || p.IsParams)); Debug.Assert(method.ParameterRefKinds.IsDefaultOrEmpty); } + + Debug.Assert(methodArgumentInfo.Arguments.All(arg => arg is not BoundConversion { ConversionKind: ConversionKind.InterpolatedStringHandler })); #endif - return MakeCall( + return MakeArgumentsAndCall( syntax, expression, method, @@ -505,7 +508,8 @@ private BoundExpression MakeCallWithNoExplicitArgument(MethodArgumentInfo method invokedAsExtensionMethod: method.IsExtensionMethod, methodArgumentInfo.ArgsToParamsOpt, resultKind: LookupResultKind.Viable, - type: method.ReturnType); + type: method.ReturnType, + temps: null); } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 0b4f2a282c6da..f90f3fe486e9c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -525,7 +525,7 @@ public LocalSymbol SynthesizedLocal( #if DEBUG , [CallerLineNumber] int createdAtLineNumber = 0, - [CallerFilePath] string? createdAtFilePath = null + [CallerFilePath] string createdAtFilePath = "" #endif ) { @@ -536,6 +536,29 @@ public LocalSymbol SynthesizedLocal( ); } + public LocalSymbol InterpolatedStringHandlerLocal( + TypeSymbol type, + uint valEscapeScope, + SyntaxNode syntax +#if DEBUG + , + [CallerLineNumber] int createdAtLineNumber = 0, + [CallerFilePath] string createdAtFilePath = "" +#endif + ) + { + return new SynthesizedLocalWithValEscape( + CurrentFunction, + TypeWithAnnotations.Create(type), + SynthesizedLocalKind.InterpolatedStringHandler, + valEscapeScope, + syntax +#if DEBUG + , createdAtLineNumber: createdAtLineNumber, createdAtFilePath: createdAtFilePath +#endif + ); + } + public ParameterSymbol SynthesizedParameter(TypeSymbol type, string name, MethodSymbol? container = null, int ordinal = 0) { return SynthesizedParameterSymbol.Create(container, TypeWithAnnotations.Create(type), ordinal, RefKind.None, name); diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index a3e22b158ca00..4a66c88350e8d 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -949,15 +949,26 @@ private ILocalFunctionOperation CreateBoundLocalFunctionStatementOperation(Bound return new LocalFunctionOperation(symbol, body, ignoredBody, _semanticModel, syntax, isImplicit); } - private IOperation CreateBoundConversionOperation(BoundConversion boundConversion) + private IOperation CreateBoundConversionOperation(BoundConversion boundConversion, bool forceOperandImplicitLiteral = false) { - bool isImplicit = boundConversion.WasCompilerGenerated || !boundConversion.ExplicitCastInCode; + Debug.Assert(!forceOperandImplicitLiteral || boundConversion.Operand is BoundLiteral); + bool isImplicit = boundConversion.WasCompilerGenerated || !boundConversion.ExplicitCastInCode || forceOperandImplicitLiteral; BoundExpression boundOperand = boundConversion.Operand; + + if (boundConversion.ConversionKind == ConversionKind.InterpolatedStringHandler) + { + // https://github.com/dotnet/roslyn/issues/54505 Support interpolation handlers in conversions + Debug.Assert(!forceOperandImplicitLiteral); + Debug.Assert(boundOperand is BoundInterpolatedString); + var interpolatedString = Create(boundOperand); + return new NoneOperation(ImmutableArray.Create(interpolatedString), _semanticModel, boundConversion.Syntax, boundConversion.GetPublicTypeSymbol(), boundConversion.ConstantValue, isImplicit); + } + if (boundConversion.ConversionKind == CSharp.ConversionKind.MethodGroup) { SyntaxNode syntax = boundConversion.Syntax; ITypeSymbol? type = boundConversion.GetPublicTypeSymbol(); - ConstantValue? constantValue = boundConversion.ConstantValue; + Debug.Assert(!forceOperandImplicitLiteral); if (boundConversion.Type is FunctionPointerTypeSymbol) { @@ -990,6 +1001,7 @@ private IOperation CreateBoundConversionOperation(BoundConversion boundConversio Debug.Assert(boundOperand.Kind == BoundKind.BadExpression || ((boundOperand as BoundLambda)?.Body.Statements.SingleOrDefault() as BoundReturnStatement)?. ExpressionOpt?.Kind == BoundKind.BadExpression); + Debug.Assert(!forceOperandImplicitLiteral); return Create(boundOperand); } @@ -1002,6 +1014,7 @@ private IOperation CreateBoundConversionOperation(BoundConversion boundConversio { // Erase this conversion, this is an artificial conversion added on top of BoundConvertedTupleLiteral // in Binder.CreateTupleLiteralConversion + Debug.Assert(!forceOperandImplicitLiteral); return Create(boundOperand); } else @@ -1046,7 +1059,9 @@ private IOperation CreateBoundConversionOperation(BoundConversion boundConversio bool isTryCast = false; // Checked conversions only matter if the conversion is a Numeric conversion. Don't have true unless the conversion is actually numeric. bool isChecked = conversion.IsNumeric && boundConversion.Checked; - IOperation operand = Create(correctedConversionNode.Operand); + IOperation operand = forceOperandImplicitLiteral + ? CreateBoundLiteralOperation((BoundLiteral)correctedConversionNode.Operand, @implicit: true) + : Create(correctedConversionNode.Operand); return new ConversionOperation(operand, conversion, isTryCast, isChecked, _semanticModel, syntax, type, constantValue, isImplicit); } } @@ -1973,7 +1988,7 @@ internal IOperation CreateBoundTupleOperation(BoundTupleExpression boundTupleExp private IInterpolatedStringOperation CreateBoundInterpolatedStringExpressionOperation(BoundInterpolatedString boundInterpolatedString) { - ImmutableArray parts = CreateBoundInterpolatedStringContentOperation(boundInterpolatedString.Parts); + ImmutableArray parts = CreateBoundInterpolatedStringContentOperation(boundInterpolatedString.Parts, boundInterpolatedString.InterpolationData); SyntaxNode syntax = boundInterpolatedString.Syntax; ITypeSymbol? type = boundInterpolatedString.GetPublicTypeSymbol(); ConstantValue? constantValue = boundInterpolatedString.ConstantValue; @@ -1981,21 +1996,132 @@ private IInterpolatedStringOperation CreateBoundInterpolatedStringExpressionOper return new InterpolatedStringOperation(parts, _semanticModel, syntax, type, constantValue, isImplicit); } - internal ImmutableArray CreateBoundInterpolatedStringContentOperation(ImmutableArray parts) + internal ImmutableArray CreateBoundInterpolatedStringContentOperation(ImmutableArray parts, InterpolatedStringHandlerData? data) { - var builder = ArrayBuilder.GetInstance(parts.Length); - foreach (var part in parts) + return data is { PositionInfo: var positionInfo } ? createHandlerInterpolatedStringContent(positionInfo) : createNonHandlerInterpolatedStringContent(); + + ImmutableArray createNonHandlerInterpolatedStringContent() { - if (part.Kind == BoundKind.StringInsert) + var builder = ArrayBuilder.GetInstance(parts.Length); + foreach (var part in parts) { - builder.Add((IInterpolatedStringContentOperation)Create(part)); + if (part.Kind == BoundKind.StringInsert) + { + builder.Add((IInterpolatedStringContentOperation)Create(part)); + } + else + { + builder.Add(CreateBoundInterpolatedStringTextOperation((BoundLiteral)part)); + } } - else + + return builder.ToImmutableAndFree(); + } + + ImmutableArray createHandlerInterpolatedStringContent(ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)> positionInfo) + { + // For interpolated string handlers, we want to deconstruct the `AppendLiteral`/`AppendFormatted` calls into + // their relevant components. + // https://github.com/dotnet/roslyn/issues/54505 we need to handle interpolated strings used as handler conversions. + + Debug.Assert(parts.Length == positionInfo.Length); + var builder = ArrayBuilder.GetInstance(parts.Length); + + for (int i = 0; i < parts.Length; i++) { - builder.Add(CreateBoundInterpolatedStringTextOperation((BoundLiteral)part)); + var part = parts[i]; + var currentPosition = positionInfo[i]; + + BoundExpression value; + BoundExpression? alignment; + BoundExpression? format; + + switch (part) + { + case BoundCall call: + (value, alignment, format) = getCallInfo(call.Arguments, call.ArgumentNamesOpt, currentPosition); + break; + + case BoundDynamicInvocation dynamicInvocation: + (value, alignment, format) = getCallInfo(dynamicInvocation.Arguments, dynamicInvocation.ArgumentNamesOpt, currentPosition); + break; + + case BoundBadExpression bad: + Debug.Assert(bad.ChildBoundNodes.Length == + 2 + // initial value + receiver is added to the end + (currentPosition.HasAlignment ? 1 : 0) + + (currentPosition.HasFormat ? 1 : 0)); + + value = bad.ChildBoundNodes[0]; + if (currentPosition.IsLiteral) + { + alignment = format = null; + } + else + { + alignment = currentPosition.HasAlignment ? bad.ChildBoundNodes[1] : null; + format = currentPosition.HasFormat ? bad.ChildBoundNodes[^2] : null; + } + break; + + default: + throw ExceptionUtilities.UnexpectedValue(part.Kind); + } + + // We are intentionally not checking the part for implicitness here. The part is a generated AppendLiteral or AppendFormatted call, + // and will always be marked as CompilerGenerated. However, our existing behavior for non-builder interpolated strings does not mark + // the BoundLiteral or BoundStringInsert components as compiler generated. This generates a non-implicit IInterpolatedStringTextOperation + // with an implicit literal underneath, and a non-implicit IInterpolationOperation with non-implicit underlying components. + bool isImplicit = false; + if (currentPosition.IsLiteral) + { + Debug.Assert(alignment is null); + Debug.Assert(format is null); + IOperation valueOperation = value switch + { + BoundLiteral l => CreateBoundLiteralOperation(l, @implicit: true), + BoundConversion { Operand: BoundLiteral } c => CreateBoundConversionOperation(c, forceOperandImplicitLiteral: true), + _ => throw ExceptionUtilities.UnexpectedValue(value.Kind), + }; + + Debug.Assert(valueOperation.IsImplicit); + + builder.Add(new InterpolatedStringTextOperation(valueOperation, _semanticModel, part.Syntax, isImplicit)); + } + else + { + IOperation valueOperation = Create(value); + IOperation? alignmentOperation = Create(alignment); + IOperation? formatOperation = Create(format); + + Debug.Assert(valueOperation.Syntax != part.Syntax); + + builder.Add(new InterpolationOperation(valueOperation, alignmentOperation, formatOperation, _semanticModel, part.Syntax, isImplicit)); + } + } + + return builder.ToImmutableAndFree(); + + static (BoundExpression Value, BoundExpression? Alignment, BoundExpression? Format) getCallInfo(ImmutableArray arguments, ImmutableArray argumentNamesOpt, (bool IsLiteral, bool HasAlignment, bool HasFormat) currentPosition) + { + BoundExpression value = arguments[0]; + + if (currentPosition.IsLiteral || argumentNamesOpt.IsDefault) + { + // There was no alignment/format component, as binding will qualify those parameters by name + return (value, null, null); + } + else + { + var alignmentIndex = argumentNamesOpt.IndexOf("alignment"); + BoundExpression? alignment = alignmentIndex == -1 ? null : arguments[alignmentIndex]; + var formatIndex = argumentNamesOpt.IndexOf("format"); + BoundExpression? format = formatIndex == -1 ? null : arguments[formatIndex]; + + return (value, alignment, format); + } } } - return builder.ToImmutableAndFree(); } private IInterpolationOperation CreateBoundInterpolationOperation(BoundStringInsert boundStringInsert) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 37f5aa90cb7e1..9e96cc92e802b 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -232,7 +232,7 @@ private TNode CreateForGlobalFailure(int position, TNode node) where TNod return AddError(node, position, 0, ErrorCode.ERR_InsufficientStack); } - private NamespaceDeclarationSyntax ParseNamespaceDeclaration( + private BaseNamespaceDeclarationSyntax ParseNamespaceDeclaration( SyntaxList attributeLists, SyntaxListBuilder modifiers) { @@ -243,7 +243,7 @@ private NamespaceDeclarationSyntax ParseNamespaceDeclaration( return result; } - private NamespaceDeclarationSyntax ParseNamespaceDeclarationCore( + private BaseNamespaceDeclarationSyntax ParseNamespaceDeclarationCore( SyntaxList attributeLists, SyntaxListBuilder modifiers) { @@ -257,8 +257,14 @@ private NamespaceDeclarationSyntax ParseNamespaceDeclarationCore( var name = this.ParseQualifiedName(); - SyntaxToken openBrace; - if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || IsPossibleNamespaceMemberDeclaration()) + SyntaxToken openBrace = null; + SyntaxToken semicolon = null; + + if (this.CurrentToken.Kind == SyntaxKind.SemicolonToken) + { + semicolon = this.EatToken(SyntaxKind.SemicolonToken); + } + else if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken || IsPossibleNamespaceMemberDeclaration()) { //either we see the brace we expect here or we see something that could come after a brace //so we insert a missing one @@ -272,19 +278,48 @@ private NamespaceDeclarationSyntax ParseNamespaceDeclarationCore( openBrace = this.ConvertToMissingWithTrailingTrivia(openBrace, SyntaxKind.OpenBraceToken); } + Debug.Assert(semicolon != null || openBrace != null); + var body = new NamespaceBodyBuilder(_pool); - SyntaxListBuilder initialBadNodes = null; try { - this.ParseNamespaceBody(ref openBrace, ref body, ref initialBadNodes, SyntaxKind.NamespaceDeclaration); + if (openBrace == null) + { + Debug.Assert(semicolon != null); - var closeBrace = this.EatToken(SyntaxKind.CloseBraceToken); - var semicolon = this.TryEatToken(SyntaxKind.SemicolonToken); + SyntaxListBuilder initialBadNodes = null; + this.ParseNamespaceBody(ref semicolon, ref body, ref initialBadNodes, SyntaxKind.FileScopedNamespaceDeclaration); + Debug.Assert(initialBadNodes == null); // init bad nodes should have been attached to semicolon... - Debug.Assert(initialBadNodes == null); // init bad nodes should have been attached to open brace... - return _syntaxFactory.NamespaceDeclaration( - attributeLists, modifiers.ToList(), - namespaceToken, name, openBrace, body.Externs, body.Usings, body.Members, closeBrace, semicolon); + namespaceToken = CheckFeatureAvailability(namespaceToken, MessageID.IDS_FeatureFileScopedNamespace); + return _syntaxFactory.FileScopedNamespaceDeclaration( + attributeLists, + modifiers.ToList(), + namespaceToken, + name, + semicolon, + body.Externs, + body.Usings, + body.Members); + } + else + { + SyntaxListBuilder initialBadNodes = null; + this.ParseNamespaceBody(ref openBrace, ref body, ref initialBadNodes, SyntaxKind.NamespaceDeclaration); + Debug.Assert(initialBadNodes == null); // init bad nodes should have been attached to open brace... + + return _syntaxFactory.NamespaceDeclaration( + attributeLists, + modifiers.ToList(), + namespaceToken, + name, + openBrace, + body.Externs, + body.Usings, + body.Members, + this.EatToken(SyntaxKind.CloseBraceToken), + this.TryEatToken(SyntaxKind.SemicolonToken)); + } } finally { @@ -318,7 +353,7 @@ private static bool IsPossibleStartOfTypeDeclaration(SyntaxKind kind) } private void AddSkippedNamespaceText( - ref SyntaxToken openBrace, + ref SyntaxToken openBraceOrSemicolon, ref NamespaceBodyBuilder body, ref SyntaxListBuilder initialBadNodes, CSharpSyntaxNode skippedSyntax) @@ -339,9 +374,9 @@ private void AddSkippedNamespaceText( { AddTrailingSkippedSyntax(body.Externs, skippedSyntax); } - else if (openBrace != null) + else if (openBraceOrSemicolon != null) { - openBrace = AddTrailingSkippedSyntax(openBrace, skippedSyntax); + openBraceOrSemicolon = AddTrailingSkippedSyntax(openBraceOrSemicolon, skippedSyntax); } else { @@ -366,12 +401,12 @@ private enum NamespaceParts TopLevelStatementsAfterTypesAndNamespaces = 6, } - private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuilder body, ref SyntaxListBuilder initialBadNodes, SyntaxKind parentKind) + private void ParseNamespaceBody(ref SyntaxToken openBraceOrSemicolon, ref NamespaceBodyBuilder body, ref SyntaxListBuilder initialBadNodes, SyntaxKind parentKind) { // "top-level" expressions and statements should never occur inside an asynchronous context Debug.Assert(!IsInAsync); - bool isGlobal = openBrace == null; + bool isGlobal = openBraceOrSemicolon == null; var saveTerm = _termState; _termState |= TerminatorState.IsNamespaceMemberStartOrStop; @@ -410,13 +445,13 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil if (isGlobal) { // incomplete members must be processed before we add any nodes to the body: - ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes); + ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes); var token = this.EatToken(); token = this.AddError(token, IsScript ? ErrorCode.ERR_GlobalDefinitionOrStatementExpected : ErrorCode.ERR_EOFExpected); - this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, token); + this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, token); reportUnexpectedToken = true; break; } @@ -439,13 +474,13 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil else { // incomplete members must be processed before we add any nodes to the body: - ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes); + ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes); var @extern = ParseExternAliasDirective(); if (seen > NamespaceParts.ExternAliases) { @extern = this.AddErrorToFirstToken(@extern, ErrorCode.ERR_ExternAfterElements); - this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, @extern); + this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, @extern); } else { @@ -465,7 +500,7 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil } else { - parseUsingDirective(ref openBrace, ref body, ref initialBadNodes, ref seen, ref pendingIncompleteMembers); + parseUsingDirective(ref openBraceOrSemicolon, ref body, ref initialBadNodes, ref seen, ref pendingIncompleteMembers); } reportUnexpectedToken = true; @@ -478,7 +513,7 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil } else { - parseUsingDirective(ref openBrace, ref body, ref initialBadNodes, ref seen, ref pendingIncompleteMembers); + parseUsingDirective(ref openBraceOrSemicolon, ref body, ref initialBadNodes, ref seen, ref pendingIncompleteMembers); } reportUnexpectedToken = true; @@ -488,13 +523,13 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil if (this.IsPossibleGlobalAttributeDeclaration()) { // incomplete members must be processed before we add any nodes to the body: - ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes); + ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes); var attribute = this.ParseAttributeDeclaration(); if (!isGlobal || seen > NamespaceParts.GlobalAttributes) { attribute = this.AddError(attribute, attribute.Target.Identifier, ErrorCode.ERR_GlobalAttributesNotFirst); - this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, attribute); + this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, attribute); } else { @@ -513,7 +548,7 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil if (memberOrStatement == null) { // incomplete members must be processed before we add any nodes to the body: - ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBrace, ref body, ref initialBadNodes); + ReduceIncompleteMembers(ref pendingIncompleteMembers, ref openBraceOrSemicolon, ref body, ref initialBadNodes); // eat one token and try to parse declaration or statement again: var skippedToken = EatToken(); @@ -526,7 +561,7 @@ private void ParseNamespaceBody(ref SyntaxToken openBrace, ref NamespaceBodyBuil reportUnexpectedToken = false; } - this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, skippedToken); + this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, skippedToken); } else if (memberOrStatement.Kind == SyntaxKind.IncompleteMember && seen < NamespaceParts.MembersAndStatements) { @@ -576,6 +611,7 @@ MemberDeclarationSyntax adjustStateAndReportStatementOutOfOrder(ref NamespacePar break; case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.ClassDeclaration: @@ -639,12 +675,15 @@ private static void AddIncompleteMembers(ref SyntaxListBuilder incompleteMembers, - ref SyntaxToken openBrace, ref NamespaceBodyBuilder body, ref SyntaxListBuilder initialBadNodes) + private void ReduceIncompleteMembers( + ref SyntaxListBuilder incompleteMembers, + ref SyntaxToken openBraceOrSemicolon, + ref NamespaceBodyBuilder body, + ref SyntaxListBuilder initialBadNodes) { for (int i = 0; i < incompleteMembers.Count; i++) { - this.AddSkippedNamespaceText(ref openBrace, ref body, ref initialBadNodes, incompleteMembers[i]); + this.AddSkippedNamespaceText(ref openBraceOrSemicolon, ref body, ref initialBadNodes, incompleteMembers[i]); } incompleteMembers.Clear(); } @@ -2130,6 +2169,7 @@ private bool CanReuseMemberDeclaration(SyntaxKind kind, bool isGlobal) case SyntaxKind.DestructorDeclaration: case SyntaxKind.ConstructorDeclaration: case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: return true; @@ -2621,7 +2661,7 @@ private bool IsNoneOrIncompleteMember(SyntaxKind parentKind, SyntaxList bool abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseExpressionColonSyntax.Expression.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax abstract Microsoft.CodeAnalysis.CSharp.Syntax.LineOrSpanDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken @@ -52,6 +53,67 @@ Microsoft.CodeAnalysis.CSharp.Syntax.SubpatternSyntax.WithExpressionColon(Micros Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionColon = 9069 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.LineDirectivePosition = 9070 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind Microsoft.CodeAnalysis.CSharp.SyntaxKind.LineSpanDirectiveTrivia = 9071 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Externs.get -> Microsoft.CodeAnalysis.SyntaxList +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Members.get -> Microsoft.CodeAnalysis.SyntaxList +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.NamespaceKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Usings.get -> Microsoft.CodeAnalysis.SyntaxList +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.AddAttributeLists(params Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.AddExterns(params Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.AddMembers(params Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.AddUsings(params Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList attributeLists) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithExterns(Microsoft.CodeAnalysis.SyntaxList externs) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithMembers(Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithName(Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithNamespaceKeyword(Microsoft.CodeAnalysis.SyntaxToken namespaceKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.WithUsings(Microsoft.CodeAnalysis.SyntaxList usings) -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.AddAttributeLists(params Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.AddExterns(params Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.AddMembers(params Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.AddUsings(params Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken namespaceKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxToken semicolonToken, Microsoft.CodeAnalysis.SyntaxList externs, Microsoft.CodeAnalysis.SyntaxList usings, Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList attributeLists) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithExterns(Microsoft.CodeAnalysis.SyntaxList externs) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithMembers(Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithName(Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithNamespaceKeyword(Microsoft.CodeAnalysis.SyntaxToken namespaceKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.WithUsings(Microsoft.CodeAnalysis.SyntaxList usings) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.SyntaxKind.FileScopedNamespaceDeclaration = 8845 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.Externs.get -> Microsoft.CodeAnalysis.SyntaxList +abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.Members.get -> Microsoft.CodeAnalysis.SyntaxList +abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax +abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.NamespaceKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +abstract Microsoft.CodeAnalysis.CSharp.Syntax.BaseNamespaceDeclarationSyntax.Usings.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitFileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode +override Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Externs.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Members.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax +override Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.NamespaceKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.NamespaceDeclarationSyntax.Usings.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor visitor) -> TResult +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.AttributeLists.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Externs.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Members.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Modifiers.get -> Microsoft.CodeAnalysis.SyntaxTokenList +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Name.get -> Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.NamespaceKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax.Usings.get -> Microsoft.CodeAnalysis.SyntaxList +static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Microsoft.CodeAnalysis.SemanticModel semanticModel, Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax declarationSyntax, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.INamespaceSymbol +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxList externs, Microsoft.CodeAnalysis.SyntaxList usings, Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken namespaceKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxToken semicolonToken, Microsoft.CodeAnalysis.SyntaxList externs, Microsoft.CodeAnalysis.SyntaxList usings, Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax node) -> TResult Microsoft.CodeAnalysis.CSharp.SyntaxKind.RecordStructDeclaration = 9068 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetUsedAssemblyReferences(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Immutable.ImmutableArray override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax node) -> Microsoft.CodeAnalysis.SyntaxNode diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs index 76dbaa0e0f28b..de9e3461b256f 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor_Minimal.cs @@ -197,7 +197,7 @@ private IDictionary CreateAliasMap() startNode = usingDirective.Parent.Parent; } - var usingAliases = GetAncestorsOrThis(startNode) + var usingAliases = GetAncestorsOrThis(startNode) .SelectMany(n => n.Usings) .Concat(GetAncestorsOrThis(startNode).SelectMany(c => c.Usings)) .Where(u => u.Alias != null) diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs index 999b1979c4907..d06768b9dd9cd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousType.TypePublicSymbol.cs @@ -92,6 +92,8 @@ internal override ImmutableArray TypeArgumentsWithAnnotatio internal override bool HasCodeAnalysisEmbeddedAttribute => false; + internal override bool IsInterpolatedStringHandlerType => false; + public override ImmutableArray GetMembers(string name) { var symbols = _nameToSymbols[name]; diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs index 6b8c23d706f7f..328d7f9663bcb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs @@ -209,6 +209,8 @@ internal override IEnumerable GetFieldsToEmit() internal override bool HasCodeAnalysisEmbeddedAttribute => false; + internal override bool IsInterpolatedStringHandlerType => false; + internal override ImmutableArray TypeArgumentsWithAnnotationsNoUseSiteDiagnostics { get { return GetTypeParametersAsTypeArguments(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterWellKnownAttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterWellKnownAttributeData.cs index e23b1e87ac78e..2275e9dc1c290 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterWellKnownAttributeData.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/ParameterWellKnownAttributeData.cs @@ -158,5 +158,21 @@ public void AddNotNullIfParameterNotNull(string parameterName) _notNullIfParameterNotNull = _notNullIfParameterNotNull.Add(parameterName); SetDataStored(); } + + private ImmutableArray _interpolatedStringHandlerArguments = ImmutableArray.Empty; + public ImmutableArray InterpolatedStringHandlerArguments + { + get + { + VerifySealed(expected: true); + return _interpolatedStringHandlerArguments; + } + set + { + VerifySealed(expected: false); + _interpolatedStringHandlerArguments = value; + SetDataStored(); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/TypeWellKnownEarlyAttributeData.cs b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/TypeWellKnownEarlyAttributeData.cs new file mode 100644 index 0000000000000..a47de9345c26b --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Attributes/WellKnownAttributeData/TypeWellKnownEarlyAttributeData.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. + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// Information decoded early from well-known custom attributes applied on a type. + /// + internal sealed class TypeEarlyWellKnownAttributeData : CommonTypeEarlyWellKnownAttributeData + { + #region InterpolatedStringHandlerAttribute + private bool _hasInterpolatedStringHandlerAttribute; + public bool HasInterpolatedStringHandlerAttribute + { + get + { + VerifySealed(expected: true); + return _hasInterpolatedStringHandlerAttribute; + } + set + { + VerifySealed(expected: false); + _hasInterpolatedStringHandlerAttribute = value; + SetDataStored(); + } + } + #endregion + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs index 004f5ce10266d..df62ab4c46572 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs @@ -431,6 +431,8 @@ public sealed override bool MightContainExtensionMethods internal override bool HasCodeAnalysisEmbeddedAttribute => false; + internal override bool IsInterpolatedStringHandlerType => false; + internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList? basesBeingResolved) { return ImmutableArray.Empty; diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs index 7cb664a1fafd4..48c449aaef7e8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerParameterSymbol.cs @@ -78,5 +78,7 @@ internal int MethodHashCode() internal override bool IsCallerMemberName => false; internal override FlowAnalysisAnnotations FlowAnalysisAnnotations => FlowAnalysisAnnotations.None; internal override ImmutableHashSet NotNullIfParameterNotNull => ImmutableHashSet.Empty; + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => ImmutableArray.Empty; + internal override bool HasInterpolatedStringHandlerArgumentError => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 94efbb4550f53..14265e8e29469 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -141,6 +141,7 @@ private class UncommonProperties internal string lazyDefaultMemberName; internal NamedTypeSymbol lazyComImportCoClassType = ErrorTypeSymbol.UnknownResultType; internal ThreeState lazyHasEmbeddedAttribute = ThreeState.Unknown; + internal ThreeState lazyHasInterpolatedStringHandlerAttribute = ThreeState.Unknown; #if DEBUG internal bool IsDefaultValue() @@ -154,7 +155,8 @@ internal bool IsDefaultValue() !lazyContainsExtensionMethods.HasValue() && lazyDefaultMemberName == null && (object)lazyComImportCoClassType == (object)ErrorTypeSymbol.UnknownResultType && - !lazyHasEmbeddedAttribute.HasValue(); + !lazyHasEmbeddedAttribute.HasValue() && + !lazyHasInterpolatedStringHandlerAttribute.HasValue(); } #endif } @@ -384,6 +386,24 @@ internal TypeDefinitionHandle Handle } } + internal sealed override bool IsInterpolatedStringHandlerType + { + get + { + var uncommon = GetUncommonProperties(); + if (uncommon == s_noUncommonProperties) + { + return false; + } + + if (!uncommon.lazyHasInterpolatedStringHandlerAttribute.HasValue()) + { + uncommon.lazyHasInterpolatedStringHandlerAttribute = ContainingPEModule.Module.HasInterpolatedStringHandlerAttribute(_handle).ToThreeState(); + } + + return uncommon.lazyHasInterpolatedStringHandlerAttribute.Value(); + } + } internal override bool HasCodeAnalysisEmbeddedAttribute { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs index 09d5e081f6bbd..c2241f49a337e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Runtime.InteropServices; @@ -141,6 +142,9 @@ public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value) private ConstantValue _lazyDefaultValue = ConstantValue.Unset; private ThreeState _lazyIsParams; + private static readonly ImmutableArray s_defaultStringHandlerAttributeIndexes = ImmutableArray.Create(int.MinValue); + private ImmutableArray _lazyInterpolatedStringHandlerAttributeIndexes = s_defaultStringHandlerAttributeIndexes; + /// /// Attributes filtered out from m_lazyCustomAttributes, ParamArray, etc. /// @@ -702,6 +706,95 @@ private static FlowAnalysisAnnotations DecodeFlowAnalysisAttributes(PEModule mod return annotations; } +#nullable enable + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes + { + get + { + EnsureInterpolatedStringHandlerArgumentAttributeDecoded(); + ImmutableArray indexes = _lazyInterpolatedStringHandlerAttributeIndexes; + Debug.Assert(indexes != s_defaultStringHandlerAttributeIndexes); + return indexes.NullToEmpty(); + } + } + + internal override bool HasInterpolatedStringHandlerArgumentError + { + get + { + EnsureInterpolatedStringHandlerArgumentAttributeDecoded(); + ImmutableArray indexes = _lazyInterpolatedStringHandlerAttributeIndexes; + Debug.Assert(indexes != s_defaultStringHandlerAttributeIndexes); + return indexes.IsDefault; + } + } + + private void EnsureInterpolatedStringHandlerArgumentAttributeDecoded() + { + ImmutableArray indexes = _lazyInterpolatedStringHandlerAttributeIndexes; + if (indexes == s_defaultStringHandlerAttributeIndexes) + { + indexes = DecodeInterpolatedStringHandlerArgumentAttribute(); + Debug.Assert(indexes != s_defaultStringHandlerAttributeIndexes); + var initialized = ImmutableInterlocked.InterlockedCompareExchange(ref _lazyInterpolatedStringHandlerAttributeIndexes, value: indexes, comparand: s_defaultStringHandlerAttributeIndexes); + Debug.Assert(initialized == s_defaultStringHandlerAttributeIndexes || indexes == initialized || indexes.SequenceEqual(initialized)); + } + } + + private ImmutableArray DecodeInterpolatedStringHandlerArgumentAttribute() + { + var (paramNames, hasAttribute) = _moduleSymbol.Module.GetInterpolatedStringHandlerArgumentAttributeValues(_handle); + + if (!hasAttribute) + { + return ImmutableArray.Empty; + } + else if (paramNames.IsDefault || Type is not NamedTypeSymbol { IsInterpolatedStringHandlerType: true }) + { + return default; + } + + if (paramNames.IsEmpty) + { + return ImmutableArray.Empty; + } + + var builder = ArrayBuilder.GetInstance(paramNames.Length); + var parameters = ContainingSymbol.GetParameters(); + + foreach (var name in paramNames) + { + switch (name) + { + case null: + case "" when !ContainingSymbol.RequiresInstanceReceiver() || ContainingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor }: + // Invalid data, bail + builder.Free(); + return default; + + case "": + builder.Add(BoundInterpolatedStringArgumentPlaceholder.InstanceParameter); + break; + + default: + var param = parameters.FirstOrDefault(static (p, name) => string.Equals(p.Name, name, StringComparison.Ordinal), name); + if (param is not null && (object)param != this) + { + builder.Add(param.Ordinal); + break; + } + else + { + builder.Free(); + return default; + } + } + } + + return builder.ToImmutableAndFree(); + } +#nullable disable + internal override ImmutableHashSet NotNullIfParameterNotNull { get diff --git a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs index 05afb5f6ca28d..e43f99c4c718b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamedTypeSymbol.cs @@ -1009,6 +1009,11 @@ internal NamedTypeSymbol GetUnboundGenericTypeOrSelf() /// internal abstract bool HasCodeAnalysisEmbeddedAttribute { get; } + /// + /// Gets a value indicating whether this type has System.Runtime.CompilerServices.InterpolatedStringHandlerAttribute or not. + /// + internal abstract bool IsInterpolatedStringHandlerType { get; } + internal static readonly Func TypeWithAnnotationsIsNullFunction = type => !type.HasType; internal static readonly Func TypeWithAnnotationsIsErrorType = type => type.HasType && type.Type.IsErrorType(); diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index 023ede47768fa..ad4ebf2417df2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -376,6 +376,10 @@ internal NativeIntegerParameterSymbol(NativeIntegerTypeSymbol containingType, Na public override ImmutableArray RefCustomModifiers => _underlyingParameter.RefCustomModifiers; + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => _underlyingParameter.InterpolatedStringHandlerArgumentIndexes; + + internal override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError; + public override bool Equals(Symbol? other, TypeCompareKind comparison) => NativeIntegerTypeSymbol.EqualsHelper(this, other, comparison, symbol => symbol._underlyingParameter); public override int GetHashCode() => _underlyingParameter.GetHashCode(); diff --git a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs index 7ffdfa34258ac..4e56394c77de7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ParameterSymbol.cs @@ -399,6 +399,22 @@ internal sealed override ObsoleteAttributeData ObsoleteAttributeData internal abstract ImmutableHashSet NotNullIfParameterNotNull { get; } + /// + /// Indexes of the parameters that will be passed to the constructor of the interpolated string handler type + /// when an interpolated string handler conversion occurs. These indexes are ordered in the order to be passed + /// to the constructor. + /// + /// Indexes greater than or equal to 0 are references to parameters defined on the containing method or indexer. + /// Indexes less than 0 are constants defined on . + /// + internal abstract ImmutableArray InterpolatedStringHandlerArgumentIndexes { get; } + + /// + /// True if the parameter is attributed with InterpolatedStringHandlerArgumentAttribute and the attribute + /// has some error (such as invalid names). + /// + internal abstract bool HasInterpolatedStringHandlerArgumentError { get; } + protected sealed override int HighestPriorityUseSiteError { get diff --git a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs index e21bad8622851..2c68e1d6d79b5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReducedExtensionMethodSymbol.cs @@ -621,6 +621,10 @@ public override ImmutableArray RefCustomModifiers } } + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable; + + internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable; + public sealed override bool Equals(Symbol obj, TypeCompareKind compareKind) { if ((object)this == obj) diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs index 0bc0df65a9f2c..3f146256ab6c7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs @@ -122,6 +122,10 @@ internal override ImmutableArray MarshallingDescriptor { get { return null; } } + + internal sealed override ImmutableArray InterpolatedStringHandlerArgumentIndexes => _underlyingParameter.InterpolatedStringHandlerArgumentIndexes; + + internal override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError; } internal sealed class RetargetingMethodParameterSymbol : RetargetingParameterSymbol diff --git a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs index 06a6293693491..e11ab8d783161 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SignatureOnlyParameterSymbol.cs @@ -88,6 +88,10 @@ public SignatureOnlyParameterSymbol( internal override ModuleSymbol ContainingModule { get { throw ExceptionUtilities.Unreachable; } } + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable; + + internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable; + #endregion Not used by MethodSignatureComparer public override bool Equals(Symbol obj, TypeCompareKind compareKind) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs index b1d0f3322af98..8a64f4b4317f0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs @@ -165,6 +165,8 @@ internal override ObsoleteAttributeData ObsoleteAttributeData internal override bool HasCodeAnalysisEmbeddedAttribute => false; + internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable; internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SimpleProgramNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SimpleProgramNamedTypeSymbol.cs index b4c8955fa13c0..64862a1ae150e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SimpleProgramNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SimpleProgramNamedTypeSymbol.cs @@ -193,6 +193,8 @@ internal override ObsoleteAttributeData? ObsoleteAttributeData internal override bool HasCodeAnalysisEmbeddedAttribute => false; + internal override bool IsInterpolatedStringHandlerType => false; + protected override MembersAndInitializers BuildMembersAndInitializers(BindingDiagnosticBag diagnostics) { bool reportAnError = false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs index da998b80f9574..1185683ef784e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceClonedParameterSymbol.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -164,6 +165,10 @@ internal override ImmutableHashSet NotNullIfParameterNotNull get { return ImmutableHashSet.Empty; } } + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable; + + internal override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable; + #endregion } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 794cf7baeeadf..974c325e7295a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -7,9 +7,11 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -129,6 +131,12 @@ private bool HasCallerMemberNameAttribute && !HasCallerFilePathAttribute && HasCallerMemberNameAttribute; + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes + => (GetDecodedWellKnownAttributeData()?.InterpolatedStringHandlerArguments).NullToEmpty(); + + internal override bool HasInterpolatedStringHandlerArgumentError + => GetDecodedWellKnownAttributeData()?.InterpolatedStringHandlerArguments.IsDefault ?? false; + internal override FlowAnalysisAnnotations FlowAnalysisAnnotations { get @@ -641,6 +649,7 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu var attribute = arguments.Attribute; Debug.Assert(!attribute.HasErrors); Debug.Assert(arguments.SymbolPart == AttributeLocation.None); + Debug.Assert(AttributeDescription.InterpolatedStringHandlerArgumentAttribute.Signatures.Length == 2); var diagnostics = (BindingDiagnosticBag)arguments.Diagnostics; if (attribute.IsTargetAttribute(this, AttributeDescription.DefaultParameterValueAttribute)) @@ -746,6 +755,10 @@ internal override void DecodeWellKnownAttribute(ref DecodeWellKnownAttributeArgu arguments.GetOrCreateData().HasEnumeratorCancellationAttribute = true; ValidateCancellationTokenAttribute(arguments.AttributeSyntaxOpt, (BindingDiagnosticBag)arguments.Diagnostics); } + else if (attribute.GetTargetAttributeSignatureIndex(this, AttributeDescription.InterpolatedStringHandlerArgumentAttribute) is (0 or 1) and var index) + { + DecodeInterpolatedStringHandlerArgumentAttribute(ref arguments, diagnostics, index); + } } private static bool? DecodeMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(AttributeDescription description, CSharpAttributeData attribute) @@ -1048,6 +1061,142 @@ bool needsReporting() } } +#nullable enable + private void DecodeInterpolatedStringHandlerArgumentAttribute(ref DecodeWellKnownAttributeArguments arguments, BindingDiagnosticBag diagnostics, int attributeIndex) + { + Debug.Assert(attributeIndex is 0 or 1); + Debug.Assert(arguments.Attribute.IsTargetAttribute(this, AttributeDescription.InterpolatedStringHandlerArgumentAttribute) && arguments.Attribute.CommonConstructorArguments.Length == 1); + Debug.Assert(arguments.AttributeSyntaxOpt is not null); + + var errorLocation = arguments.AttributeSyntaxOpt.Location; + + if (Type is not NamedTypeSymbol { IsInterpolatedStringHandlerType: true } handlerType) + { + // '{0}' is not an interpolated string handler type. + diagnostics.Add(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, errorLocation, Type); + setInterpolatedStringHandlerAttributeError(ref arguments); + return; + } + + TypedConstant constructorArgument = arguments.Attribute.CommonConstructorArguments[0]; + + ImmutableArray containingSymbolParameters = ContainingSymbol.GetParameters(); + + ImmutableArray parameterOrdinals; + ArrayBuilder parameters; + if (attributeIndex == 0) + { + if (decodeName(constructorArgument, ref arguments) is not (int ordinal, var parameter)) + { + // If an error needs to be reported, it will already have been reported by another step. + setInterpolatedStringHandlerAttributeError(ref arguments); + return; + } + + parameterOrdinals = ImmutableArray.Create(ordinal); + parameters = ArrayBuilder.GetInstance(1); + parameters.Add(parameter); + } + else if (attributeIndex == 1) + { + bool hadError = false; + parameters = ArrayBuilder.GetInstance(constructorArgument.Values.Length); + var ordinalsBuilder = ArrayBuilder.GetInstance(constructorArgument.Values.Length); + foreach (var nestedArgument in constructorArgument.Values) + { + if (decodeName(nestedArgument, ref arguments) is (int ordinal, var parameter) && !hadError) + { + parameters.Add(parameter); + ordinalsBuilder.Add(ordinal); + } + else + { + hadError = true; + } + } + + if (hadError) + { + parameters.Free(); + ordinalsBuilder.Free(); + setInterpolatedStringHandlerAttributeError(ref arguments); + return; + } + + parameterOrdinals = ordinalsBuilder.ToImmutableAndFree(); + } + else + { + throw ExceptionUtilities.Unreachable; + } + + var parameterWellKnownAttributeData = arguments.GetOrCreateData(); + parameterWellKnownAttributeData.InterpolatedStringHandlerArguments = parameterOrdinals; + + (int Ordinal, ParameterSymbol? Parameter)? decodeName(TypedConstant constant, ref DecodeWellKnownAttributeArguments arguments) + { + Debug.Assert(arguments.AttributeSyntaxOpt is not null); + if (constant.IsNull) + { + // null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + diagnostics.Add(ErrorCode.ERR_NullInvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt.Location); + return null; + } + + if (constant.TypeInternal is not { SpecialType: SpecialType.System_String }) + { + // There has already been an error reported. Just return null. + return null; + } + + var name = constant.DecodeValue(SpecialType.System_String); + Debug.Assert(name != null); + if (name == "") + { + // Name refers to the "this" instance parameter. + if (!ContainingSymbol.RequiresInstanceReceiver() || ContainingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor }) + { + // '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + diagnostics.Add(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt.Location, ContainingSymbol); + return null; + } + + return (BoundInterpolatedStringArgumentPlaceholder.InstanceParameter, null); + } + + var parameter = containingSymbolParameters.FirstOrDefault(static (param, name) => string.Equals(param.Name, name, StringComparison.Ordinal), name); + if (parameter is null) + { + // '{0}' is not a valid parameter name from '{1}'. + diagnostics.Add(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, arguments.AttributeSyntaxOpt.Location, name, ContainingSymbol); + return null; + } + + if ((object)parameter == this) + { + // InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + diagnostics.Add(ErrorCode.ERR_CannotUseSelfAsInterpolatedStringHandlerArgument, errorLocation); + return null; + } + + if (parameter.Ordinal > Ordinal) + { + // Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. + // This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated + // string handler parameter after all arguments involved. + diagnostics.Add(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, errorLocation, parameter.Name, this); + } + + return (parameter.Ordinal, parameter); + } + + static void setInterpolatedStringHandlerAttributeError(ref DecodeWellKnownAttributeArguments arguments) + { + arguments.GetOrCreateData().InterpolatedStringHandlerArguments = default; + } + } +#nullable disable + internal override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { Debug.Assert(!boundAttributes.IsDefault); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index 7cf65647d90a8..b983626e0e04b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -2957,8 +2957,9 @@ private void AddDeclaredNontypeMembers(DeclaredMembersAndInitializersBuilder bui break; case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: // The members of a global anonymous type is in a syntax tree of a namespace declaration or a compilation unit. - AddNonTypeMembers(builder, ((NamespaceDeclarationSyntax)syntax).Members, diagnostics); + AddNonTypeMembers(builder, ((BaseNamespaceDeclarationSyntax)syntax).Members, diagnostics); break; case SyntaxKind.CompilationUnit: @@ -4487,8 +4488,9 @@ private void AddNonTypeMembers( default: Debug.Assert( SyntaxFacts.IsTypeDeclaration(m.Kind()) || - m.Kind() == SyntaxKind.NamespaceDeclaration || - m.Kind() == SyntaxKind.IncompleteMember); + m.Kind() is SyntaxKind.NamespaceDeclaration or + SyntaxKind.FileScopedNamespaceDeclaration or + SyntaxKind.IncompleteMember); break; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 712d6ca063559..c513b6cb48dfa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -847,13 +847,14 @@ private TypeWellKnownAttributeData GetDecodedWellKnownAttributeData() return (TypeWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData; } +#nullable enable /// /// Returns data decoded from special early bound well-known attributes applied to the symbol or null if there are no applied attributes. /// /// /// Forces binding and decoding of attributes. /// - internal CommonTypeEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttributeData() + internal TypeEarlyWellKnownAttributeData? GetEarlyDecodedWellKnownAttributeData() { var attributesBag = _lazyCustomAttributesBag; if (attributesBag == null || !attributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) @@ -861,10 +862,10 @@ internal CommonTypeEarlyWellKnownAttributeData GetEarlyDecodedWellKnownAttribute attributesBag = this.GetAttributesBag(); } - return (CommonTypeEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData; + return (TypeEarlyWellKnownAttributeData)attributesBag.EarlyDecodedWellKnownAttributeData; } - internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) + internal override CSharpAttributeData? EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) { bool hasAnyDiagnostics; CSharpAttributeData boundAttribute; @@ -874,7 +875,7 @@ internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDec boundAttribute = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); if (!boundAttribute.HasErrors) { - arguments.GetOrCreateData().HasComImportAttribute = true; + arguments.GetOrCreateData().HasComImportAttribute = true; if (!hasAnyDiagnostics) { return boundAttribute; @@ -889,7 +890,7 @@ internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDec boundAttribute = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); if (!boundAttribute.HasErrors) { - arguments.GetOrCreateData().HasCodeAnalysisEmbeddedAttribute = true; + arguments.GetOrCreateData().HasCodeAnalysisEmbeddedAttribute = true; if (!hasAnyDiagnostics) { return boundAttribute; @@ -904,8 +905,8 @@ internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDec boundAttribute = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); if (!boundAttribute.HasErrors) { - string name = boundAttribute.GetConstructorArgument(0, SpecialType.System_String); - arguments.GetOrCreateData().AddConditionalSymbol(name); + string? name = boundAttribute.GetConstructorArgument(0, SpecialType.System_String); + arguments.GetOrCreateData().AddConditionalSymbol(name); if (!hasAnyDiagnostics) { return boundAttribute; @@ -920,7 +921,7 @@ internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDec { if (obsoleteData != null) { - arguments.GetOrCreateData().ObsoleteAttributeData = obsoleteData; + arguments.GetOrCreateData().ObsoleteAttributeData = obsoleteData; } return boundAttribute; @@ -934,7 +935,7 @@ internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDec AttributeUsageInfo info = this.DecodeAttributeUsageAttribute(boundAttribute, arguments.AttributeSyntax, diagnose: false); if (!info.IsNull) { - var typeData = arguments.GetOrCreateData(); + var typeData = arguments.GetOrCreateData(); if (typeData.AttributeUsageInfo.IsNull) { typeData.AttributeUsageInfo = info; @@ -950,14 +951,38 @@ internal override CSharpAttributeData EarlyDecodeWellKnownAttribute(ref EarlyDec return null; } + // We want to decode this early because it can influence overload resolution, which could affect attribute binding itself. Consider an attribute with these + // constructors: + // + // MyAttribute(string s) + // MyAttribute(CustomBuilder c) // CustomBuilder has InterpolatedStringHandlerAttribute on the type + // + // If it's applied with [MyAttribute($"{1}")], overload resolution rules say that we should prefer the CustomBuilder overload over the string overload. This + // is an error scenario regardless (non-constant interpolated string), but it's good to get right as it will affect public API results. + if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.InterpolatedStringHandlerAttribute)) + { + boundAttribute = arguments.Binder.GetAttribute(arguments.AttributeSyntax, arguments.AttributeType, out hasAnyDiagnostics); + if (!boundAttribute.HasErrors) + { + arguments.GetOrCreateData().HasInterpolatedStringHandlerAttribute = true; + if (!hasAnyDiagnostics) + { + return boundAttribute; + } + } + + return null; + } + return base.EarlyDecodeWellKnownAttribute(ref arguments); } +#nullable disable internal override AttributeUsageInfo GetAttributeUsageInfo() { Debug.Assert(this.SpecialType == SpecialType.System_Object || this.DeclaringCompilation.IsAttributeType(this)); - CommonTypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData(); + TypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData(); if (data != null && !data.AttributeUsageInfo.IsNull) { return data.AttributeUsageInfo; @@ -977,7 +1002,7 @@ internal override ObsoleteAttributeData ObsoleteAttributeData var lazyCustomAttributesBag = _lazyCustomAttributesBag; if (lazyCustomAttributesBag != null && lazyCustomAttributesBag.IsEarlyDecodedWellKnownAttributeDataComputed) { - var data = (CommonTypeEarlyWellKnownAttributeData)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData; + var data = (TypeEarlyWellKnownAttributeData)lazyCustomAttributesBag.EarlyDecodedWellKnownAttributeData; return data != null ? data.ObsoleteAttributeData : null; } @@ -1202,7 +1227,7 @@ internal override bool IsComImport { get { - CommonTypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData(); + TypeEarlyWellKnownAttributeData data = this.GetEarlyDecodedWellKnownAttributeData(); return data != null && data.HasComImportAttribute; } } @@ -1257,6 +1282,11 @@ internal override bool HasCodeAnalysisEmbeddedAttribute } } +#nullable enable + internal sealed override bool IsInterpolatedStringHandlerType + => GetEarlyDecodedWellKnownAttributeData()?.HasInterpolatedStringHandlerAttribute == true; +#nullable disable + internal sealed override bool ShouldAddWinRTMembers { get { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs index ccb5b049a620d..3d608ade18e44 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.AliasesAndUsings.cs @@ -43,7 +43,7 @@ public Imports GetImports(CSharpSyntaxNode declarationSyntax, ConsList GetExternAliases(CSharpSynta } break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: if (!namespaceDecl.Externs.Any()) { #if DEBUG @@ -143,7 +143,7 @@ public ImmutableArray GetUsingAliases(CSharpSyntaxNode d } break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: if (!namespaceDecl.Usings.Any()) { #if DEBUG @@ -175,7 +175,7 @@ public ImmutableDictionary GetUsingAliasesMap(CS } break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: if (!namespaceDecl.Usings.Any()) { #if DEBUG @@ -207,7 +207,7 @@ public ImmutableArray GetUsingNamespacesOrType } break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: if (!namespaceDecl.Usings.Any()) { #if DEBUG @@ -420,7 +420,7 @@ private ExternAliasesAndDiagnostics GetExternAliasesAndDiagnostics(SourceNamespa externAliasDirectives = compilationUnit.Externs; break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: externAliasDirectives = namespaceDecl.Externs; break; @@ -549,7 +549,7 @@ private UsingsAndDiagnostics GetUsingsAndDiagnostics(ref UsingsAndDiagnostics? u usingDirectives = compilationUnit.Usings; break; - case NamespaceDeclarationSyntax namespaceDecl: + case BaseNamespaceDeclarationSyntax namespaceDecl: Debug.Assert(!onlyGlobal); applyIsGlobalFilter = null; // Global Using directives are not allowed in namespaces, treat them as regular, an error is reported elsewhere. usingDirectives = namespaceDecl.Usings; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs index 8599e760f0a5c..e45ee440e6543 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceSimpleParameterSymbol.cs @@ -86,6 +86,10 @@ internal override bool IsCallerMemberName get { return false; } } + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => ImmutableArray.Empty; + + internal override bool HasInterpolatedStringHandlerArgumentError => false; + internal override FlowAnalysisAnnotations FlowAnalysisAnnotations { get { return FlowAnalysisAnnotations.None; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs index 334dbd884062e..193ef06eec568 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ThisParameterSymbol.cs @@ -162,5 +162,9 @@ internal override MarshalPseudoCustomAttributeData MarshallingInformation { get { return null; } } + + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => ImmutableArray.Empty; + + internal override bool HasInterpolatedStringHandlerArgumentError => false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs index 00422c0fab1c2..27f82c713cea3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs @@ -67,6 +67,9 @@ public override TypeWithAnnotations TypeWithAnnotations } } + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => _underlyingParameter.InterpolatedStringHandlerArgumentIndexes; + + internal override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError; public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 4858cf03c15bf..0e17882f9f76c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -320,7 +320,7 @@ internal virtual LexicalSortKey GetLexicalSortKey() /// /// Note that for namespace symbol, the declaring syntax might be declaring a nested /// namespace. For example, the declaring syntax node for N1 in "namespace N1.N2 {...}" is - /// the entire for N1.N2. For the global namespace, the declaring + /// the entire for N1.N2. For the global namespace, the declaring /// syntax will be the . /// /// @@ -889,7 +889,7 @@ internal bool HasUseSiteError } /// - /// Returns diagnostic info that should be reported at the use site of the symbol, or null if there is none. + /// Returns diagnostic info that should be reported at the use site of the symbol, or default if there is none. /// internal virtual UseSiteInfo GetUseSiteInfo() { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/InterpolatedStringBuilderLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/InterpolatedStringBuilderLocalSymbol.cs new file mode 100644 index 0000000000000..4804f30babf2c --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/InterpolatedStringBuilderLocalSymbol.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// A synthesized local variable with a val escape scope. + /// + [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] + internal sealed class SynthesizedLocalWithValEscape : SynthesizedLocal + { + public SynthesizedLocalWithValEscape( + MethodSymbol? containingMethod, + TypeWithAnnotations typeWithAnnotations, + SynthesizedLocalKind kind, + uint valEscapeScope, + SyntaxNode? syntaxOpt = null, + bool isPinned = false, + RefKind refKind = RefKind.None +#if DEBUG + , + [CallerLineNumber] int createdAtLineNumber = 0, + [CallerFilePath] string? createdAtFilePath = null +#endif + ) : base(containingMethod, typeWithAnnotations, kind, syntaxOpt, isPinned, refKind +#if DEBUG + , createdAtLineNumber, createdAtFilePath +#endif + ) + { + ValEscapeScope = valEscapeScope; + } + + internal override uint ValEscapeScope { get; } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs index 35dcd9f5949fa..5ddc18abecfce 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordClone.cs @@ -97,8 +97,8 @@ static bool modifiersAreValid(DeclarationModifiers modifiers) protected override (TypeWithAnnotations ReturnType, ImmutableArray Parameters, bool IsVararg, ImmutableArray DeclaredConstraintsForOverrideOrImplementation) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics) { - return (ReturnType: VirtualCloneInBase() is { } baseClone ? - baseClone.ReturnTypeWithAnnotations : // Use covariant returns when available + return (ReturnType: !ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses && VirtualCloneInBase() is { } baseClone ? + baseClone.ReturnTypeWithAnnotations : TypeWithAnnotations.Create(isNullableEnabled: true, ContainingType), Parameters: ImmutableArray.Empty, IsVararg: false, diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index 7d424e0ca1f77..49c32f1f43a57 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -130,6 +130,8 @@ internal override ImmutableArray TypeArgumentsWithAnnotatio internal override bool HasCodeAnalysisEmbeddedAttribute => false; + internal sealed override bool IsInterpolatedStringHandlerType => false; + public override ImmutableArray GetMembers() { Symbol constructor = this.Constructor; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs index c206668864d31..91fdaa6613923 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs @@ -99,6 +99,8 @@ public SynthesizedEmbeddedAttributeSymbolBase( internal override bool HasCodeAnalysisEmbeddedAttribute => true; + internal override bool IsInterpolatedStringHandlerType => false; + internal override bool HasSpecialName => false; internal override bool IsComImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs index aabe1ed5ce55b..531f224911171 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedLocal.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// A synthesized local variable. /// [DebuggerDisplay("{GetDebuggerDisplay(),nq}")] - internal sealed class SynthesizedLocal : LocalSymbol + internal class SynthesizedLocal : LocalSymbol { private readonly MethodSymbol _containingMethodOpt; private readonly TypeWithAnnotations _type; @@ -67,7 +67,7 @@ public SyntaxNode SyntaxOpt get { return _syntaxOpt; } } - internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) + internal sealed override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocalKind kind, SyntaxNode syntax) { return new SynthesizedLocal( _containingMethodOpt, @@ -78,78 +78,78 @@ internal override LocalSymbol WithSynthesizedLocalKindAndSyntax(SynthesizedLocal _refKind); } - public override RefKind RefKind + public sealed override RefKind RefKind { get { return _refKind; } } - internal override bool IsImportedFromMetadata + internal sealed override bool IsImportedFromMetadata { get { return false; } } - internal override LocalDeclarationKind DeclarationKind + internal sealed override LocalDeclarationKind DeclarationKind { get { return LocalDeclarationKind.None; } } - internal override SynthesizedLocalKind SynthesizedKind + internal sealed override SynthesizedLocalKind SynthesizedKind { get { return _kind; } } - internal override SyntaxNode ScopeDesignatorOpt + internal sealed override SyntaxNode ScopeDesignatorOpt { get { return null; } } - internal override SyntaxToken IdentifierToken + internal sealed override SyntaxToken IdentifierToken { get { return default(SyntaxToken); } } - public override Symbol ContainingSymbol + public sealed override Symbol ContainingSymbol { get { return _containingMethodOpt; } } - public override string Name + public sealed override string Name { get { return null; } } - public override TypeWithAnnotations TypeWithAnnotations + public sealed override TypeWithAnnotations TypeWithAnnotations { get { return _type; } } - public override ImmutableArray Locations + public sealed override ImmutableArray Locations { get { return (_syntaxOpt == null) ? ImmutableArray.Empty : ImmutableArray.Create(_syntaxOpt.GetLocation()); } } - public override ImmutableArray DeclaringSyntaxReferences + public sealed override ImmutableArray DeclaringSyntaxReferences { get { return (_syntaxOpt == null) ? ImmutableArray.Empty : ImmutableArray.Create(_syntaxOpt.GetReference()); } } - internal override SyntaxNode GetDeclaratorSyntax() + internal sealed override SyntaxNode GetDeclaratorSyntax() { Debug.Assert(_syntaxOpt != null); return _syntaxOpt; } - public override bool IsImplicitlyDeclared + public sealed override bool IsImplicitlyDeclared { get { return true; } } - internal override bool IsPinned + internal sealed override bool IsPinned { get { return _isPinned; } } - internal override bool IsCompilerGenerated + internal sealed override bool IsCompilerGenerated { get { return true; } } @@ -164,14 +164,14 @@ internal override bool IsCompilerGenerated /// Compiler should always be synthesizing locals with correct escape semantics. /// Checking escape scopes is not valid here. /// - internal override uint RefEscapeScope => throw ExceptionUtilities.Unreachable; + internal sealed override uint RefEscapeScope => throw ExceptionUtilities.Unreachable; - internal override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, BindingDiagnosticBag diagnostics) + internal sealed override ConstantValue GetConstantValue(SyntaxNode node, LocalSymbol inProgress, BindingDiagnosticBag diagnostics) { return null; } - internal override ImmutableBindingDiagnostic GetConstantValueDiagnostics(BoundExpression boundInitValue) + internal sealed override ImmutableBindingDiagnostic GetConstantValueDiagnostics(BoundExpression boundInitValue) { return ImmutableBindingDiagnostic.Empty; } @@ -193,7 +193,7 @@ internal string DumperString() } #endif - internal override string GetDebuggerDisplay() + internal sealed override string GetDebuggerDisplay() { var builder = new StringBuilder(); builder.Append('<'); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs index 64ced300122d1..03a40075009e3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedParameterSymbol.cs @@ -170,6 +170,10 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); } } + + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => ImmutableArray.Empty; + + internal override bool HasInterpolatedStringHandlerArgumentError => false; } internal sealed class SynthesizedParameterSymbol : SynthesizedParameterSymbolBase diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs index c2531dae3f9ee..48a0389a946be 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs @@ -192,6 +192,8 @@ internal override bool IsMetadataSealed internal override bool HasCodeAnalysisEmbeddedAttribute => _underlyingType.HasCodeAnalysisEmbeddedAttribute; + internal override bool IsInterpolatedStringHandlerType => _underlyingType.IsInterpolatedStringHandlerType; + internal override ObsoleteAttributeData ObsoleteAttributeData { get { return _underlyingType.ObsoleteAttributeData; } diff --git a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs index 2b94b2d586928..e2ec607c57024 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LookupPosition.cs @@ -167,6 +167,13 @@ internal static bool IsInNamespaceDeclaration(int position, NamespaceDeclaration return IsBetweenTokens(position, namespaceDecl.NamespaceKeyword, namespaceDecl.CloseBraceToken); } + internal static bool IsInNamespaceDeclaration(int position, FileScopedNamespaceDeclarationSyntax namespaceDecl) + { + Debug.Assert(namespaceDecl != null); + + return position >= namespaceDecl.SpanStart; + } + internal static bool IsInConstructorParameterScope(int position, ConstructorDeclarationSyntax constructorDecl) { Debug.Assert(constructorDecl != null); diff --git a/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs b/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs index 10aae6d6b8710..2bc80f592d079 100644 --- a/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs +++ b/src/Compilers/CSharp/Portable/Syntax/NamespaceDeclarationSyntaxReference.cs @@ -37,7 +37,7 @@ internal static SyntaxNode GetSyntax(SyntaxReference reference, CancellationToke node = node.Parent; } - Debug.Assert(node is CompilationUnitSyntax || node is NamespaceDeclarationSyntax); + Debug.Assert(node is CompilationUnitSyntax || node is BaseNamespaceDeclarationSyntax); return node; } diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index b8cabf7960bcb..452c34af0795f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -3044,20 +3044,29 @@ - + + + + + + + + + + - + - + - - - + + + @@ -3068,6 +3077,21 @@ + + + + + + + + + + + + + + + Class representing one or more attributes applied to a language construct. diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index 24e70583c7166..52224a85215a4 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -735,6 +735,7 @@ public enum SyntaxKind : ushort NamespaceDeclaration = 8842, UsingDirective = 8843, ExternAliasDirective = 8844, + FileScopedNamespaceDeclaration = 8845, // attributes AttributeList = 8847, diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 9b51d4afad7d7..59a3fbece6123 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -354,7 +354,9 @@ public static bool IsTypeDeclaration(SyntaxKind kind) } public static bool IsNamespaceMemberDeclaration(SyntaxKind kind) - => IsTypeDeclaration(kind) || (kind == SyntaxKind.NamespaceDeclaration); + => IsTypeDeclaration(kind) || + kind == SyntaxKind.NamespaceDeclaration || + kind == SyntaxKind.FileScopedNamespaceDeclaration; public static bool IsAnyUnaryExpression(SyntaxKind token) { diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs index d5f482bb80f08..7378923cd60d2 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs @@ -1168,7 +1168,7 @@ private static int GetDeclarationDepth(SyntaxNode? node) int parentDepth = GetDeclarationDepth(node.Parent); - if (node.Parent.IsKind(SyntaxKind.GlobalStatement)) + if (node.Parent.Kind() is SyntaxKind.GlobalStatement or SyntaxKind.FileScopedNamespaceDeclaration) { return parentDepth; } diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 7c626606aacef..6dce0699bab19 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -247,6 +247,11 @@ Rozšiřující metoda, kde jako cíl je nastavený příjemce, se nedá použít jako cíl operátoru &. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' {0} musí odpovídat vlastnosti jenom pro inicializaci přepsaného člena {1}. @@ -432,6 +437,11 @@ Strom výrazů nesmí obsahovat výraz indexu od-do (^). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Strom výrazů možná neobsahuje vzor přístupu indexeru System.Index nebo System.Range. @@ -497,6 +507,16 @@ Událost podobná poli {0} nemůže mít modifikátor readonly. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? Příkaz foreach nejde použít pro proměnné typu {0}, protože {0} neobsahuje veřejnou definici instance nebo rozšíření pro {1}. Měli jste v úmyslu await foreach místo foreach? @@ -617,6 +637,36 @@ Vnitřní chyba v kompilátoru jazyka C# + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. {0} není platný modifikátor návratového typu ukazatele na funkci. Platné modifikátory jsou ref a ref readonly. @@ -632,6 +682,11 @@ Neplatný název algoritmu hash: {0} + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. Modifikátor {0} není platný pro tuto položku v jazyce C# {1}. Použijte prosím verzi jazyka {2} nebo vyšší. @@ -742,6 +797,11 @@ Atribut [EnumeratorCancellation] nejde použít na víc parametrů. + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Seznam parametrů může mít jenom částečná deklarace jednoho záznamu. @@ -787,11 +847,21 @@ Člen záznamu {0} musí být veřejný. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. {0} musí povolovat přepisování, protože obsahující záznam není zapečetěný. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' Očekávala se hodnota enable, disable nebo restore. @@ -1082,6 +1152,11 @@ Omezení class, struct, unmanaged, notnull a default se nedají kombinovat ani použít více než jednou a v seznamu omezení se musí zadat jako první. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. Aby se typ {0} dal použít jako konvence volání, musí být veřejný. @@ -1097,6 +1172,11 @@ {0} má atribut UnmanagedCallersOnly a nedá se převést na typ delegáta. Pro tuto metodu získejte ukazatel na funkci. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ Vytvoření objektu s cílovým typem + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ Parametr musí mít při ukončení hodnotu jinou než null, protože parametr, na který se odkazuje NotNullIfNotNull není null + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' {0} definuje Equals, ale ne GetHashCode. @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Obor názvů nemůže přímo obsahovat členy, jako jsou pole a metody. + A namespace cannot directly contain members such as fields, methods or statements + Obor názvů nemůže přímo obsahovat členy, jako jsou pole a metody. @@ -9239,11 +9339,6 @@ Potlačení upozornění zvažte jenom v případě, když určitě nechcete če Optimální přetěžovaná metoda Add {0} pro element inicializátoru kolekce je zastaralá. {1} - - Yield statements may not appear at the top level in interactive code. - Příkazy yield se nesmí objevit na horní úrovni v interaktivním kódu. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. Atribut zabezpečení {0} není platný u tohoto typu deklarace. Atributy zabezpečení jsou platné jenom u deklarací sestavení, typu a metody. @@ -10772,11 +10867,6 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference Aby se typ {0} mohl použít jako AsyncMethodBuilder pro typ {1}, měla by jeho vlastnost Task vracet typ {1} místo typu {2}. - - Attributes are not allowed on local function parameters or type parameters - V parametrech místní funkce nebo v parametrech typu se atributy nepovolují. - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. Modul {0} v sestavení {1} předává typ {2} několika sestavením: {3} a {4}. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 0b71f878d980f..394976f076936 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -247,6 +247,11 @@ Eine Erweiterungsmethode mit einem Empfänger kann nicht als Ziel eines &-Operators verwendet werden. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' "{0}" muss mit der init-Zugriffsmethode des außer Kraft gesetzten Members "{1}" übereinstimmen. @@ -432,6 +437,11 @@ Eine Ausdrucksbaumstruktur darf keinen vom Ende ausgehenden Indexausdruck ("^") enthalten. + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Eine Ausdrucksbaumstruktur darf keinen System.Index- oder System.Range-Musterindexerzugriff enthalten. @@ -497,6 +507,16 @@ Ein feldähnliches Ereignis "{0}" darf nicht "readonly" sein. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? Eine foreach-Anweisung kann nicht für Variablen vom Typ "{0}" verwendet werden, weil "{0}" keine öffentliche Instanz- oder Erweiterungsdefinition für "{1}" enthält. Meinten Sie "await foreach" statt "foreach"? @@ -617,6 +637,36 @@ Interner Fehler im C#-Compiler. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. "{0}" ist kein gültiger Rückgabetyp-Modifizierer für Funktionszeiger. Gültige Modifizierer sind "ref" und "ref readonly". @@ -632,6 +682,11 @@ Ungültiger Name für Hashalgorithmus: "{0}" + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. Der Modifizierer "{0}" ist für dieses Element in C# {1} ungültig. Verwenden Sie Sprachversion {2} oder höher. @@ -742,6 +797,11 @@ Das Attribut [EnumeratorCancellation] kann nicht für mehrere Parameter verwendet werden. + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Nur eine partielle Deklaration eines einzelnen Datensatzes darf eine Parameterliste aufweisen. @@ -787,11 +847,21 @@ Der Datensatzmember "{0}" muss öffentlich sein. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. "{0}" muss Überschreibungen zulassen, weil der enthaltende Datensatz nicht versiegelt ist. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' "enable", "disable" oder "restore" erwartet. @@ -1082,6 +1152,11 @@ Die Einschränkungen "class", "struct", "unmanaged", "notnull" und "default" können nicht kombiniert oder dupliziert werden und müssen in der Einschränkungsliste zuerst angegeben werden. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. Der Typ "{0}" muss öffentlich sein, damit er als Aufrufkonvention verwendet werden kann. @@ -1097,6 +1172,11 @@ "{0}" ist mit dem Attribut "UnmanagedCallersOnly" versehen und kann nicht in einen Delegattyp konvertiert werden. Rufen Sie einen Funktionszeiger auf diese Methode ab. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ Objekterstellung mit Zieltyp + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ Der Parameter muss beim Beenden einen Wert ungleich NULL aufweisen, weil der von NotNullIfNotNull referenzierte Parameter nicht NULL ist. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' "{0}" definiert "Equals", aber nicht "GetHashCode". @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Member, wie z. B. Felder oder Methoden, können nicht direkt in einem Namespace enthalten sein. + A namespace cannot directly contain members such as fields, methods or statements + Member, wie z. B. Felder oder Methoden, können nicht direkt in einem Namespace enthalten sein. @@ -9239,11 +9339,6 @@ Sie sollten das Unterdrücken der Warnung nur in Betracht ziehen, wenn Sie siche Die beste überladene Add-Methode "{0}" für das Sammlungsinitialisiererelement ist veraltet. {1} - - Yield statements may not appear at the top level in interactive code. - Yield-Anweisungen dürfen nicht in der obersten Ebene von interaktivem Code enthalten sein. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. Das Sicherheitsattribut "{0}" ist für diesen Deklarationstyp nicht gültig. Sicherheitsattribute sind nur für Assembly-, Typ- und Methodendeklarationen gültig. @@ -10772,11 +10867,6 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett Damit der Typ "{0}" als "AsyncMethodBuilder" für den Typ "{1}" verwendet wird, muss seine Aufgabeneigenschaft den Typ "{1}" anstelle des Typs "{2}" zurückgeben. - - Attributes are not allowed on local function parameters or type parameters - Attribute sind bei lokalen Funktionsparametern oder Typenparametern nicht zulässig. - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. Das Modul "{0}" in der Assembly "{1}" leitet den Typ "{2}" an mehrere Assemblys weiter: "{3}" und "{4}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index b193e0dbb480e..b23615d2e4d2c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -247,6 +247,11 @@ No se puede usar un método de extensión con un receptor como destino de un operador "&". + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' "{0}" debe coincidir por solo inicialización del miembro invalidado "{1}" @@ -432,6 +437,11 @@ Un árbol de expresión no puede contener una expresión de índice del otro extremo ("^"). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Un árbol de expresión no puede contener un patrón System.Index o un acceso a indizador System.Range. @@ -497,6 +507,16 @@ El evento de tipo campo "{0}" no puede ser "readonly". + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? La instrucción foreach no puede funcionar en variables de tipo "{0}" porque "{0}" no contiene ninguna definición de extensión o instancia pública para "{1}". ¿Quiso decir “await foreach” en lugar de “foreach”? @@ -617,6 +637,36 @@ Error interno en el compilador de C#. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. "{0}" no es un modificador de tipo de valor devuelto de puntero de función válido. Los modificadores válidos son "ref" y "ref readonly". @@ -632,6 +682,11 @@ Nombre de algoritmo hash no válido: "{0}" + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. El modificador "{0}" no es válido para este elemento en C# {1}. Use la versión de lenguaje "{2}" o una posterior. @@ -742,6 +797,11 @@ El atributo [EnumeratorCancellation] no se puede usar en varios parámetros + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Solo una declaración parcial de un registro puede tener una lista de parámetros @@ -787,11 +847,21 @@ El miembro de registro "{0}" debe ser público. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. "{0}" debe permitir la invalidación porque el registro contenedor no está sellado. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' Se esperaba "enable", "disable" o "restore". @@ -1082,6 +1152,11 @@ Las restricciones "class", "struct", "unmanaged", "notnull" y "default" no se pueden combinar ni duplicar y se deben especificar en primer lugar en la lista de restricciones. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. El tipo "{0}" debe ser público para poder usarlo como convención de llamada. @@ -1097,6 +1172,11 @@ ' {0} ' tiene un atributo ' UnmanagedCallersOnly ' y no se puede convertir en un tipo de delegado. Obtenga un puntero de función a este método. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ creación de objetos con tipo de destino + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ El parámetro debe tener un valor que no sea NULL al salir porque el parámetro al que NotNullIfNotNull hace referencia no es NULL. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' "{0}" define "Equals" pero no "GetHashCode" @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Un espacio de nombres no puede contener directamente miembros como campos o métodos. + A namespace cannot directly contain members such as fields, methods or statements + Un espacio de nombres no puede contener directamente miembros como campos o métodos. @@ -9239,11 +9339,6 @@ Considere la posibilidad de suprimir la advertencia solo si tiene la seguridad d El mejor método Add sobrecargado '{0}' para el elemento inicializador de la colección está obsoleto. {1} - - Yield statements may not appear at the top level in interactive code. - Las instrucciones yield pueden no aparecer en el nivel superior del código interactivo. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. El valor '{0}' de SecurityAction no es válido en este tipo de declaración. Los atributos de seguridad solo son válidos en las declaraciones de ensamblado, de tipo y de método. @@ -10772,11 +10867,6 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe Para que el tipo "{0}" se utilice como AsyncMethodBuilder para el tipo "{1}", su propiedad Task debe devolver el tipo "{1}" en lugar del tipo "{2}". - - Attributes are not allowed on local function parameters or type parameters - No se permiten atributos en los parámetros de tipo o parámetros de funciones locales. - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. El módulo "{0}" del ensamblado "{1}" va a reenviar el tipo "{2}" a varios ensamblados: "{3}" y "{4}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 0211b5f5b179a..e448ee074be50 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -247,6 +247,11 @@ Impossible d'utiliser une méthode d'extension avec un récepteur en tant que cible d'un opérateur '&'. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}' doit correspondre par initialisation uniquement au membre substitué '{1}' @@ -432,6 +437,11 @@ Une arborescence de l'expression ne peut pas contenir d'expression d'index partant de la fin ('^'). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Une arborescence de l'expression ne peut pas contenir de modèle d'accès à l'indexeur System.Index ou System.Range @@ -497,6 +507,16 @@ L'événement de type champ '{0}' ne peut pas être 'readonly'. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? L'instruction foreach ne peut pas fonctionner sur des variables de type '{0}', car '{0}' ne contient pas de définition d'extension ou d'instance publique pour '{1}'. Vouliez-vous dire 'await foreach' plutôt que 'foreach' ? @@ -617,6 +637,36 @@ Erreur interne dans le compilateur C#. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' n'est pas un modificateur de type de retour de pointeur de fonction valide. Les modificateurs valides sont 'ref' et 'ref readonly'. @@ -632,6 +682,11 @@ Nom d'algorithme de hachage non valide : '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. Le modificateur '{0}' est non valide pour cet élément en C# {1}. Utilisez la version de langage '{2}' ou une version ultérieure. @@ -742,6 +797,11 @@ Impossible d'utiliser l'attribut [EnumeratorCancellation] sur plusieurs paramètres + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Seule une déclaration partielle d'un seul enregistrement peut avoir une liste de paramètres @@ -787,11 +847,21 @@ Le membre d'enregistrement '{0}' doit être public. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. '{0}' doit autoriser la substitution, car l'enregistrement contenant n'est pas sealed. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' 'enable', 'disable' ou 'restore' attendu @@ -1082,6 +1152,11 @@ Les contraintes 'class', 'struct', 'unmanaged', 'notnull' et 'default' ne peuvent pas être combinées ou dupliquées. De plus, elles doivent être spécifiées en premier dans la liste des contraintes. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. Le type '{0}' doit être public pour être utilisé comme convention d'appel. @@ -1097,6 +1172,11 @@ '{0}' est attribué avec 'UnmanagedCallersOnly' et ne peut pas être converti en type délégué. Obtenez un pointeur de fonction vers cette méthode. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ création d'un objet typé cible + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ Le paramètre doit avoir une valeur non null au moment de la sortie, car le paramètre référencé par NotNullIfNotNull a une valeur non null. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}' définit 'Equals' mais pas 'GetHashCode' @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Un espace de noms ne peut pas contenir directement des membres tels que des champs ou des méthodes + A namespace cannot directly contain members such as fields, methods or statements + Un espace de noms ne peut pas contenir directement des membres tels que des champs ou des méthodes @@ -9239,11 +9339,6 @@ Supprimez l'avertissement seulement si vous êtes sûr de ne pas vouloir attendr La meilleure méthode Add surchargée '{0}' pour l'élément initialiseur de collection est obsolète. {1} - - Yield statements may not appear at the top level in interactive code. - Les instructions yield ne peuvent pas figurer au niveau supérieur dans le code interactif. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. L'attribut de sécurité '{0}' n'est pas valide dans ce type de déclaration. Les attributs de sécurité ne sont valides que dans les déclarations d'assembly, de type et de méthode. @@ -10772,11 +10867,6 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Pour que le type '{0}' soit utilisé comme AsyncMethodBuilder du type '{1}', sa propriété Task doit retourner le type '{1}' à la place du type '{2}'. - - Attributes are not allowed on local function parameters or type parameters - Les attributs ne sont pas autorisés sur les paramètres de fonction locale ou les paramètres de type - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. Le module '{0}' dans l'assembly '{1}' transfère le type '{2}' à plusieurs assemblys : '{3}' et '{4}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index ef24f9d168b62..158b6c38f6640 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -247,6 +247,11 @@ Non è possibile usare un metodo di estensione con un ricevitore come destinazione di un operatore '&'. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}' deve corrispondere per sola inizializzazione del membro '{1}' di cui è stato eseguito l'override @@ -432,6 +437,11 @@ Un albero delle espressioni non può contenere un'espressione di indice from end ('^'). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Un albero delle espressioni non può contenere un accesso a indicizzatore System.Index o System.Range di criterio @@ -497,6 +507,16 @@ L'evento simile a campo '{0}' non può essere 'readonly'. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? L'istruzione foreach non può funzionare con variabili di tipo '{0}' perché '{0}' non contiene una definizione di istanza o estensione pubblica per '{1}'. Si intendeva 'await foreach' invece di 'foreach'? @@ -617,6 +637,36 @@ Si è verificato un errore interno nel compilatore C#. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' non è un modificatore di tipo restituito di puntatore a funzione valido. I modificatori validi sono 'ref' e 'ref readonly'. @@ -632,6 +682,11 @@ Il nome dell'algoritmo hash non è valido: '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. Il modificatore '{0}' non è valido per questo elemento in C# {1}. Usare la versione '{2}' o versioni successive del linguaggio. @@ -742,6 +797,11 @@ Non è possibile usare l'attributo [EnumeratorCancellation] in più parametri + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Solo una dichiarazione parziale di singolo record può includere un elenco di parametri @@ -787,11 +847,21 @@ Il membro del record '{0}' deve essere pubblico. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. '{0}' deve consentire l'override perché il record contenitore non è sealed. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' È previsto 'enable', 'disable' o 'restore' @@ -1082,6 +1152,11 @@ I vincoli 'class', 'struct', 'unmanaged', 'notnull' e 'default' non possono essere combinati o duplicati e devono essere specificati per primi nell'elenco di vincoli. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. Il tipo '{0}' deve essere pubblico per poterlo usare come convenzione di chiamata. @@ -1097,6 +1172,11 @@ '{0}', a cui è assegnato l'attributo 'UnmanagedCallersOnly', non può essere convertito in un tipo delegato. Ottenere un puntatore a funzione per questo metodo. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ creazione di oggetti con tipo di destinazione + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ Il parametro deve avere un valore non Null quando viene terminato perché il parametro a cui fa riferimento NotNullIfNotNull è non Null. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}' definisce 'Equals' ma non 'GetHashCode' @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Uno spazio dei nomi non può contenere direttamente membri come campi o metodi + A namespace cannot directly contain members such as fields, methods or statements + Uno spazio dei nomi non può contenere direttamente membri come campi o metodi @@ -9239,11 +9339,6 @@ Come procedura consigliata, è consigliabile attendere sempre la chiamata. Il miglior metodo Add di overload '{0}' per l'elemento inizializzatore di raccolta è obsoleto. {1} - - Yield statements may not appear at the top level in interactive code. - Le istruzioni yield potrebbero non apparire al primo livello del codice interattivo. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. L'attributo di sicurezza '{0}' non è valido in questo tipo di dichiarazione. Gli attributi di sicurezza sono validi solo in dichiarazioni di metodo, assembly e tipi. @@ -10772,11 +10867,6 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr La proprietà Task del tipo '{0}' da usare come elemento AsyncMethodBuilder per il tipo '{1}' deve restituire il tipo '{1}' invece di '{2}'. - - Attributes are not allowed on local function parameters or type parameters - Gli attributi non sono consentiti in parametri di funzione locali o parametri di tipo - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. Il modulo '{0}' nell'assembly '{1}' inoltra il tipo '{2}' a più assembly '{3}' e '{4}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 53f734edc7f2e..f58205c7d66a7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -247,6 +247,11 @@ レシーバーが '&' 演算子の対象となっている拡張メソッドを使用することはできません。 + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}' は、オーバーライドされたメンバー '{1}' と同じく、初期化専用である必要があります @@ -432,6 +437,11 @@ 式ツリーに、from-end インデックス ('^') 式を含めることはできません。 + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access 式ツリーに、System.Index または System.Range インデクサー アクセスのパターンを含めることはできません @@ -497,6 +507,16 @@ フィールドに類似したイベント '{0}' を 'readonly' にすることはできません。 + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? '{0}' は '{1}' のパブリック インスタンスまたは拡張機能の定義を含んでいないため、型 '{0}' の変数に対して foreach ステートメントを使用することはできません。'foreach' ではなく 'await foreach' ですか? @@ -617,6 +637,36 @@ C# コンパイラで内部エラーが発生しました。 + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' は有効な関数ポインターの戻り値の型修飾子ではありません。有効な修飾子は 'ref ' および 'ref readonly' です。 @@ -632,6 +682,11 @@ 無効なハッシュ アルゴリズム名: '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. C# {1} では、修飾子 '{0}' はこの項目に対して有効ではありません。'{2}' 以上の言語バージョンをご使用ください。 @@ -742,6 +797,11 @@ 属性 [EnumeratorCancellation] を複数のパラメーターで使用することはできません + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list 1 つのレコードの部分宣言のみがパラメーター リストを持つことができます @@ -787,11 +847,21 @@ レコード メンバー '{0}' は public でなければなりません。 + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. '{0}' ではオーバーライドを許可する必要があります。これが含まれているレコードが sealed ではないためです。 + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' 'enable'、'disable'、'restore' のいずれかが必要でした @@ -1082,6 +1152,11 @@ 'class'、'struct'、'unmanaged'、'notnull'、'default' の制約を組み合わせたり、複製したりすることはできません。これらは制約リストの最初に指定する必要があります。 + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. 呼び出し規則として使用する型 '{0}' はパブリックでなければなりません。 @@ -1097,6 +1172,11 @@ '{0}' は 'UnmanagedCallersOnly' 属性が設定されているため、デリゲート型に変換できません。このメソッドへの関数ポインターを取得してください。 UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ target-typed オブジェクトの作成 + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ NotNullIfNotNull によって参照されているパラメーターが null 以外であるため、パラメーターには終了時に null 以外の値が含まれている必要があります。 + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}' では 'Equals' が定義されていますが、'GetHashCode' は定義されていません @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - 名前空間にフィールドやメソッドのようなメンバーを直接含めることはできません + A namespace cannot directly contain members such as fields, methods or statements + 名前空間にフィールドやメソッドのようなメンバーを直接含めることはできません @@ -9239,11 +9339,6 @@ You should consider suppressing the warning only if you're sure that you don't w コレクション初期化子要素に最も適しているオーバーロード Add メソッド '{0}' は古い形式です。{1} - - Yield statements may not appear at the top level in interactive code. - Yield ステートメントは、対話型コードの最上部に表示できません。 - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. セキュリティ属性 '{0}' はこの宣言型では無効です。セキュリティ属性は、アセンブリ、型、メソッドの宣言でのみ有効です。 @@ -10772,11 +10867,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 型 '{0}' を型 '{1}' の AsyncMethodBuilder として使うには、その Task プロパティが型 '{2}' ではなく型 '{1}' を返す必要があります。 - - Attributes are not allowed on local function parameters or type parameters - ローカル関数パラメーターまたは型パラメーターに属性を使うことはできません - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. アセンブリ '{1}' のモジュール '{0}' によって、型 '{2}' が複数のアセンブリ '{3}' および '{4}' に転送されています。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 5a5636f894b69..cac56c7b486ec 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -247,6 +247,11 @@ '&' 연산자의 대상으로 수신기가 있는 확장 메서드는 사용할 수 없습니다. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}'은(는) 재정의된 멤버 '{1}'의 초기화 전용으로 일치해야 합니다. @@ -432,6 +437,11 @@ 식 트리에는 내림차순 인덱스('^') 식을 포함할 수 없습니다. + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access 식 트리에는 System.Index 또는 System.Range 패턴의 인덱서 액세스를 포함할 수 없습니다. @@ -497,6 +507,16 @@ 필드와 유사한 이벤트 '{0}'이(가) 'readonly'일 수 없습니다. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? '{0}' 형식 변수에서 foreach 문을 수행할 수 없습니다. '{0}'에는 '{1}'의 공개 인스턴스 또는 확장 정의가 없기 때문입니다. 'foreach' 대신 'await foreach'를 사용하시겠습니까? @@ -617,6 +637,36 @@ C# 컴파일러의 내부 오류입니다. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}'은(는) 유효한 함수 포인터 반환 형식 한정자가 아닙니다. 유효한 한정자는 'ref' 및 'ref readonly'입니다. @@ -632,6 +682,11 @@ 잘못된 해시 알고리즘 이름: '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. C# {1}의 이 항목에는 '{0}' 한정자가 유효하지 않습니다. 언어 버전 '{2}' 이상을 사용하세요. @@ -742,6 +797,11 @@ [EnumeratorCancellation] 특성은 여러 매개 변수에 사용할 수 없습니다. + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list 단일 레코드 partial 선언에만 매개 변수 목록을 사용할 수 있습니다. @@ -787,11 +847,21 @@ 레코드 멤버 '{0}'은(는) 퍼블릭이어야 합니다. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. 포함된 레코드가 봉인되지 않았으므로 '{0}'은(는) 재정의를 허용해야 합니다. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' 'enable', 'disable' 또는 'restore'가 필요합니다. @@ -1082,6 +1152,11 @@ 'class', 'struct', 'unmanaged', 'notnull' 및 'default' 제약 조건은 결합되거나 중복될 수 없으며 제약 조건 목록에서 먼저 지정되어야 합니다. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. 호출 규칙으로 사용하려면 '{0}' 형식이 public이어야 합니다. @@ -1097,6 +1172,11 @@ '{0}'에는 'UnmanagedCallersOnly' 특성이 지정되어 있으며 이 항목은 대리자 형식으로 변환할 수 없습니다. 이 메서드에 대한 함수 포인터를 가져오세요. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ 대상으로 형식화된 개체 만들기 + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ NotNullIfNotNull이 참조하는 매개 변수가 null이 아니므로 매개 변수는 종료할 때 null이 아닌 값을 가져야 합니다. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}'은(는) 'Equals'를 정의하지만 'GetHashCode'는 정의하지 않습니다. @@ -3706,8 +3806,8 @@ - A namespace cannot directly contain members such as fields or methods - 네임스페이스는 필드나 메서드와 같은 멤버를 직접 포함할 수 없습니다. + A namespace cannot directly contain members such as fields, methods or statements + 네임스페이스는 필드나 메서드와 같은 멤버를 직접 포함할 수 없습니다. @@ -9238,11 +9338,6 @@ You should consider suppressing the warning only if you're sure that you don't w 오버로드된 Add 메서드 중 해당 컬렉션 이니셜라이저 요소에 가장 적합한 '{0}'은(는) 사용되지 않습니다. {1} - - Yield statements may not appear at the top level in interactive code. - Yield 문이 대화형 코드의 최상위 수준에 표시되지 않을 수 있습니다. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. 이 선언 형식에서는 '{0}' 보안 특성이 유효하지 않습니다. 보안 특성은 어셈블리, 형식 및 메서드 선언에서만 유효합니다. @@ -10771,11 +10866,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{1}' 형식에 대한 AsyncMethodBuilder로 사용할 '{0}' 형식의 작업 속성은 '{2}' 형식 대신 '{1}' 형식을 반환해야 합니다. - - Attributes are not allowed on local function parameters or type parameters - 특성은 로컬 함수 매개 변수 또는 형식 매개 변수에서 사용할 수 없습니다. - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. '{1}' 어셈블리의 '{0}' 모듈이 여러 어셈블리 '{3}' 및 '{4}'에 '{2}' 형식을 전달하고 있습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 585801538c802..c88af13137e65 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -247,6 +247,11 @@ Nie można użyć metody rozszerzenia z odbiornikiem jako elementem docelowym operatora „&”. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' Element „{0}” musi odpowiadać zmiennymi tylko do inicjowania przesłoniętej składowej „{1}” @@ -432,6 +437,11 @@ Drzewo wyrażeń nie może zawierać wyrażenia „od końca indeksu” („^”). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Drzewo wyrażenia nie może zawierać dostępu do indeksatora z wzorcem System.Index lub System.Range @@ -497,6 +507,16 @@ Zdarzenie-pole „{0}” nie może być zadeklarowane jako „readonly”. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? Instrukcja foreach nie może operować na zmiennych typu „{0}”, ponieważ typ „{0}” nie zawiera publicznego wystąpienia lub definicji rozszerzenia dla elementu „{1}”. Czy planowano użyć instrukcji „await foreach”, a nie „foreach”? @@ -617,6 +637,36 @@ Błąd wewnętrzny w kompilatorze języka C#. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. Element „{0}” nie jest prawidłowym modyfikatorem zwracanego typu wskaźnikowego funkcji. Prawidłowe modyfikatory to „ref” i „ref readonly”. @@ -632,6 +682,11 @@ Nieprawidłowa nazwa algorytmu wyznaczania wartości skrótu: „{0}” + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. Modyfikator „{0}” nie jest prawidłowy dla tego elementu w języku C# {1}. Użyj wersji języka „{2}” lub nowszej. @@ -742,6 +797,11 @@ Nie można użyć atrybutu [EnumeratorCancellation] w wielu parametrach + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Tylko pojedyncza częściowa deklaracja rekordu może mieć listę parametrów @@ -787,11 +847,21 @@ Składowa rekordu „{0}” musi być publiczna. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. Element „{0}” musi zezwalać na przesłanianie, ponieważ zawierający go rekord nie jest zapieczętowany. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' Oczekiwano opcji „enable”, „disable” lub „restore” @@ -1082,6 +1152,11 @@ Ograniczeń „class”, „struct”, „unmanaged”, „notnull” i „default” nie można łączyć ani duplikować i należy je najpierw określić na liście ograniczeń. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. Typ „{0}” musi być publiczny, aby można go było używać jako konwencji wywoływania. @@ -1097,6 +1172,11 @@ Element „{0}” ma atrybut „UnmanagedCallersOnly” i nie można go przekonwertować na typ delegowany. Uzyskaj wskaźnik funkcji do tej metody. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ tworzenie obiektu z typem docelowym + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ Parametr musi mieć wartość inną niż null podczas kończenia działania, ponieważ parametr przywoływany przez element NotNullIfNotNull ma wartość inną niż null. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' Element „{0}” definiuje element „Equals”, lecz nie element „GetHashCode” @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Przestrzeń nazw nie może bezpośrednio zawierać składowych, takich jak pola lub metody + A namespace cannot directly contain members such as fields, methods or statements + Przestrzeń nazw nie może bezpośrednio zawierać składowych, takich jak pola lub metody @@ -9239,11 +9339,6 @@ Pominięcie ostrzeżenia należy wziąć pod uwagę tylko w sytuacji, gdy na pew Najlepsza przeciążona metoda Add „{0}” dla elementu inicjatora kolekcji jest przestarzała. {1} - - Yield statements may not appear at the top level in interactive code. - Instrukcje yield nie mogą pojawiać się na najwyższym poziomie kodu interaktywnego. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. Atrybut zabezpieczeń „{0}” jest nieprawidłowy w tym typie deklaracji. Atrybuty zabezpieczeń są prawidłowe tylko dla deklaracji zestawu, typu i metody. @@ -10772,11 +10867,6 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Aby typ „{0}” mógł zostać użyty jako element AsyncMethodBuilder dla typu „{1}”, jego właściwość zadania powinna zwracać typ „{1}” zamiast typu „{2}”. - - Attributes are not allowed on local function parameters or type parameters - Atrybuty nie są dozwolone w przypadku lokalnych parametrów funkcji lub parametrów typu - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. Moduł „{0}” w zestawie „{1}” przekazuje typ „{2}” do wielu zestawów: „{3}” i „{4}”. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 827a53734c08c..c02b4151d8222 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -247,6 +247,11 @@ Não é possível usar um método de extensão com um receptor como destino de um operador '&'. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}' precisa corresponder por somente de inicialização do membro substituído '{1}' @@ -432,6 +437,11 @@ Uma árvore de expressão não pode conter uma expressão de índice de front-end ('^'). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Uma árvore de expressão não pode conter um padrão System.Index ou acesso do indexador System.Range @@ -497,6 +507,16 @@ O evento '{0}' semelhante ao de campo não pode ser 'readonly'. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? A instrução foreach não pode operar em variáveis do tipo '{0}' porque '{0}' não contém uma definição de extensão ou de instância pública para '{1}'. Você quis dizer 'await foreach' em vez de 'foreach'? @@ -617,6 +637,36 @@ Erro interno no compilador C#. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' não é um modificador de tipo de retorno de ponteiro de função válido. Os modificadores válidos são 'ref' e 'ref readonly'. @@ -632,6 +682,11 @@ Nome de algoritmo de hash inválido: '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. O modificador '{0}' não é válido para este item no C# {1}. Use a versão de linguagem '{2}' ou superior. @@ -742,6 +797,11 @@ O atributo [EnumeratorCancellation] não pode ser usado em vários parâmetros + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Apenas uma declaração parcial de registro único pode ter uma lista de parâmetros @@ -787,11 +847,21 @@ O membro do registro '{0}' precisa ser público. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. '{0}' precisa permitir a substituição porque o registro contentor não está selado. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' Esperava-se 'enable', 'disable' ou 'restore' @@ -1082,6 +1152,11 @@ As restrições 'class', 'struct', 'unmanaged', 'notnull' e 'default' não podem ser combinadas nem duplicadas e precisam ser especificadas primeiro na lista de restrições. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. O tipo '{0}' precisa ser público para ser usado como uma convenção de chamada. @@ -1097,6 +1172,11 @@ '{0}' foi atribuído com 'UnmanagedCallersOnly' e não pode ser convertido em um tipo delegado. Obtenha um ponteiro de função para esse método. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ criação de objeto de tipo de destino + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ O parâmetro precisa ter um valor não nulo ao sair porque o parâmetro referenciado por NotNullIfNotNull não é nulo. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}' define 'Equals', mas não 'GetHashCode' @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Um namespace não pode conter diretamente membros, como campos ou métodos + A namespace cannot directly contain members such as fields, methods or statements + Um namespace não pode conter diretamente membros, como campos ou métodos @@ -9239,11 +9339,6 @@ Você pode suprimir o aviso se tiver certeza de que não vai querer aguardar a c O melhor método Adicionar sobrecarregado "{0}" para o elemento do inicializador de coleção está obsoleto. {1} - - Yield statements may not appear at the top level in interactive code. - Instruções yield podem não aparecer no nível superior no código interativo. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. Atributo de segurança "{0}" não é válido neste tipo de declaração. Atributos de segurança são somente válidos em declarações de assembly, tipo e método. @@ -10772,11 +10867,6 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Para o tipo '{0}' a ser usado como um AsyncMethodBuilder para o tipo '{1}', sua propriedade Task deve retornar o tipo '{1}' em vez do tipo '{2}'. - - Attributes are not allowed on local function parameters or type parameters - Os atributos não são permitidos em parâmetros de função local ou parâmetros de tipo - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. O módulo '{0}' no assembly '{1}' está encaminhando o tipo '{2}' para vários assemblies: '{3}' e '{4}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 2f9dbc720fd75..c2e68d13e07a3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -247,6 +247,11 @@ Невозможно использовать метод расширения с приемником в качестве целевого объекта оператора "&". + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' "{0}" и переопределяемый элемент "{1}" должны соответствовать по методу доступа, вызываемому только во время инициализации. @@ -432,6 +437,11 @@ Дерево выражений не может содержать выражение индекса, отсчитываемого с конца ("^"). + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access Дерево выражения не может содержать доступ к индексатору System.Index или System.Range шаблона. @@ -497,6 +507,16 @@ Подобное полю событие "{0}" не может быть readonly. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? Оператор foreach не работает с переменными типа "{0}", так как "{0}" не содержит открытое определение экземпляра или расширения для "{1}" Возможно, вы имели в виду "await foreach", а не "foreach"? @@ -617,6 +637,36 @@ Внутренняя ошибка в компиляторе C#. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. "{0}" не является допустимым модификатором типа для возвращаемого значения указателя на функцию. Допустимые модификаторы: ref и ref readonly. @@ -632,6 +682,11 @@ Недопустимое имя хэш-алгоритма: "{0}" + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. Модификатор "{0}" недопустим для этого элемента в C# {1}. Используйте версию языка "{2}" или более позднюю. @@ -742,6 +797,11 @@ Атрибут [EnumeratorCancellation] невозможно использовать для нескольких параметров. + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Только частичное объявление отдельной записи может иметь список параметров. @@ -787,11 +847,21 @@ Элемент записи "{0}" должен быть открытым. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. "{0}" должен допускать переопределение, поскольку содержащая его запись не является запечатанной. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' Ожидается "enable", "disable" или "restore" @@ -1082,6 +1152,11 @@ Ограничения "class", "struct", "unmanaged", "notnull" и "default" не могут быть объединены или повторяться, поэтому они должны быть указаны первыми в списке ограничений. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. Тип "{0}" должен быть открытым для использования в качестве соглашения о вызовах. @@ -1097,6 +1172,11 @@ "{0}" имеет атрибут "UnmanagedCallersOnly" и не может быть преобразован в тип делегата. Получите указатель на функцию для этого метода. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ создание объекта с типом целевого объекта + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ При выходе параметр должен иметь значение, отличное от NULL, так как параметр, на который ссылается NotNullIfNotNull, имеет значение, отличное от NULL. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' "{0}" определяет "Equals", но не "GetHashCode" @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Пространство имен не может напрямую включать в себя такие члены, как поля или методы. + A namespace cannot directly contain members such as fields, methods or statements + Пространство имен не может напрямую включать в себя такие члены, как поля или методы. @@ -9239,11 +9339,6 @@ You should consider suppressing the warning only if you're sure that you don't w Наиболее подходящий перегруженный метод Add "{0}" для элемента инициализатора набора устарел. {1} - - Yield statements may not appear at the top level in interactive code. - Операторы yield не могут использоваться на верхнем уровне интерактивного кода. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. Атрибут безопасности "{0}" не допускается для этого типа объявления. Атрибуты безопасности допустимы только в сборке, типе и объявлениях метода. @@ -10772,11 +10867,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Чтобы тип "{0}" можно было использовать как AsyncMethodBuilder для типа "{1}", его свойство Task должно возвращать тип "{1}" вместо "{2}". - - Attributes are not allowed on local function parameters or type parameters - Атрибуты запрещены для параметров локальной функции и параметров типа - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. Модуль "{0}" в сборке "{1}" перенаправляет тип "{2}" в несколько сборок: "{3}" и "{4}". diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 1f324ab3fe24b..0a0cd4d39dd7d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -247,6 +247,11 @@ '&' operatörünün hedefi olarak alıcı içeren bir genişletme metodu kullanılamaz. + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}', geçersiz kılınmış '{1}' üyesinin yalnızca init öğesi bakımından eşleşmelidir @@ -432,6 +437,11 @@ İfade ağacı, sondan dizin ('^') ifadesi içeremez. + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access İfade ağacı, desen System.Index veya System.Range dizin oluşturucu erişimi içeremez @@ -497,6 +507,16 @@ Alan benzeri '{0}' olayı 'readonly' olamaz. + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? '{0}', '{1}' için bir genel örnek veya uzantı tanımı içermediğinden foreach deyimi '{0}' türündeki değişkenler üzerinde çalışamaz. 'foreach' yerine 'await foreach' mi kullanmak istediniz? @@ -617,6 +637,36 @@ C# derleyicisinde iç hata oluştu. + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}', geçerli bir işlev işaretçisi dönüş türü değiştiricisi değil. Geçerli değiştiriciler: 'ref' ve 'ref readonly'. @@ -632,6 +682,11 @@ Geçersiz karma algoritması adı: '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. '{0}' değiştiricisi bu öğe için C# {1} sürümünde geçerli değil. Lütfen '{2}' veya daha yüksek bir dil sürümü kullanın. @@ -742,6 +797,11 @@ [EnumeratorCancellation] özniteliği birden çok parametre üzerinde kullanılamaz + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list Yalnızca tek kaydın kısmi bildiriminde parametre listesi olabilir @@ -787,11 +847,21 @@ '{0}' kayıt üyesi genel olmalıdır. + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. '{0}', kapsayan kayıt mühürlü olmadığından geçersiz kılmaya izin vermelidir. + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' 'enable', 'disable' veya 'restore' bekleniyor @@ -1082,6 +1152,11 @@ 'class', 'struct', 'unmanaged', 'notnull' ve 'default' kısıtlamaları birleştirilemez veya yinelenemez ve kısıtlamalar listesinde ilk olarak belirtilmelidir. + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. '{0}' türünün bir çağırma kuralı olarak kullanılabilmesi için genel olması gerekir. @@ -1097,6 +1172,11 @@ '{0}', 'UnmanagedCallersOnly' özniteliğine sahip ve temsilci türüne dönüştürülemez. Bu yöntem için bir işlev işaretçisi edinin. UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ hedeflenen türde nesne oluşturma + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ NotNullIfNotNull tarafından başvurulan parametre null olmadığından parametre çıkış yaparken null olmayan bir değere sahip olmalıdır. + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}', 'GetHashCode' değil, 'Equals' tanımlıyor @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - Ad uzayı, alanlar veya yöntemler gibi üyeleri doğrudan içeremez + A namespace cannot directly contain members such as fields, methods or statements + Ad uzayı, alanlar veya yöntemler gibi üyeleri doğrudan içeremez @@ -9239,11 +9339,6 @@ Yalnızca asenkron çağrının tamamlanmasını beklemek istemediğinizden ve Koleksiyon başlatıcı öğesi için en iyi aşırı yüklenen '{0}' Ekle yöntemi artık kullanılmıyor. {1} - - Yield statements may not appear at the top level in interactive code. - Yield deyimleri etkileşimli kodun en üst düzeyinde görünmeyebilir. - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. '{0}' güvenlik özniteliği bu bildirim türü için geçerli değil. Güvenlik öznitelikleri yalnızca derleme, tür ve yöntem bildirimlerinde geçerlidir. @@ -10772,11 +10867,6 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T '{0}' türünün '{1}' türü için AsyncMethodBuilder olarak kullanılması için, Task özelliğinin '{2}' türü yerine '{1}' türü döndürmesi gerekir. - - Attributes are not allowed on local function parameters or type parameters - Yerel işlev parametrelerinde veya tür parametrelerinde özniteliklere izin verilmez - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. '{1}' bütünleştirilmiş kodundaki '{0}' modülü, '{2}' türünü birden çok bütünleştirilmiş koda iletiyor: '{3}' ve '{4}'. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 4499899f70de9..e385fddc16c18 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -247,6 +247,11 @@ 不可将具有接收器的扩展方法用作 "&" 运算符的目标。 + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' “{0}”必须与重写成员“{1}”的“仅 init”匹配 @@ -432,6 +437,11 @@ 表达式树不能包含 from-end 索引("^")表达式。 + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access 表达式树不能包含模式 System.Index 或 System.Range 索引器访问 @@ -497,6 +507,16 @@ 类似字段的事件 "{0}" 不能为 "readonly"。 + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? “{0}”不包含“{1}”的公共实例或扩展定义,因此 foreach 语句不能作用于“{0}”类型的变量。是否希望使用 "await foreach" 而非 "foreach"? @@ -617,6 +637,36 @@ C# 编译器中出现内部错误。 + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. “{0}”不是有效的函数指针返回类型修饰符。有效的修饰符为 "ref" 和 "ref readonly"。 @@ -632,6 +682,11 @@ 无效的哈希算法名称:“{0}” + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. 在 C# {1} 中,修饰符 "{0}" 对此项无效。请使用语言版本 "{2}" 或更高版本。 @@ -742,6 +797,11 @@ 不可在多个参数上使用 [EnumeratorCancellation] 属性 + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list 只有一个记录分部声明可以具有参数列表 @@ -787,11 +847,21 @@ 记录成员“{0}”必须是公共的。 + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. “{0}”必须允许替代,因为包含的记录未密封。 + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' 应为 "enable"、"disable" 或 "restore" @@ -1082,6 +1152,11 @@ "class"、"struct"、"unmanaged"、"notnull" 和 "default" 约束不能组合或重复,并且必须先在约束列表中进行指定。 + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. 类型“{0}”必须是公共的,才能用作调用约定。 @@ -1097,6 +1172,11 @@ “{0}”使用 "UnmanagedCallersOnly" 进行特性化,无法转换为委托类型。请获取指向此方法的函数指针。 UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ 创建目标类型对象 + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ 退出时参数必须具有非 null 值,因为由 NotNullIfNotNull 引用的参数是非 null。 + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' “{0}”定义 "Equals",而不定义 "GetHashCode" @@ -3712,8 +3812,8 @@ - A namespace cannot directly contain members such as fields or methods - 命名空间不能直接包含字段或方法之类的成员 + A namespace cannot directly contain members such as fields, methods or statements + 命名空间不能直接包含字段或方法之类的成员 @@ -9244,11 +9344,6 @@ You should consider suppressing the warning only if you're sure that you don't w 与集合初始值设定项元素最匹配的重载 Add 方法“{0}”已过时。{1} - - Yield statements may not appear at the top level in interactive code. - Yield 语句不能出现在交互代码中的顶层。 - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. 安全特性“{0}”对此声明类型无效。安全特性仅对程序集、类型和方法声明有效。 @@ -10777,11 +10872,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 对于用作类型“{1}”的 AsyncMethodBuilder 的类型“{0}”,它的任务属性应返回类型“{1}”,而不是类型“{2}”。 - - Attributes are not allowed on local function parameters or type parameters - 属性在本地函数参数或类型参数中不被允许 - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. 程序集“{1}”中的模块“{0}”将类型“{2}”转发到多个程序集: “{3}”和“{4}”。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index c5bbaeaea6057..9d6053fdcb056 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -247,6 +247,11 @@ 無法使用具有接收器的擴充方法作為 '&' 運算子的目標。 + + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + '{0}' must match by init-only of overridden member '{1}' '{0}' 必須符合被覆寫之成員 '{1}' 的僅供初始化 @@ -432,6 +437,11 @@ 運算式樹狀架構不可包含 from-end index ('^') 運算式。 + + An expression tree may not contain an interpolated string handler conversion. + An expression tree may not contain an interpolated string handler conversion. + + An expression tree may not contain a pattern System.Index or System.Range indexer access 運算式樹狀架構不可包含 System.Index 或 System.Range 索引子存取模式 @@ -497,6 +507,16 @@ 類似欄位的事件 '{0}' 不能是 'readonly'。 + + Source file can not contain both file-scoped and normal namespace declarations. + Source file can not contain both file-scoped and normal namespace declarations. + + + + File-scoped namespace must precede all other members in a file. + File-scoped namespace must precede all other members in a file. + + foreach statement cannot operate on variables of type '{0}' because '{0}' does not contain a public instance or extension definition for '{1}'. Did you mean 'await foreach' rather than 'foreach'? 因為 '{0}' 不包含 '{1}' 的公用執行個體或延伸模組定義,所以 foreach 陳述式無法在型別 '{0}' 的變數上運作。您指的是 'await foreach' 而不是 'foreach' 嗎? @@ -617,6 +637,36 @@ C# 編譯器中的內部錯誤。 + + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + The InterpolatedStringHandlerArgumentAttribute applied to parameter '{0}' is malformed and cannot be interpreted. Construct an instance of '{1}' manually. + InterpolatedStringHandlerArgumentAttribute is a type name and should not be translated. + + + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + Parameter '{0}' is an argument to the interpolated string handler conversion on parameter '{1}', but the corresponding argument is specified after the interpolated string expression. Reorder the arguments to move '{0}' before '{1}'. + + + + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + Parameter '{0}' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter '{1}'. Specify the value of '{0}' before '{1}'. + + + + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + An interpolated string handler construction cannot use dynamic. Manually construct an instance of '{0}'. + + + + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + Interpolated string handler method '{0}' has inconsistent return type. Expected to return '{1}'. + + + + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. + void and bool are keywords + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}'不是有效的函式指標傳回型別修飾元。有效的修飾元為 'ref' 與 'ref readonly'。 @@ -632,6 +682,11 @@ 雜湊演算法名稱無效: '{0}' + + '{0}' is not a valid parameter name from '{1}'. + '{0}' is not a valid parameter name from '{1}'. + + The modifier '{0}' is not valid for this item in C# {1}. Please use language version '{2}' or greater. 修飾元 '{0}' 在 C# {1} 中對此項目無效。請使用 '{2}' 或更高的語言版本。 @@ -742,6 +797,11 @@ 無法在多個參數上使用屬性 [EnumeratorCancellation] + + Source file can only contain one file-scoped namespace declaration. + Source file can only contain one file-scoped namespace declaration. + + Only a single record partial declaration may have a parameter list 只有一筆記錄可以在部分宣告中包含參數清單 @@ -787,11 +847,21 @@ 記錄成員 '{0}' 必須為公用。 + + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + '{0}' is not an instance method, the receiver cannot be an interpolated string handler argument. + + '{0}' must allow overriding because the containing record is not sealed. '{0}' 必須允許覆寫,因為包含的記錄並未密封。 + + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + + Expected 'enable', 'disable', or 'restore' 應為 'enable'、'disable' 或 'restore' @@ -1082,6 +1152,11 @@ 無法合併或複製 'class'、'struct'、'unmanaged'、'notnull' 以及 'default' 條件約束,而且必須先在條件約束清單中指定。 + + '{0}' is not an interpolated string handler type. + '{0}' is not an interpolated string handler type. + + Type '{0}' must be public to be used as a calling convention. 類型 '{0}' 必須是公用,才能用為呼叫慣例。 @@ -1097,6 +1172,11 @@ '{0}' 使用 'UnmanagedCallersOnly' 屬性化,因此無法轉換為委派類型。取得此方法的函式指標。 UnmanagedCallersOnly is not localizable. + + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + A generic task-like return type was expected, but the type '{0}' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + + The using directive for '{0}' appeared previously as global using The using directive for '{0}' appeared previously as global using @@ -1127,6 +1207,11 @@ extended property patterns + + file-scoped namespace + file-scoped namespace + + generic attributes generic attributes @@ -1142,6 +1227,11 @@ 建立具目標類型的物件 + + interpolated string handlers + interpolated string handlers + + inferred delegate type inferred delegate type @@ -1222,6 +1312,16 @@ 因為 NotNullIfNotNull 所參考的參數不是 null,所以參數在結束時必須有非 null 值。 + + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + Parameter {0} occurs after {1} in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + + + + Parameter to interpolated string handler conversion occurs after handler parameter + Parameter to interpolated string handler conversion occurs after handler parameter + + '{0}' defines 'Equals' but not 'GetHashCode' '{0}' 會定義 'Equals' 而非 'GetHashCode' @@ -3707,8 +3807,8 @@ - A namespace cannot directly contain members such as fields or methods - 命名空間不能直接包含如欄位或方法等成員 + A namespace cannot directly contain members such as fields, methods or statements + 命名空間不能直接包含如欄位或方法等成員 @@ -9239,11 +9339,6 @@ You should consider suppressing the warning only if you're sure that you don't w 集合初始設定式元素最符合的多載 Add 方法 '{0}' 已經過時。{1} - - Yield statements may not appear at the top level in interactive code. - Yield 陳述式不可出現在互動式程式碼的最上層。 - - Security attribute '{0}' is not valid on this declaration type. Security attributes are only valid on assembly, type and method declarations. 安全屬性 '{0}' 在此宣告類型上無效。安全屬性只有在組件、類型和方法宣告上才有效。 @@ -10772,11 +10867,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 若要讓 '{0}' 類型作為 '{1}' 類型的 AsyncMethodBuilder,其 Task 屬性應傳回 '{1}' 類型,而非 '{2}' 類型。 - - Attributes are not allowed on local function parameters or type parameters - 區域函式參數或型別參數中不允許屬性 - - Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'. 組件 '{1}' 中的模組 '{0}' 正在將類型 '{2}' 轉送給多個組件: '{3}' 及 '{4}'。 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs index a220a78b9be9a..e76fddcd11a38 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncMethodBuilderOverrideTests.cs @@ -619,12 +619,12 @@ class C // (9,29): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // static async MyTask F() { System.Console.Write("F "); await Task.Delay(0); } Diagnostic(ErrorCode.ERR_BadAsyncReturn, @"{ System.Console.Write(""F ""); await Task.Delay(0); }").WithLocation(9, 29), - // (12,38): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator + // (12,38): error CS8940: A generic task-like return type was expected, but the type 'MyTaskMethodBuilder' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // static async MyTask G(T t) { System.Console.Write("G "); await Task.Delay(0); return t; } - Diagnostic(ErrorCode.ERR_BadAsyncReturn, @"{ System.Console.Write(""G ""); await Task.Delay(0); return t; }").WithLocation(12, 38), - // (15,41): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, @"{ System.Console.Write(""G ""); await Task.Delay(0); return t; }").WithArguments("MyTaskMethodBuilder").WithLocation(12, 38), + // (15,41): error CS8940: A generic task-like return type was expected, but the type 'MyTaskMethodBuilder' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // public static async MyTask M() { System.Console.Write("M "); await F(); return await G(3); } - Diagnostic(ErrorCode.ERR_BadAsyncReturn, @"{ System.Console.Write(""M ""); await F(); return await G(3); }").WithLocation(15, 41) + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, @"{ System.Console.Write(""M ""); await F(); return await G(3); }").WithArguments("MyTaskMethodBuilder").WithLocation(15, 41) ); } @@ -1501,11 +1501,11 @@ class C {AsyncMethodBuilderAttribute} "; - var compilation = CreateCompilationWithMscorlib45(source, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilationWithMscorlib45(source); compilation.VerifyEmitDiagnostics( - // (9,34): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator + // (9,34): error CS8940: A generic task-like return type was expected, but the type 'MyTaskMethodBuilder' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // static async MyTask M() { await Task.Delay(0); throw null; } - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "{ await Task.Delay(0); throw null; }").WithLocation(9, 34) + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "{ await Task.Delay(0); throw null; }").WithArguments("MyTaskMethodBuilder").WithLocation(9, 34) ); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs index cb53e0e9ce146..77028be86a8c4 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncTests.cs @@ -3343,11 +3343,11 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : "; var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyEmitDiagnostics( - // (17,27): error CS1983: The return type of an async method must be void, Task or Task - // async T_NIT f1() => await Task.FromResult(1); - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(17, 27), - // (18,22): error CS1983: The return type of an async method must be void, Task or Task - // async T_NIN f2() => await Task.FromResult(1); + // (17,27): error CS8940: A generic task-like return type was expected, but the type 'N.BG' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + // async T_NIT f1() => await Task.FromResult(1); + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "=> await Task.FromResult(1)").WithArguments("N.BG").WithLocation(17, 27), + // (18,22): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator + // async T_NIN f2() => await Task.FromResult(1); Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(18, 22), // (19,27): error CS0656: Missing compiler required member 'N.BG.Task' // async T_NOT f3() => await Task.FromResult(1); // ok builderType genericity (but missing members) @@ -3355,40 +3355,40 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : // (19,27): error CS0656: Missing compiler required member 'N.BG.Create' // async T_NOT f3() => await Task.FromResult(1); // ok builderType genericity (but missing members) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.FromResult(1)").WithArguments("N.BG", "Create").WithLocation(19, 27), - // (20,22): error CS1983: The return type of an async method must be void, Task or Task - // async T_NON f4() => await Task.FromResult(1); + // (20,22): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator + // async T_NON f4() => await Task.FromResult(1); Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(20, 22), - // (21,27): error CS1983: The return type of an async method must be void, Task or Task - // async T_NNT f5() => await Task.FromResult(1); - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(21, 27), + // (21,27): error CS8940: A generic task-like return type was expected, but the type 'N.BN' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. + // async T_NNT f5() => await Task.FromResult(1); + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "=> await Task.FromResult(1)").WithArguments("N.BN").WithLocation(21, 27), // (22,22): error CS0656: Missing compiler required member 'N.BN.Task' // async T_NNN f6() => await Task.FromResult(1); // ok builderType genericity (but missing members) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.FromResult(1)").WithArguments("N.BN", "Task").WithLocation(22, 22), // (22,22): error CS0656: Missing compiler required member 'N.BN.Create' // async T_NNN f6() => await Task.FromResult(1); // ok builderType genericity (but missing members) Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "=> await Task.FromResult(1)").WithArguments("N.BN", "Create").WithLocation(22, 22), - // (39,27): error CS1983: The return type of an async method must be void, Task or Task + // (39,27): error CS8940: A generic task-like return type was expected, but the type 'G.BG' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // async T_IIT g1() => await Task.FromResult(1); - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(39, 27), - // (40,22): error CS1983: The return type of an async method must be void, Task or Task + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "=> await Task.FromResult(1)").WithArguments("G.BG").WithLocation(39, 27), + // (40,22): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // async T_IIN g2() => await Task.FromResult(1); Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(40, 22), - // (41,27): error CS1983: The return type of an async method must be void, Task or Task + // (41,27): error CS8940: A generic task-like return type was expected, but the type 'G.BN' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // async T_INT g3() => await Task.FromResult(1); - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(41, 27), - // (42,22): error CS1983: The return type of an async method must be void, Task or Task + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "=> await Task.FromResult(1)").WithArguments("G.BN").WithLocation(41, 27), + // (42,22): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // async T_INN g4() => await Task.FromResult(1); // might have been ok builder genericity but we decided not Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(42, 22), - // (43,27): error CS1983: The return type of an async method must be void, Task or Task + // (43,27): error CS8940: A generic task-like return type was expected, but the type 'G<>.BG<>' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // async T_OOT g5() => await Task.FromResult(1); - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(43, 27), - // (44,22): error CS1983: The return type of an async method must be void, Task or Task + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "=> await Task.FromResult(1)").WithArguments("G<>.BG<>").WithLocation(43, 27), + // (44,22): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // async T_OON g6() => await Task.FromResult(1); Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(44, 22), - // (45,27): error CS1983: The return type of an async method must be void, Task or Task + // (45,27): error CS8940: A generic task-like return type was expected, but the type 'G<>.BN' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // async T_ONT g7() => await Task.FromResult(1); - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(45, 27), - // (46,22): error CS1983: The return type of an async method must be void, Task or Task + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "=> await Task.FromResult(1)").WithArguments("G<>.BN").WithLocation(45, 27), + // (46,22): error CS1983: The return type of an async method must be void, Task, Task, a task-like type, IAsyncEnumerable, or IAsyncEnumerator // async T_ONN g8() => await Task.FromResult(1); Diagnostic(ErrorCode.ERR_BadAsyncReturn, "=> await Task.FromResult(1)").WithLocation(46, 22) ); @@ -3824,9 +3824,9 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : "; var comp = CreateCompilationWithMscorlib45(source); comp.VerifyEmitDiagnostics( - // (5,30): error CS1983: The return type of an async method must be void, Task or Task + // (5,30): error CS8940: A generic task-like return type was expected, but the type 'Mismatch1MethodBuilder' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // async Mismatch1 f() { await (Task)null; return 1; } - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "{ await (Task)null; return 1; }").WithLocation(5, 30), + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "{ await (Task)null; return 1; }").WithArguments("Mismatch1MethodBuilder").WithLocation(5, 30), // (6,45): error CS1997: Since 'C.g()' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task'? // async Mismatch2 g() { await (Task)null; return 1; } Diagnostic(ErrorCode.ERR_TaskRetNoObjectRequired, "return").WithArguments("C.g()").WithLocation(6, 45) @@ -4828,9 +4828,9 @@ public class AsyncMethodBuilderAttribute : System.Attribute { public AsyncMethod }"; var compilation = CreateCompilation(source, options: TestOptions.DebugDll); compilation.VerifyEmitDiagnostics( - // (8,53): error CS1983: The return type of an async method must be void, Task or Task + // (8,53): error CS8940: A generic task-like return type was expected, but the type 'CustomAsyncTaskMethodBuilder<,>' found in 'AsyncMethodBuilder' attribute was not suitable. It must be an unbound generic type of arity one, and its containing type (if any) must be non-generic. // public async MyAwesomeType CustomTask() { await Task.Delay(1000); return string.Empty; } - Diagnostic(ErrorCode.ERR_BadAsyncReturn, "{ await Task.Delay(1000); return string.Empty; }").WithLocation(8, 53) + Diagnostic(ErrorCode.ERR_WrongArityAsyncReturn, "{ await Task.Delay(1000); return string.Empty; }").WithArguments("CustomAsyncTaskMethodBuilder<,>").WithLocation(8, 53) ); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/WinMdDelegateTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/WinMdDelegateTests.cs index 91944f42f246e..b5626e22c70e1 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/WinMdDelegateTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/WinMdDelegateTests.cs @@ -25,14 +25,14 @@ public class WinMdDelegateTests : CSharpTestBase /// When the output type is .winmdobj, delegate types shouldn't output Begin/End invoke /// members. /// - [Fact(), WorkItem(1003193, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1003193")] - public void SimpleDelegateMembersTest() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace)), WorkItem(1003193, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1003193")] + public void SimpleDelegateMembersTest(string ob, string cb) { - const string libSrc = -@"namespace Test -{ + string libSrc = +$@"namespace Test {ob} public delegate void voidDelegate(); -}"; +{cb} +"; Func> getValidator = expectedMembers => m => { { @@ -54,7 +54,8 @@ public void SimpleDelegateMembersTest() libSrc, sourceSymbolValidator: validator, symbolValidator: validator, - options: winmd ? TestOptions.ReleaseWinMD : TestOptions.ReleaseDll); + options: winmd ? TestOptions.ReleaseWinMD : TestOptions.ReleaseDll, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); verifier.VerifyDiagnostics(); }; diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index a06040ba8b4c2..78acdc77f3244 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -149,7 +149,7 @@ public static void Main() } } -namespace N.Goo; +namespace N.; "); EmitResult emitResult; @@ -161,22 +161,21 @@ namespace N.Goo; Assert.False(emitResult.Success); emitResult.Diagnostics.Verify( - // (13,16): error CS1514: { expected - // namespace N.Foo; - Diagnostic(ErrorCode.ERR_LbraceExpected, ";").WithLocation(13, 16), - // (13,17): error CS1513: } expected - // namespace N.Foo; - Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(13, 17), - // (4,16): error CS0246: The type or namespace name 'Blah' could not be found (are you missing a using directive or an assembly reference?) - // public Blah field; - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Blah").WithArguments("Blah").WithLocation(4, 16), - // (8,13): error CS0198: A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) - // ro = 4; - Diagnostic(ErrorCode.ERR_AssgReadonlyStatic, "ro").WithLocation(8, 13), - // (4,21): warning CS0649: Field 'X.field' is never assigned to, and will always have its default value null - // public Blah field; - Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("N.X.field", "null").WithLocation(4, 21) - ); + // (13,13): error CS1001: Identifier expected + // namespace N.; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(13, 13), + // (13,11): error CS8942: File-scoped namespace must precede all other members in a file. + // namespace N.; + Diagnostic(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, "N.").WithLocation(13, 11), + // (4,16): error CS0246: The type or namespace name 'Blah' could not be found (are you missing a using directive or an assembly reference?) + // public Blah field; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Blah").WithArguments("Blah").WithLocation(4, 16), + // (8,13): error CS0198: A static readonly field cannot be assigned to (except in a static constructor or a variable initializer) + // ro = 4; + Diagnostic(ErrorCode.ERR_AssgReadonlyStatic, "ro").WithLocation(8, 13), + // (4,21): warning CS0649: Field 'X.field' is never assigned to, and will always have its default value null + // public Blah field; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "field").WithArguments("N.X.field", "null").WithLocation(4, 21)); } // Check that EmitMetadataOnly works diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs index 16def4e4ea2af..631a347e2793e 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs @@ -4431,5 +4431,54 @@ .maxstack 2 Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default), Row(7, TableIndex.MethodDef, EditAndContinueOperation.Default)); } + + [Fact] + public void TopLevelStatement_Closure() + { + var source0 = MarkedSource(@" + +using System; + +Func x = () => args[0]; +Console.WriteLine(x()); + +"); + var source1 = MarkedSource(@" + +using System; + +Func x = () => args[1]; +Console.WriteLine(x()); + +"); + var compilation0 = CreateCompilation(source0.Tree, options: TestOptions.DebugExe); + var compilation1 = compilation0.WithSource(source1.Tree); + + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("$.
$"); + var f1 = compilation1.GetMember("$.
$"); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + // no new synthesized members generated (with #1 in names): + diff1.VerifySynthesizedMembers( + "$.<>c__DisplayClass0_0: {args, <
$>b__0}", + "$: {<>c__DisplayClass0_0}"); + + var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + + // Method updates + CheckEncLogDefinitions(reader1, + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default)); + } } } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 3a6e5142eb10f..f0b68c24dd12c 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -11461,5 +11461,71 @@ record R(int X) "Microsoft.CodeAnalysis: {EmbeddedAttribute}", "System.Runtime.CompilerServices: {NullableAttribute, NullableContextAttribute}"); } + + [Fact] + public void TopLevelStatement_Update() + { + var source0 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var source1 = @" +using System; + +Console.WriteLine(""Hello World""); +"; + var compilation0 = CreateCompilation(source0, options: TestOptions.DebugExe); + var compilation1 = compilation0.WithSource(source1); + + var method0 = compilation0.GetMember("$.
$"); + var method1 = compilation1.GetMember("$.
$"); + + // Verify full metadata contains expected rows. + var bytes0 = compilation0.EmitToArray(); + using var md0 = ModuleMetadata.CreateFromImage(bytes0); + var reader0 = md0.MetadataReader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "$"); + CheckNames(reader0, reader0.GetMethodDefNames(), "
$"); + CheckNames(reader0, reader0.GetMemberRefNames(), /*CompilationRelaxationsAttribute.*/".ctor", /*RuntimeCompatibilityAttribute.*/".ctor", /*Object.*/".ctor", /*DebuggableAttribute*/".ctor", /*Console.*/"WriteLine"); + + var generation0 = EmitBaseline.CreateInitialBaseline( + md0, + EmptyLocalsProvider); + + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Update, method0, method1))); + + // Verify delta metadata contains expected rows. + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + EncValidation.VerifyModuleMvid(1, reader0, reader1); + + CheckNames(readers, reader1.GetTypeDefNames()); + CheckNames(readers, reader1.GetMethodDefNames(), "
$"); + CheckNames(readers, reader1.GetMemberRefNames(), /*CompilerGenerated*/".ctor", /*Console.*/"WriteLine"); + + CheckEncLog(reader1, + Row(2, TableIndex.AssemblyRef, EditAndContinueOperation.Default), + Row(6, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(7, TableIndex.MemberRef, EditAndContinueOperation.Default), + Row(8, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(9, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(10, TableIndex.TypeRef, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default)); // Synthesized Main method + + CheckEncMap(reader1, + Handle(8, TableIndex.TypeRef), + Handle(9, TableIndex.TypeRef), + Handle(10, TableIndex.TypeRef), + Handle(1, TableIndex.MethodDef), + Handle(6, TableIndex.MemberRef), + Handle(7, TableIndex.MemberRef), + Handle(2, TableIndex.AssemblyRef)); + } } } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs index 48f91e506fdb8..5e54b64042ffd 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArgument.cs @@ -1058,7 +1058,7 @@ void M2( "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest((source, "file.cs"), expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] @@ -1101,7 +1101,7 @@ bool M2( "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest((source, "file.cs"), expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] @@ -1144,7 +1144,7 @@ static bool M2( "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest((source, "file.cs"), expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] @@ -1199,7 +1199,7 @@ static bool M2( "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest((source, "file.cs"), expectedOperationTree, TargetFramework.Mscorlib46, expectedDiagnostics); } [Fact] diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs similarity index 65% rename from src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringExpression.cs rename to src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs index 2ff9866436f12..5d23747fa0a49 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs @@ -5,6 +5,7 @@ #nullable disable using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -13,9 +14,15 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class IOperationTests_IInterpolatedStringExpression : SemanticModelTestBase { + private static CSharpTestSource GetSource(string code, bool hasDefaultHandler) + => hasDefaultHandler + ? new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) } + : code; + [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_Empty() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_Empty(bool hasDefaultHandler) { string source = @" using System; @@ -34,12 +41,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_OnlyTextPart() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_OnlyTextPart(bool hasDefaultHandler) { string source = @" using System; @@ -61,12 +69,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_OnlyInterpolationPart() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_OnlyInterpolationPart(bool hasDefaultHandler) { string source = @" using System; @@ -92,12 +101,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_EmptyInterpolationPart() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_EmptyInterpolationPart(bool hasDefaultHandler) { string source = @" using System; @@ -128,12 +138,13 @@ public void M() Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(8, 40) }; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_TextAndInterpolationParts() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_TextAndInterpolationParts(bool hasDefaultHandler) { string source = @" using System; @@ -172,12 +183,13 @@ public void M(int x) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_FormatAndAlignment() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_FormatAndAlignment(bool hasDefaultHandler) { string source = @" using System; @@ -233,12 +245,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_InterpolationAndFormatAndAlignment() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_InterpolationAndFormatAndAlignment(bool hasDefaultHandler) { string source = @" using System; @@ -274,12 +287,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_InvocationInInterpolation() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_InvocationInInterpolation(bool hasDefaultHandler) { string source = @" using System; @@ -339,12 +353,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_NestedInterpolation() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_NestedInterpolation(bool hasDefaultHandler) { string source = @" using System; @@ -392,12 +407,13 @@ public void M() "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation)] - [Fact, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] - public void InterpolatedStringExpression_InvalidExpressionInInterpolation() + [Theory, WorkItem(18300, "https://github.com/dotnet/roslyn/issues/18300")] + [CombinatorialData] + public void InterpolatedStringExpression_InvalidExpressionInInterpolation(bool hasDefaultHandler) { string source = @" using System; @@ -446,12 +462,13 @@ public void M(int x) Diagnostic(ErrorCode.ERR_BadSKunknown, "Class").WithArguments("Class", "type").WithLocation(8, 65) }; - VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + VerifyOperationTreeAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedOperationTree, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_Empty_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_Empty_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -487,12 +504,13 @@ public void M(string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_OnlyTextPart_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_OnlyTextPart_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -531,12 +549,13 @@ public void M(string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_OnlyInterpolationPart_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_OnlyInterpolationPart_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -614,12 +633,13 @@ public void M(bool a, string b, string c, string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_MultipleInterpolationParts_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_MultipleInterpolationParts_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -704,12 +724,13 @@ public void M(bool a, string b, string c, string c2, string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_TextAndInterpolationParts_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_TextAndInterpolationParts_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -823,12 +844,13 @@ public void M(bool a, string b, string c, bool a2, string b2, string c2, string "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_FormatAndAlignment_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_FormatAndAlignment_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -906,12 +928,13 @@ public void M(bool a, string b, string c, string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_FormatAndAlignment_Flow_02() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_FormatAndAlignment_Flow_02(bool hasDefaultHandler) { string source = @" using System; @@ -1004,12 +1027,13 @@ public void M(bool a, string b, string b2, string c, string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_FormatAndAlignment_Flow_03() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_FormatAndAlignment_Flow_03(bool hasDefaultHandler) { string source = @" using System; @@ -1117,12 +1141,13 @@ public void M(bool a, string b, string b2, string b3, string c, string p) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_NestedInterpolation_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_NestedInterpolation_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -1230,12 +1255,13 @@ public void M(string a, int? b, int c) "; var expectedDiagnostics = DiagnosticDescription.None; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_ConditionalCodeInAlignment_Flow() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_ConditionalCodeInAlignment_Flow(bool hasDefaultHandler) { string source = @" using System; @@ -1321,12 +1347,13 @@ public void M(bool a, int b, int c, string d, string p) Diagnostic(ErrorCode.ERR_ConstantExpected, "(a ? b : c)").WithLocation(8, 18) }; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); } [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] - [Fact] - public void InterpolatedStringExpression_ConditionalCodeInAlignment_Flow_02() + [Theory] + [CombinatorialData] + public void InterpolatedStringExpression_ConditionalCodeInAlignment_Flow_02(bool hasDefaultHandler) { string source = @" using System; @@ -1416,7 +1443,476 @@ public void M(bool a, string b, string c, string c2, string p) Diagnostic(ErrorCode.ERR_NoImplicitConv, "a ? b : c").WithArguments("string", "int").WithLocation(8, 20) }; - VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + VerifyFlowGraphAndDiagnosticsForTest(GetSource(source, hasDefaultHandler), expectedFlowGraph, expectedDiagnostics); + } + + [Fact] + public void InterpolatedStringHandlerConversion_01() + { + var code = @" +int i = 0; +CustomHandler /**/c = $""literal{i,1:test}""/**/; +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = $""literal{i,1:test}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= $""literal{i,1:test}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{i,1:test}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""literal{i,1:test}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: 'literal') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i,1:test}') + Expression: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + FormatString: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""test"") (Syntax: ':test') +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, handler }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void InterpolatedStringHandlerConversion_02() + { + var code = @" +int i = 0; +CustomHandler /**/c = $""literal{i,1}""/**/; +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = $""literal{i,1}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= $""literal{i,1}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{i,1}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""literal{i,1}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: 'literal') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i,1}') + Expression: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + FormatString: + null +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, handler }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void InterpolatedStringHandlerConversion_03() + { + var code = @" +int i = 0; +CustomHandler /**/c = $""literal{i:test}""/**/; +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = $""literal{i:test}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= $""literal{i:test}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{i:test}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""literal{i:test}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: 'literal') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i:test}') + Expression: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + null + FormatString: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""test"") (Syntax: ':test') +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, handler }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void InterpolatedStringHandlerConversion_04() + { + var code = @" +int i = 0; +CustomHandler /**/c = $""literal{i}""/**/; +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = $""literal{i}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= $""literal{i}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{i}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""literal{i}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: 'literal') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i}') + Expression: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + null + FormatString: + null +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, handler }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void InterpolatedStringHandlerConversion_05() + { + var code = @" +using System.Runtime.CompilerServices; +int i = 0; +CustomHandler /**/c = $""literal{i}""/**/; + +[InterpolatedStringHandler] +public struct CustomHandler {} +"; + + var expectedDiagnostics = new[] + { + // (4,29): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // CustomHandler /**/c = $"literal{i}"/**/; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""literal{i}""").WithArguments("CustomHandler", "2").WithLocation(4, 29), + // (4,29): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // CustomHandler /**/c = $"literal{i}"/**/; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""literal{i}""").WithArguments("CustomHandler", "3").WithLocation(4, 29), + // (4,31): error CS1061: 'CustomHandler' does not contain a definition for 'AppendLiteral' and no accessible extension method 'AppendLiteral' accepting a first argument of type 'CustomHandler' could be found (are you missing a using directive or an assembly reference?) + // CustomHandler /**/c = $"literal{i}"/**/; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "literal").WithArguments("CustomHandler", "AppendLiteral").WithLocation(4, 31), + // (4,31): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // CustomHandler /**/c = $"literal{i}"/**/; + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "literal").WithArguments("?.()").WithLocation(4, 31), + // (4,38): error CS1061: 'CustomHandler' does not contain a definition for 'AppendFormatted' and no accessible extension method 'AppendFormatted' accepting a first argument of type 'CustomHandler' could be found (are you missing a using directive or an assembly reference?) + // CustomHandler /**/c = $"literal{i}"/**/; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "{i}").WithArguments("CustomHandler", "AppendFormatted").WithLocation(4, 38), + // (4,38): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // CustomHandler /**/c = $"literal{i}"/**/; + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{i}").WithArguments("?.()").WithLocation(4, 38) + }; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'c = $""literal{i}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= $""literal{i}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsInvalid, IsImplicit) (Syntax: '$""literal{i}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$""literal{i}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null, IsInvalid) (Syntax: 'literal') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsInvalid, IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null, IsInvalid) (Syntax: '{i}') + Expression: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'i') + Alignment: + null + FormatString: + null +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, InterpolatedStringHandlerAttribute }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void InterpolatedStringHandlerConversion_06() + { + var code = @" +using System.Runtime.CompilerServices; +int i = 0; +CustomHandler /**/c = $""literal{i,1:test}""/**/; + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) {} + public void AppendFormatted(object o, object alignment, object format) {} + public void AppendLiteral(object o) {} +} +"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'c = $""literal{i,1:test}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= $""literal{i,1:test}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsImplicit) (Syntax: '$""literal{i,1:test}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""literal{i,1:test}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: 'literal') + Text: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'literal') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i,1:test}') + Expression: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'i') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: '1') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + FormatString: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: ':test') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""test"") (Syntax: ':test') +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, InterpolatedStringHandlerAttribute }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void InterpolatedStringHandlerConversion_07() + { + var code = @" +using System.Runtime.CompilerServices; +CustomHandler /**/c = $""{}""/**/; + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) {} + public void AppendFormatted(object o, object alignment, object format) {} + public void AppendLiteral(object o) {} +} +"; + + var expectedDiagnostics = new[] { + // (3,32): error CS1733: Expected expression + // CustomHandler /**/c = $"{}"/**/; + Diagnostic(ErrorCode.ERR_ExpressionExpected, "").WithLocation(3, 32) + }; + + string expectedOperationTree = @" +IVariableDeclaratorOperation (Symbol: CustomHandler c) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'c = $""{}""') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= $""{}""') + IOperation: (OperationKind.None, Type: CustomHandler, IsInvalid, IsImplicit) (Syntax: '$""{}""') + Children(1): + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$""{}""') + Parts(1): + IInterpolationOperation (OperationKind.Interpolation, Type: null, IsInvalid) (Syntax: '{}') + Expression: + IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '') + Children(0) + Alignment: + null + FormatString: + null + +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, InterpolatedStringHandlerAttribute }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void DynamicConstruction_01() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +dynamic d = 1; +M(/**/$""literal{d,1:format}""/**/); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) + { + } + + public void AppendLiteral(dynamic d) + { + Console.WriteLine(""AppendLiteral""); + } + + public void AppendFormatted(dynamic d, int alignment, string format) + { + Console.WriteLine(""AppendFormatted""); + } +} +"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""literal{d,1:format}""') + Parts(2): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: 'literal') + Text: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: dynamic, IsImplicit) (Syntax: 'literal') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsImplicit) (Syntax: 'literal') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{d,1:format}') + Expression: + ILocalReferenceOperation: d (OperationKind.LocalReference, Type: dynamic) (Syntax: 'd') + Alignment: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + FormatString: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""format"") (Syntax: ':format') +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, InterpolatedStringHandlerAttribute }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact] + public void DynamicConstruction_02() + { + var code = @" +using System.Runtime.CompilerServices; +dynamic d = 1; +M(d, /**/$""{1}literal""/**/); + +void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, dynamic d) : this() {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + + var expectedDiagnostics = new[] { + // (4,16): error CS8953: An interpolated string handler construction cannot use dynamic. Manually construct an instance of 'CustomHandler'. + // M(d, /**/$"{1}literal"/**/); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, @"$""{1}literal""").WithArguments("CustomHandler").WithLocation(4, 16) + }; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String, IsInvalid) (Syntax: '$""{1}literal""') + Parts(2): + IInterpolationOperation (OperationKind.Interpolation, Type: null, IsInvalid) (Syntax: '{1}') + Expression: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsInvalid, IsImplicit) (Syntax: '1') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + Alignment: + null + FormatString: + null + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null, IsInvalid) (Syntax: 'literal') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""literal"", IsInvalid, IsImplicit) (Syntax: 'literal') +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void InterpolationEscapeConstantValue_WithDefaultHandler() + { + var code = @" +int i = 1; +System.Console.WriteLine(/**/$""{{ {i} }}""/**/);"; + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{{ {i} }}""') + Parts(3): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: '{{ ') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{ "", IsImplicit) (Syntax: '{{ ') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i}') + Expression: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + null + FormatString: + null + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: ' }}') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "" }"", IsImplicit) (Syntax: ' }}') + +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void InterpolationEscapeConstantValue_WithoutDefaultHandler() + { + var code = @" +int i = 1; +System.Console.WriteLine(/**/$""{{ {i} }}""/**/);"; + + var expectedDiagnostics = DiagnosticDescription.None; + + // The difference between this test and the previous one is the constant value of the ILiteralOperations. When handlers are involved, the + // constant value is { and }. When they are not involved, the constant values are {{ and }} + string expectedOperationTree = @" +IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{{ {i} }}""') + Parts(3): + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: '{{ ') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""{{ "", IsImplicit) (Syntax: '{{ ') + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{i}') + Expression: + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + Alignment: + null + FormatString: + null + IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: ' }}') + Text: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "" }}"", IsImplicit) (Syntax: ' }}') + +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); } } } diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs index a29e0821f920e..6b6b577e777c0 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/RegionAnalysisTests.cs @@ -8575,5 +8575,91 @@ static void Local() } #endregion + + #region "Top level statements" + + [Fact] + public void TestTopLevelStatements() + { + var analysisResults = CompileAndAnalyzeControlAndDataFlowStatements(@" +using System; +using System.Linq; + +/**/ +Console.Write(1); +Console.Write(2); +Console.Write(3); +Console.Write(4); +Console.Write(5); +/**/ +"); + var controlFlowAnalysisResults = analysisResults.Item1; + var dataFlowAnalysisResults = analysisResults.Item2; + Assert.Equal(0, controlFlowAnalysisResults.EntryPoints.Count()); + Assert.Equal(0, controlFlowAnalysisResults.ExitPoints.Count()); + Assert.True(controlFlowAnalysisResults.EndPointIsReachable); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnEntry)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnExit)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); + } + + [Fact] + public void TestTopLevelStatements_Lambda() + { + var analysisResults = CompileAndAnalyzeControlAndDataFlowStatements(@" +using System; +using System.Linq; + +int i = 1; +Func lambda = () => { /**/return i;/**/ }; +"); + var controlFlowAnalysisResults = analysisResults.Item1; + var dataFlowAnalysisResults = analysisResults.Item2; + Assert.Equal(0, controlFlowAnalysisResults.EntryPoints.Count()); + Assert.Equal(1, controlFlowAnalysisResults.ExitPoints.Count()); + Assert.False(controlFlowAnalysisResults.EndPointIsReachable); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared)); + Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut)); + Assert.Equal("i, args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnEntry)); + Assert.Equal("i, args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnExit)); + Assert.Equal("i", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside)); + Assert.Equal("i, lambda, args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); + } + + [Fact] + public void TestTopLevelStatements_LambdaCapturingArgs() + { + var analysisResults = CompileAndAnalyzeControlAndDataFlowStatements(@" +using System; +using System.Linq; + +Func lambda = () => { /**/return args.Length;/**/ }; +"); + var controlFlowAnalysisResults = analysisResults.Item1; + var dataFlowAnalysisResults = analysisResults.Item2; + Assert.Equal(0, controlFlowAnalysisResults.EntryPoints.Count()); + Assert.Equal(1, controlFlowAnalysisResults.ExitPoints.Count()); + Assert.False(controlFlowAnalysisResults.EndPointIsReachable); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.VariablesDeclared)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsIn)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.DataFlowsOut)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnEntry)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.DefinitelyAssignedOnExit)); + Assert.Equal("args", GetSymbolNamesJoined(dataFlowAnalysisResults.ReadInside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.ReadOutside)); + Assert.Null(GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenInside)); + Assert.Equal("lambda, args", GetSymbolNamesJoined(dataFlowAnalysisResults.WrittenOutside)); + } + + #endregion } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs index 896ca5c3e4cbe..fbf712806045b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTasklikeMoreTests.cs @@ -785,7 +785,7 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : } [Fact] - static void AsyncMethodBuilderAttributeMultipleParameters() + public static void AsyncMethodBuilderAttributeMultipleParameters() { var source = @" using System.Runtime.CompilerServices; @@ -820,7 +820,7 @@ namespace System.Runtime.CompilerServices { class AsyncMethodBuilderAttribute : } [Fact] - static void AsyncMethodBuilderAttributeSingleParameterWrong() + public static void AsyncMethodBuilderAttributeSingleParameterWrong() { var source = @" using System.Runtime.CompilerServices; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/GlobalUsingDirectiveTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/GlobalUsingDirectiveTests.cs index 35eb4bc5d2573..57020206ab059 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/GlobalUsingDirectiveTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/GlobalUsingDirectiveTests.cs @@ -46,7 +46,7 @@ namespace ns4 {} ); CreateCompilation(source, parseOptions: TestOptions.Regular10).VerifyDiagnostics( - // (6,1): error CS9002: A global using directive must precede all non-global using directives. + // (6,1): error CS8915: A global using directive must precede all non-global using directives. // global using ns3; Diagnostic(ErrorCode.ERR_GlobalUsingOutOfOrder, "global").WithLocation(6, 1) ); @@ -107,7 +107,7 @@ namespace ns4 {} } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( - // (6,5): error CS9001: A global using directive cannot be used in a namespace declaration. + // (6,5): error CS8914: A global using directive cannot be used in a namespace declaration. // global using ns1; Diagnostic(ErrorCode.ERR_GlobalUsingInNamespace, "global").WithLocation(6, 5) ); @@ -133,7 +133,7 @@ namespace ns4 {} } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( - // (6,5): error CS9001: A global using directive cannot be used in a namespace declaration. + // (6,5): error CS8914: A global using directive cannot be used in a namespace declaration. // global using ns1; Diagnostic(ErrorCode.ERR_GlobalUsingInNamespace, "global").WithLocation(6, 5) ); @@ -1111,7 +1111,7 @@ public class C8 {} var comp = CreateCompilation(source1, parseOptions: TestOptions.RegularPreview); comp.GetDiagnostics().Where(d => d.Code != (int)ErrorCode.HDN_UnusedUsingDirective).Verify( - // (5,1): error CS9002: A global using directive must precede all non-global using directives. + // (5,1): error CS8915: A global using directive must precede all non-global using directives. // global using C = A.C2; Diagnostic(ErrorCode.ERR_GlobalUsingOutOfOrder, "global").WithLocation(5, 1), // (5,18): error CS0246: The type or namespace name 'A' could not be found (are you missing a using directive or an assembly reference?) @@ -1963,7 +1963,7 @@ public class C5 "; var comp = CreateCompilation(source1, parseOptions: TestOptions.RegularPreview); comp.GetDiagnostics().Where(d => d.Code != (int)ErrorCode.HDN_UnusedUsingDirective).Verify( - // (5,1): error CS9002: A global using directive must precede all non-global using directives. + // (5,1): error CS8915: A global using directive must precede all non-global using directives. // global using C = C2; Diagnostic(ErrorCode.ERR_GlobalUsingOutOfOrder, "global").WithLocation(5, 1), // (5,18): error CS0246: The type or namespace name 'C2' could not be found (are you missing a using directive or an assembly reference?) @@ -4889,7 +4889,7 @@ public static void M5() {} } "; CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics( - // (4,5): error CS9001: A global using directive cannot be used in a namespace declaration. + // (4,5): error CS8914: A global using directive cannot be used in a namespace declaration. // global using NS2; Diagnostic(ErrorCode.ERR_GlobalUsingInNamespace, "global").WithLocation(4, 5), // (2000,17): error CS0246: The type or namespace name 'NS1C1' could not be found (are you missing a using directive or an assembly reference?) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 62cc043b27220..1d37a8c5c1083 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -4,10 +4,16 @@ #nullable disable +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; +using System.Text; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics @@ -207,6 +213,61 @@ static void Main(string[] args) CompileAndVerify(source, expectedOutput: expectedOutput); } + [Fact] + public void OneLiteral() + { + string source = +@"using System; +class Program +{ + static void Main(string[] args) + { + Console.WriteLine( $""Hello"" ); + } +}"; + string expectedOutput = @"Hello"; + var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); + verifier.VerifyIL("Program.Main", @" +{ + // Code size 11 (0xb) + .maxstack 1 + IL_0000: ldstr ""Hello"" + IL_0005: call ""void System.Console.WriteLine(string)"" + IL_000a: ret +} +"); + } + + [Fact] + public void OneInsert() + { + string source = +@"using System; +class Program +{ + static void Main(string[] args) + { + var hello = $""Hello""; + Console.WriteLine( $""{hello}"" ); + } +}"; + string expectedOutput = @"Hello"; + var verifier = CompileAndVerify(source, expectedOutput: expectedOutput); + verifier.VerifyIL("Program.Main", @" +{ + // Code size 20 (0x14) + .maxstack 2 + IL_0000: ldstr ""Hello"" + IL_0005: dup + IL_0006: brtrue.s IL_000e + IL_0008: pop + IL_0009: ldstr """" + IL_000e: call ""void System.Console.WriteLine(string)"" + IL_0013: ret +} +"); + } + [Fact] public void TwoInserts() { @@ -1195,5 +1256,10068 @@ static void Main() Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""{""""}""").WithArguments("System.FormattableString", "System.IFormattable").WithLocation(23, 33) ); } + + [Fact, WorkItem(54702, "https://github.com/dotnet/roslyn/issues/54702")] + public void InterpolatedStringHandler_ConcatPreferencesForAllStringElements() + { + var code = @" +using System; +Console.WriteLine(TwoComponents()); +Console.WriteLine(ThreeComponents()); +Console.WriteLine(FourComponents()); +Console.WriteLine(FiveComponents()); + +string TwoComponents() +{ + string s1 = ""1""; + string s2 = ""2""; + return $""{s1}{s2}""; +} + +string ThreeComponents() +{ + string s1 = ""1""; + string s2 = ""2""; + string s3 = ""3""; + return $""{s1}{s2}{s3}""; +} + +string FourComponents() +{ + string s1 = ""1""; + string s2 = ""2""; + string s3 = ""3""; + string s4 = ""4""; + return $""{s1}{s2}{s3}{s4}""; +} + +string FiveComponents() +{ + string s1 = ""1""; + string s2 = ""2""; + string s3 = ""3""; + string s4 = ""4""; + string s5 = ""5""; + return $""{s1}{s2}{s3}{s4}{s5}""; +} +"; + + var handler = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { code, handler }, expectedOutput: @" +12 +123 +1234 +value:1 +value:2 +value:3 +value:4 +value:5 +"); + + verifier.VerifyIL("$.<
$>g__TwoComponents|0_0()", @" +{ + // Code size 18 (0x12) + .maxstack 2 + .locals init (string V_0) //s2 + IL_0000: ldstr ""1"" + IL_0005: ldstr ""2"" + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: call ""string string.Concat(string, string)"" + IL_0011: ret +} +"); + + verifier.VerifyIL("$.<
$>g__ThreeComponents|0_1()", @" +{ + // Code size 25 (0x19) + .maxstack 3 + .locals init (string V_0, //s2 + string V_1) //s3 + IL_0000: ldstr ""1"" + IL_0005: ldstr ""2"" + IL_000a: stloc.0 + IL_000b: ldstr ""3"" + IL_0010: stloc.1 + IL_0011: ldloc.0 + IL_0012: ldloc.1 + IL_0013: call ""string string.Concat(string, string, string)"" + IL_0018: ret +} +"); + + verifier.VerifyIL("$.<
$>g__FourComponents|0_2()", @" +{ + // Code size 32 (0x20) + .maxstack 4 + .locals init (string V_0, //s2 + string V_1, //s3 + string V_2) //s4 + IL_0000: ldstr ""1"" + IL_0005: ldstr ""2"" + IL_000a: stloc.0 + IL_000b: ldstr ""3"" + IL_0010: stloc.1 + IL_0011: ldstr ""4"" + IL_0016: stloc.2 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: call ""string string.Concat(string, string, string, string)"" + IL_001f: ret +} +"); + + verifier.VerifyIL("$.<
$>g__FiveComponents|0_3()", @" +{ + // Code size 89 (0x59) + .maxstack 3 + .locals init (string V_0, //s1 + string V_1, //s2 + string V_2, //s3 + string V_3, //s4 + string V_4, //s5 + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_5) + IL_0000: ldstr ""1"" + IL_0005: stloc.0 + IL_0006: ldstr ""2"" + IL_000b: stloc.1 + IL_000c: ldstr ""3"" + IL_0011: stloc.2 + IL_0012: ldstr ""4"" + IL_0017: stloc.3 + IL_0018: ldstr ""5"" + IL_001d: stloc.s V_4 + IL_001f: ldloca.s V_5 + IL_0021: ldc.i4.0 + IL_0022: ldc.i4.5 + IL_0023: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0028: ldloca.s V_5 + IL_002a: ldloc.0 + IL_002b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_0030: ldloca.s V_5 + IL_0032: ldloc.1 + IL_0033: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_0038: ldloca.s V_5 + IL_003a: ldloc.2 + IL_003b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_0040: ldloca.s V_5 + IL_0042: ldloc.3 + IL_0043: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_0048: ldloca.s V_5 + IL_004a: ldloc.s V_4 + IL_004c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_0051: ldloca.s V_5 + IL_0053: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0058: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void InterpolatedStringHandler_OverloadsAndBoolReturns(bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg) + { + var source = +@"int a = 1; +System.Console.WriteLine($""base{a}{a,1}{a:X}{a,2:Y}"");"; + + string interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters, useBoolReturns, constructorBoolArg: constructorBoolArg); + + string expectedOutput = useDefaultParameters ? +@"base +value:1,alignment:0:format: +value:1,alignment:1:format: +value:1,alignment:0:format:X +value:1,alignment:2:format:Y" : +@"base +value:1 +value:1,alignment:1 +value:1:format:X +value:1,alignment:2:format:Y"; + + string expectedIl = getIl(); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput); + verifier.VerifyIL("", expectedIl); + + var comp1 = CreateCompilation(interpolatedStringBuilder); + + foreach (var reference in new[] { comp1.EmitToImageReference(), comp1.ToMetadataReference() }) + { + var comp2 = CreateCompilation(source, new[] { reference }); + verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput); + verifier.VerifyIL("", expectedIl); + } + + string getIl() => (useDefaultParameters, useBoolReturns, constructorBoolArg) switch + { + (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: false) => @" +{ + // Code size 80 (0x50) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.4 + IL_0005: ldc.i4.4 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldstr ""base"" + IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0017: ldloca.s V_1 + IL_0019: ldloc.0 + IL_001a: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_001f: ldloca.s V_1 + IL_0021: ldloc.0 + IL_0022: ldc.i4.1 + IL_0023: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int)"" + IL_0028: ldloca.s V_1 + IL_002a: ldloc.0 + IL_002b: ldstr ""X"" + IL_0030: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)"" + IL_0035: ldloca.s V_1 + IL_0037: ldloc.0 + IL_0038: ldc.i4.2 + IL_0039: ldstr ""Y"" + IL_003e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0043: ldloca.s V_1 + IL_0045: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_004a: call ""void System.Console.WriteLine(string)"" + IL_004f: ret +} +", + (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: false) => @" +{ + // Code size 84 (0x54) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.4 + IL_0005: ldc.i4.4 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldstr ""base"" + IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0017: ldloca.s V_1 + IL_0019: ldloc.0 + IL_001a: ldc.i4.0 + IL_001b: ldnull + IL_001c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0021: ldloca.s V_1 + IL_0023: ldloc.0 + IL_0024: ldc.i4.1 + IL_0025: ldnull + IL_0026: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_002b: ldloca.s V_1 + IL_002d: ldloc.0 + IL_002e: ldc.i4.0 + IL_002f: ldstr ""X"" + IL_0034: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0039: ldloca.s V_1 + IL_003b: ldloc.0 + IL_003c: ldc.i4.2 + IL_003d: ldstr ""Y"" + IL_0042: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0047: ldloca.s V_1 + IL_0049: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_004e: call ""void System.Console.WriteLine(string)"" + IL_0053: ret +} +", + (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: false) => @" +{ + // Code size 92 (0x5c) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.4 + IL_0005: ldc.i4.4 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldstr ""base"" + IL_0012: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0017: brfalse.s IL_004d + IL_0019: ldloca.s V_1 + IL_001b: ldloc.0 + IL_001c: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0021: brfalse.s IL_004d + IL_0023: ldloca.s V_1 + IL_0025: ldloc.0 + IL_0026: ldc.i4.1 + IL_0027: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int)"" + IL_002c: brfalse.s IL_004d + IL_002e: ldloca.s V_1 + IL_0030: ldloc.0 + IL_0031: ldstr ""X"" + IL_0036: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)"" + IL_003b: brfalse.s IL_004d + IL_003d: ldloca.s V_1 + IL_003f: ldloc.0 + IL_0040: ldc.i4.2 + IL_0041: ldstr ""Y"" + IL_0046: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_004b: br.s IL_004e + IL_004d: ldc.i4.0 + IL_004e: pop + IL_004f: ldloca.s V_1 + IL_0051: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0056: call ""void System.Console.WriteLine(string)"" + IL_005b: ret +} +", + (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: false) => @" +{ + // Code size 96 (0x60) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.4 + IL_0005: ldc.i4.4 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldstr ""base"" + IL_0012: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0017: brfalse.s IL_0051 + IL_0019: ldloca.s V_1 + IL_001b: ldloc.0 + IL_001c: ldc.i4.0 + IL_001d: ldnull + IL_001e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0023: brfalse.s IL_0051 + IL_0025: ldloca.s V_1 + IL_0027: ldloc.0 + IL_0028: ldc.i4.1 + IL_0029: ldnull + IL_002a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_002f: brfalse.s IL_0051 + IL_0031: ldloca.s V_1 + IL_0033: ldloc.0 + IL_0034: ldc.i4.0 + IL_0035: ldstr ""X"" + IL_003a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_003f: brfalse.s IL_0051 + IL_0041: ldloca.s V_1 + IL_0043: ldloc.0 + IL_0044: ldc.i4.2 + IL_0045: ldstr ""Y"" + IL_004a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_004f: br.s IL_0052 + IL_0051: ldc.i4.0 + IL_0052: pop + IL_0053: ldloca.s V_1 + IL_0055: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_005a: call ""void System.Console.WriteLine(string)"" + IL_005f: ret +} +", + (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: true) => @" +{ + // Code size 89 (0x59) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.4 + IL_0003: ldc.i4.4 + IL_0004: ldloca.s V_2 + IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_000b: stloc.1 + IL_000c: ldloc.2 + IL_000d: brfalse.s IL_004a + IL_000f: ldloca.s V_1 + IL_0011: ldstr ""base"" + IL_0016: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_001b: ldloca.s V_1 + IL_001d: ldloc.0 + IL_001e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0023: ldloca.s V_1 + IL_0025: ldloc.0 + IL_0026: ldc.i4.1 + IL_0027: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int)"" + IL_002c: ldloca.s V_1 + IL_002e: ldloc.0 + IL_002f: ldstr ""X"" + IL_0034: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)"" + IL_0039: ldloca.s V_1 + IL_003b: ldloc.0 + IL_003c: ldc.i4.2 + IL_003d: ldstr ""Y"" + IL_0042: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0047: ldc.i4.1 + IL_0048: br.s IL_004b + IL_004a: ldc.i4.0 + IL_004b: pop + IL_004c: ldloca.s V_1 + IL_004e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0053: call ""void System.Console.WriteLine(string)"" + IL_0058: ret +} +", + (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: true) => @" +{ + // Code size 93 (0x5d) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.4 + IL_0003: ldc.i4.4 + IL_0004: ldloca.s V_2 + IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_000b: stloc.1 + IL_000c: ldloc.2 + IL_000d: brfalse.s IL_004e + IL_000f: ldloca.s V_1 + IL_0011: ldstr ""base"" + IL_0016: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_001b: ldloca.s V_1 + IL_001d: ldloc.0 + IL_001e: ldc.i4.0 + IL_001f: ldnull + IL_0020: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0025: ldloca.s V_1 + IL_0027: ldloc.0 + IL_0028: ldc.i4.1 + IL_0029: ldnull + IL_002a: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_002f: ldloca.s V_1 + IL_0031: ldloc.0 + IL_0032: ldc.i4.0 + IL_0033: ldstr ""X"" + IL_0038: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_003d: ldloca.s V_1 + IL_003f: ldloc.0 + IL_0040: ldc.i4.2 + IL_0041: ldstr ""Y"" + IL_0046: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_004b: ldc.i4.1 + IL_004c: br.s IL_004f + IL_004e: ldc.i4.0 + IL_004f: pop + IL_0050: ldloca.s V_1 + IL_0052: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0057: call ""void System.Console.WriteLine(string)"" + IL_005c: ret +} +", + (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: true) => @" +{ + // Code size 96 (0x60) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.4 + IL_0003: ldc.i4.4 + IL_0004: ldloca.s V_2 + IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_000b: stloc.1 + IL_000c: ldloc.2 + IL_000d: brfalse.s IL_0051 + IL_000f: ldloca.s V_1 + IL_0011: ldstr ""base"" + IL_0016: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_001b: brfalse.s IL_0051 + IL_001d: ldloca.s V_1 + IL_001f: ldloc.0 + IL_0020: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0025: brfalse.s IL_0051 + IL_0027: ldloca.s V_1 + IL_0029: ldloc.0 + IL_002a: ldc.i4.1 + IL_002b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int)"" + IL_0030: brfalse.s IL_0051 + IL_0032: ldloca.s V_1 + IL_0034: ldloc.0 + IL_0035: ldstr ""X"" + IL_003a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, string)"" + IL_003f: brfalse.s IL_0051 + IL_0041: ldloca.s V_1 + IL_0043: ldloc.0 + IL_0044: ldc.i4.2 + IL_0045: ldstr ""Y"" + IL_004a: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_004f: br.s IL_0052 + IL_0051: ldc.i4.0 + IL_0052: pop + IL_0053: ldloca.s V_1 + IL_0055: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_005a: call ""void System.Console.WriteLine(string)"" + IL_005f: ret +} +", + (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: true) => @" +{ + // Code size 100 (0x64) + .maxstack 4 + .locals init (int V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.4 + IL_0003: ldc.i4.4 + IL_0004: ldloca.s V_2 + IL_0006: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_000b: stloc.1 + IL_000c: ldloc.2 + IL_000d: brfalse.s IL_0055 + IL_000f: ldloca.s V_1 + IL_0011: ldstr ""base"" + IL_0016: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_001b: brfalse.s IL_0055 + IL_001d: ldloca.s V_1 + IL_001f: ldloc.0 + IL_0020: ldc.i4.0 + IL_0021: ldnull + IL_0022: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0027: brfalse.s IL_0055 + IL_0029: ldloca.s V_1 + IL_002b: ldloc.0 + IL_002c: ldc.i4.1 + IL_002d: ldnull + IL_002e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0033: brfalse.s IL_0055 + IL_0035: ldloca.s V_1 + IL_0037: ldloc.0 + IL_0038: ldc.i4.0 + IL_0039: ldstr ""X"" + IL_003e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0043: brfalse.s IL_0055 + IL_0045: ldloca.s V_1 + IL_0047: ldloc.0 + IL_0048: ldc.i4.2 + IL_0049: ldstr ""Y"" + IL_004e: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int, int, string)"" + IL_0053: br.s IL_0056 + IL_0055: ldc.i4.0 + IL_0056: pop + IL_0057: ldloca.s V_1 + IL_0059: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_005e: call ""void System.Console.WriteLine(string)"" + IL_0063: ret +} +", + }; + } + + [Fact] + public void UseOfSpanInInterpolationHole_CSharp9() + { + var source = @" +using System; +ReadOnlySpan span = stackalloc char[1]; +Console.WriteLine($""{span}"");"; + + var comp = CreateCompilation(new[] { source, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false) }, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (4,22): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. + // Console.WriteLine($"{span}"); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "span").WithArguments("interpolated string handlers", "10.0").WithLocation(4, 22) + ); + } + + [ConditionalTheory(typeof(MonoOrCoreClrOnly))] + [CombinatorialData] + public void UseOfSpanInInterpolationHole(bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg) + { + var source = +@" +using System; +ReadOnlySpan a = ""1""; +System.Console.WriteLine($""base{a}{a,1}{a:X}{a,2:Y}"");"; + + string interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters, useBoolReturns, constructorBoolArg: constructorBoolArg); + + string expectedOutput = useDefaultParameters ? +@"base +value:1,alignment:0:format: +value:1,alignment:1:format: +value:1,alignment:0:format:X +value:1,alignment:2:format:Y" : +@"base +value:1 +value:1,alignment:1 +value:1:format:X +value:1,alignment:2:format:Y"; + + string expectedIl = getIl(); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: expectedOutput, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.Regular10); + verifier.VerifyIL("", expectedIl); + + var comp1 = CreateCompilation(interpolatedStringBuilder, targetFramework: TargetFramework.NetCoreApp); + + foreach (var reference in new[] { comp1.EmitToImageReference(), comp1.ToMetadataReference() }) + { + var comp2 = CreateCompilation(source, new[] { reference }, targetFramework: TargetFramework.NetCoreApp, parseOptions: TestOptions.Regular10); + verifier = CompileAndVerify(comp2, expectedOutput: expectedOutput); + verifier.VerifyIL("", expectedIl); + } + + string getIl() => (useDefaultParameters, useBoolReturns, constructorBoolArg) switch + { + (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: false) => @" +{ + // Code size 89 (0x59) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldloca.s V_1 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.4 + IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0014: ldloca.s V_1 + IL_0016: ldstr ""base"" + IL_001b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0020: ldloca.s V_1 + IL_0022: ldloc.0 + IL_0023: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan)"" + IL_0028: ldloca.s V_1 + IL_002a: ldloc.0 + IL_002b: ldc.i4.1 + IL_002c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int)"" + IL_0031: ldloca.s V_1 + IL_0033: ldloc.0 + IL_0034: ldstr ""X"" + IL_0039: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, string)"" + IL_003e: ldloca.s V_1 + IL_0040: ldloc.0 + IL_0041: ldc.i4.2 + IL_0042: ldstr ""Y"" + IL_0047: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_004c: ldloca.s V_1 + IL_004e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0053: call ""void System.Console.WriteLine(string)"" + IL_0058: ret +} +", + (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: false) => @" +{ + // Code size 93 (0x5d) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldloca.s V_1 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.4 + IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0014: ldloca.s V_1 + IL_0016: ldstr ""base"" + IL_001b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0020: ldloca.s V_1 + IL_0022: ldloc.0 + IL_0023: ldc.i4.0 + IL_0024: ldnull + IL_0025: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_002a: ldloca.s V_1 + IL_002c: ldloc.0 + IL_002d: ldc.i4.1 + IL_002e: ldnull + IL_002f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0034: ldloca.s V_1 + IL_0036: ldloc.0 + IL_0037: ldc.i4.0 + IL_0038: ldstr ""X"" + IL_003d: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0042: ldloca.s V_1 + IL_0044: ldloc.0 + IL_0045: ldc.i4.2 + IL_0046: ldstr ""Y"" + IL_004b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0050: ldloca.s V_1 + IL_0052: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0057: call ""void System.Console.WriteLine(string)"" + IL_005c: ret +} +", + (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: false) => @" +{ + // Code size 101 (0x65) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldloca.s V_1 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.4 + IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0014: ldloca.s V_1 + IL_0016: ldstr ""base"" + IL_001b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0020: brfalse.s IL_0056 + IL_0022: ldloca.s V_1 + IL_0024: ldloc.0 + IL_0025: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan)"" + IL_002a: brfalse.s IL_0056 + IL_002c: ldloca.s V_1 + IL_002e: ldloc.0 + IL_002f: ldc.i4.1 + IL_0030: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int)"" + IL_0035: brfalse.s IL_0056 + IL_0037: ldloca.s V_1 + IL_0039: ldloc.0 + IL_003a: ldstr ""X"" + IL_003f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, string)"" + IL_0044: brfalse.s IL_0056 + IL_0046: ldloca.s V_1 + IL_0048: ldloc.0 + IL_0049: ldc.i4.2 + IL_004a: ldstr ""Y"" + IL_004f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0054: br.s IL_0057 + IL_0056: ldc.i4.0 + IL_0057: pop + IL_0058: ldloca.s V_1 + IL_005a: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_005f: call ""void System.Console.WriteLine(string)"" + IL_0064: ret +} +", + (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: false) => @" +{ + // Code size 105 (0x69) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldloca.s V_1 + IL_000d: ldc.i4.4 + IL_000e: ldc.i4.4 + IL_000f: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0014: ldloca.s V_1 + IL_0016: ldstr ""base"" + IL_001b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0020: brfalse.s IL_005a + IL_0022: ldloca.s V_1 + IL_0024: ldloc.0 + IL_0025: ldc.i4.0 + IL_0026: ldnull + IL_0027: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_002c: brfalse.s IL_005a + IL_002e: ldloca.s V_1 + IL_0030: ldloc.0 + IL_0031: ldc.i4.1 + IL_0032: ldnull + IL_0033: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0038: brfalse.s IL_005a + IL_003a: ldloca.s V_1 + IL_003c: ldloc.0 + IL_003d: ldc.i4.0 + IL_003e: ldstr ""X"" + IL_0043: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0048: brfalse.s IL_005a + IL_004a: ldloca.s V_1 + IL_004c: ldloc.0 + IL_004d: ldc.i4.2 + IL_004e: ldstr ""Y"" + IL_0053: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0058: br.s IL_005b + IL_005a: ldc.i4.0 + IL_005b: pop + IL_005c: ldloca.s V_1 + IL_005e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0063: call ""void System.Console.WriteLine(string)"" + IL_0068: ret +} +", + (useDefaultParameters: false, useBoolReturns: false, constructorBoolArg: true) => @" +{ + // Code size 98 (0x62) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldc.i4.4 + IL_000c: ldc.i4.4 + IL_000d: ldloca.s V_2 + IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_0014: stloc.1 + IL_0015: ldloc.2 + IL_0016: brfalse.s IL_0053 + IL_0018: ldloca.s V_1 + IL_001a: ldstr ""base"" + IL_001f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0024: ldloca.s V_1 + IL_0026: ldloc.0 + IL_0027: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan)"" + IL_002c: ldloca.s V_1 + IL_002e: ldloc.0 + IL_002f: ldc.i4.1 + IL_0030: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int)"" + IL_0035: ldloca.s V_1 + IL_0037: ldloc.0 + IL_0038: ldstr ""X"" + IL_003d: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, string)"" + IL_0042: ldloca.s V_1 + IL_0044: ldloc.0 + IL_0045: ldc.i4.2 + IL_0046: ldstr ""Y"" + IL_004b: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0050: ldc.i4.1 + IL_0051: br.s IL_0054 + IL_0053: ldc.i4.0 + IL_0054: pop + IL_0055: ldloca.s V_1 + IL_0057: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_005c: call ""void System.Console.WriteLine(string)"" + IL_0061: ret +} +", + (useDefaultParameters: false, useBoolReturns: true, constructorBoolArg: true) => @" +{ + // Code size 105 (0x69) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldc.i4.4 + IL_000c: ldc.i4.4 + IL_000d: ldloca.s V_2 + IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_0014: stloc.1 + IL_0015: ldloc.2 + IL_0016: brfalse.s IL_005a + IL_0018: ldloca.s V_1 + IL_001a: ldstr ""base"" + IL_001f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0024: brfalse.s IL_005a + IL_0026: ldloca.s V_1 + IL_0028: ldloc.0 + IL_0029: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan)"" + IL_002e: brfalse.s IL_005a + IL_0030: ldloca.s V_1 + IL_0032: ldloc.0 + IL_0033: ldc.i4.1 + IL_0034: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int)"" + IL_0039: brfalse.s IL_005a + IL_003b: ldloca.s V_1 + IL_003d: ldloc.0 + IL_003e: ldstr ""X"" + IL_0043: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, string)"" + IL_0048: brfalse.s IL_005a + IL_004a: ldloca.s V_1 + IL_004c: ldloc.0 + IL_004d: ldc.i4.2 + IL_004e: ldstr ""Y"" + IL_0053: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0058: br.s IL_005b + IL_005a: ldc.i4.0 + IL_005b: pop + IL_005c: ldloca.s V_1 + IL_005e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0063: call ""void System.Console.WriteLine(string)"" + IL_0068: ret +} +", + (useDefaultParameters: true, useBoolReturns: false, constructorBoolArg: true) => @" +{ + // Code size 102 (0x66) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldc.i4.4 + IL_000c: ldc.i4.4 + IL_000d: ldloca.s V_2 + IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_0014: stloc.1 + IL_0015: ldloc.2 + IL_0016: brfalse.s IL_0057 + IL_0018: ldloca.s V_1 + IL_001a: ldstr ""base"" + IL_001f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0024: ldloca.s V_1 + IL_0026: ldloc.0 + IL_0027: ldc.i4.0 + IL_0028: ldnull + IL_0029: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_002e: ldloca.s V_1 + IL_0030: ldloc.0 + IL_0031: ldc.i4.1 + IL_0032: ldnull + IL_0033: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0038: ldloca.s V_1 + IL_003a: ldloc.0 + IL_003b: ldc.i4.0 + IL_003c: ldstr ""X"" + IL_0041: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0046: ldloca.s V_1 + IL_0048: ldloc.0 + IL_0049: ldc.i4.2 + IL_004a: ldstr ""Y"" + IL_004f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0054: ldc.i4.1 + IL_0055: br.s IL_0058 + IL_0057: ldc.i4.0 + IL_0058: pop + IL_0059: ldloca.s V_1 + IL_005b: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0060: call ""void System.Console.WriteLine(string)"" + IL_0065: ret +} +", + (useDefaultParameters: true, useBoolReturns: true, constructorBoolArg: true) => @" +{ + // Code size 109 (0x6d) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //a + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1, + bool V_2) + IL_0000: ldstr ""1"" + IL_0005: call ""System.ReadOnlySpan string.op_Implicit(string)"" + IL_000a: stloc.0 + IL_000b: ldc.i4.4 + IL_000c: ldc.i4.4 + IL_000d: ldloca.s V_2 + IL_000f: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int, out bool)"" + IL_0014: stloc.1 + IL_0015: ldloc.2 + IL_0016: brfalse.s IL_005e + IL_0018: ldloca.s V_1 + IL_001a: ldstr ""base"" + IL_001f: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0024: brfalse.s IL_005e + IL_0026: ldloca.s V_1 + IL_0028: ldloc.0 + IL_0029: ldc.i4.0 + IL_002a: ldnull + IL_002b: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_0030: brfalse.s IL_005e + IL_0032: ldloca.s V_1 + IL_0034: ldloc.0 + IL_0035: ldc.i4.1 + IL_0036: ldnull + IL_0037: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_003c: brfalse.s IL_005e + IL_003e: ldloca.s V_1 + IL_0040: ldloc.0 + IL_0041: ldc.i4.0 + IL_0042: ldstr ""X"" + IL_0047: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_004c: brfalse.s IL_005e + IL_004e: ldloca.s V_1 + IL_0050: ldloc.0 + IL_0051: ldc.i4.2 + IL_0052: ldstr ""Y"" + IL_0057: call ""bool System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan, int, string)"" + IL_005c: br.s IL_005f + IL_005e: ldc.i4.0 + IL_005f: pop + IL_0060: ldloca.s V_1 + IL_0062: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0067: call ""void System.Console.WriteLine(string)"" + IL_006c: ret +} +", + }; + } + + [Fact] + public void BoolReturns_ShortCircuit() + { + var source = @" +using System; +int a = 1; +Console.Write($""base{Throw()}{a = 2}""); +Console.WriteLine(a); +string Throw() => throw new Exception();"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: true, returnExpression: "false"); + + CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +base +1"); + } + + [Theory] + [CombinatorialData] + public void BoolOutParameter_ShortCircuits(bool useBoolReturns) + { + var source = @" +using System; +int a = 1; +Console.WriteLine(a); +Console.WriteLine($""{Throw()}{a = 2}""); +Console.WriteLine(a); +string Throw() => throw new Exception(); +"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: useBoolReturns, constructorBoolArg: true, constructorSuccessResult: false); + + CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +1 + +1"); + } + + [Fact] + public void AwaitInHoles_UsesFormat() + { + var source = @" +using System; +using System.Threading.Tasks; + +Console.WriteLine($""base{await Hole()}""); +Task Hole() => Task.FromResult(1);"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @"base1"); + + verifier.VerifyIL("$.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", @" +{ + // Code size 164 (0xa4) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Exception V_3) + IL_0000: ldarg.0 + IL_0001: ldfld ""int $.<
$>d__0.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_003e + IL_000a: call ""System.Threading.Tasks.Task $.<
$>g__Hole|0_0()"" + IL_000f: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0014: stloc.2 + IL_0015: ldloca.s V_2 + IL_0017: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_001c: brtrue.s IL_005a + IL_001e: ldarg.0 + IL_001f: ldc.i4.0 + IL_0020: dup + IL_0021: stloc.0 + IL_0022: stfld ""int $.<
$>d__0.<>1__state"" + IL_0027: ldarg.0 + IL_0028: ldloc.2 + IL_0029: stfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_002e: ldarg.0 + IL_002f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_0034: ldloca.s V_2 + IL_0036: ldarg.0 + IL_0037: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, $.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref $.<
$>d__0)"" + IL_003c: leave.s IL_00a3 + IL_003e: ldarg.0 + IL_003f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_0044: stloc.2 + IL_0045: ldarg.0 + IL_0046: ldflda ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_004b: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0051: ldarg.0 + IL_0052: ldc.i4.m1 + IL_0053: dup + IL_0054: stloc.0 + IL_0055: stfld ""int $.<
$>d__0.<>1__state"" + IL_005a: ldloca.s V_2 + IL_005c: call ""int System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0061: stloc.1 + IL_0062: ldstr ""base{0}"" + IL_0067: ldloc.1 + IL_0068: box ""int"" + IL_006d: call ""string string.Format(string, object)"" + IL_0072: call ""void System.Console.WriteLine(string)"" + IL_0077: leave.s IL_0090 + } + catch System.Exception + { + IL_0079: stloc.3 + IL_007a: ldarg.0 + IL_007b: ldc.i4.s -2 + IL_007d: stfld ""int $.<
$>d__0.<>1__state"" + IL_0082: ldarg.0 + IL_0083: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_0088: ldloc.3 + IL_0089: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_008e: leave.s IL_00a3 + } + IL_0090: ldarg.0 + IL_0091: ldc.i4.s -2 + IL_0093: stfld ""int $.<
$>d__0.<>1__state"" + IL_0098: ldarg.0 + IL_0099: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_009e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00a3: ret +} +"); + } + + [Fact] + public void NoAwaitInHoles_UsesBuilder() + { + var source = @" +using System; +using System.Threading.Tasks; + +var hole = await Hole(); +Console.WriteLine($""base{hole}""); +Task Hole() => Task.FromResult(1);"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +base +value:1"); + + verifier.VerifyIL("$.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" +{ + // Code size 185 (0xb9) + .maxstack 3 + .locals init (int V_0, + int V_1, //hole + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld ""int $.<
$>d__0.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_003e + IL_000a: call ""System.Threading.Tasks.Task $.<
$>g__Hole|0_0()"" + IL_000f: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0014: stloc.2 + IL_0015: ldloca.s V_2 + IL_0017: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_001c: brtrue.s IL_005a + IL_001e: ldarg.0 + IL_001f: ldc.i4.0 + IL_0020: dup + IL_0021: stloc.0 + IL_0022: stfld ""int $.<
$>d__0.<>1__state"" + IL_0027: ldarg.0 + IL_0028: ldloc.2 + IL_0029: stfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_002e: ldarg.0 + IL_002f: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_0034: ldloca.s V_2 + IL_0036: ldarg.0 + IL_0037: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, $.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref $.<
$>d__0)"" + IL_003c: leave.s IL_00b8 + IL_003e: ldarg.0 + IL_003f: ldfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_0044: stloc.2 + IL_0045: ldarg.0 + IL_0046: ldflda ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_004b: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0051: ldarg.0 + IL_0052: ldc.i4.m1 + IL_0053: dup + IL_0054: stloc.0 + IL_0055: stfld ""int $.<
$>d__0.<>1__state"" + IL_005a: ldloca.s V_2 + IL_005c: call ""int System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0061: stloc.1 + IL_0062: ldc.i4.4 + IL_0063: ldc.i4.1 + IL_0064: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0069: stloc.3 + IL_006a: ldloca.s V_3 + IL_006c: ldstr ""base"" + IL_0071: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0076: ldloca.s V_3 + IL_0078: ldloc.1 + IL_0079: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_007e: ldloca.s V_3 + IL_0080: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0085: call ""void System.Console.WriteLine(string)"" + IL_008a: leave.s IL_00a5 + } + catch System.Exception + { + IL_008c: stloc.s V_4 + IL_008e: ldarg.0 + IL_008f: ldc.i4.s -2 + IL_0091: stfld ""int $.<
$>d__0.<>1__state"" + IL_0096: ldarg.0 + IL_0097: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_009c: ldloc.s V_4 + IL_009e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_00a3: leave.s IL_00b8 + } + IL_00a5: ldarg.0 + IL_00a6: ldc.i4.s -2 + IL_00a8: stfld ""int $.<
$>d__0.<>1__state"" + IL_00ad: ldarg.0 + IL_00ae: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_00b3: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00b8: ret +} +"); + } + + [Fact] + public void NoAwaitInHoles_AwaitInExpression_UsesBuilder() + { + var source = @" +using System; +using System.Threading.Tasks; + +var hole = 2; +Test(await M(1), $""base{hole}"", await M(3)); +void Test(int i1, string s, int i2) => Console.WriteLine(s); +Task M(int i) +{ + Console.WriteLine(i); + return Task.FromResult(1); +}"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +1 +3 +base +value:2"); + + verifier.VerifyIL("$.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", @" +{ + // Code size 328 (0x148) + .maxstack 3 + .locals init (int V_0, + int V_1, + System.Runtime.CompilerServices.TaskAwaiter V_2, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_3, + System.Exception V_4) + IL_0000: ldarg.0 + IL_0001: ldfld ""int $.<
$>d__0.<>1__state"" + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0050 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq IL_00dc + IL_0011: ldarg.0 + IL_0012: ldc.i4.2 + IL_0013: stfld ""int $.<
$>d__0.5__2"" + IL_0018: ldc.i4.1 + IL_0019: call ""System.Threading.Tasks.Task $.<
$>g__M|0_1(int)"" + IL_001e: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_0023: stloc.2 + IL_0024: ldloca.s V_2 + IL_0026: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_002b: brtrue.s IL_006c + IL_002d: ldarg.0 + IL_002e: ldc.i4.0 + IL_002f: dup + IL_0030: stloc.0 + IL_0031: stfld ""int $.<
$>d__0.<>1__state"" + IL_0036: ldarg.0 + IL_0037: ldloc.2 + IL_0038: stfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_003d: ldarg.0 + IL_003e: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_0043: ldloca.s V_2 + IL_0045: ldarg.0 + IL_0046: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, $.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref $.<
$>d__0)"" + IL_004b: leave IL_0147 + IL_0050: ldarg.0 + IL_0051: ldfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_0056: stloc.2 + IL_0057: ldarg.0 + IL_0058: ldflda ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_005d: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_0063: ldarg.0 + IL_0064: ldc.i4.m1 + IL_0065: dup + IL_0066: stloc.0 + IL_0067: stfld ""int $.<
$>d__0.<>1__state"" + IL_006c: ldarg.0 + IL_006d: ldloca.s V_2 + IL_006f: call ""int System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_0074: stfld ""int $.<
$>d__0.<>7__wrap2"" + IL_0079: ldarg.0 + IL_007a: ldc.i4.4 + IL_007b: ldc.i4.1 + IL_007c: newobj ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0081: stloc.3 + IL_0082: ldloca.s V_3 + IL_0084: ldstr ""base"" + IL_0089: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_008e: ldloca.s V_3 + IL_0090: ldarg.0 + IL_0091: ldfld ""int $.<
$>d__0.5__2"" + IL_0096: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_009b: ldloca.s V_3 + IL_009d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_00a2: stfld ""string $.<
$>d__0.<>7__wrap3"" + IL_00a7: ldc.i4.3 + IL_00a8: call ""System.Threading.Tasks.Task $.<
$>g__M|0_1(int)"" + IL_00ad: callvirt ""System.Runtime.CompilerServices.TaskAwaiter System.Threading.Tasks.Task.GetAwaiter()"" + IL_00b2: stloc.2 + IL_00b3: ldloca.s V_2 + IL_00b5: call ""bool System.Runtime.CompilerServices.TaskAwaiter.IsCompleted.get"" + IL_00ba: brtrue.s IL_00f8 + IL_00bc: ldarg.0 + IL_00bd: ldc.i4.1 + IL_00be: dup + IL_00bf: stloc.0 + IL_00c0: stfld ""int $.<
$>d__0.<>1__state"" + IL_00c5: ldarg.0 + IL_00c6: ldloc.2 + IL_00c7: stfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_00cc: ldarg.0 + IL_00cd: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_00d2: ldloca.s V_2 + IL_00d4: ldarg.0 + IL_00d5: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted, $.<
$>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter, ref $.<
$>d__0)"" + IL_00da: leave.s IL_0147 + IL_00dc: ldarg.0 + IL_00dd: ldfld ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_00e2: stloc.2 + IL_00e3: ldarg.0 + IL_00e4: ldflda ""System.Runtime.CompilerServices.TaskAwaiter $.<
$>d__0.<>u__1"" + IL_00e9: initobj ""System.Runtime.CompilerServices.TaskAwaiter"" + IL_00ef: ldarg.0 + IL_00f0: ldc.i4.m1 + IL_00f1: dup + IL_00f2: stloc.0 + IL_00f3: stfld ""int $.<
$>d__0.<>1__state"" + IL_00f8: ldloca.s V_2 + IL_00fa: call ""int System.Runtime.CompilerServices.TaskAwaiter.GetResult()"" + IL_00ff: stloc.1 + IL_0100: ldarg.0 + IL_0101: ldfld ""int $.<
$>d__0.<>7__wrap2"" + IL_0106: ldarg.0 + IL_0107: ldfld ""string $.<
$>d__0.<>7__wrap3"" + IL_010c: ldloc.1 + IL_010d: call ""void $.<
$>g__Test|0_0(int, string, int)"" + IL_0112: ldarg.0 + IL_0113: ldnull + IL_0114: stfld ""string $.<
$>d__0.<>7__wrap3"" + IL_0119: leave.s IL_0134 + } + catch System.Exception + { + IL_011b: stloc.s V_4 + IL_011d: ldarg.0 + IL_011e: ldc.i4.s -2 + IL_0120: stfld ""int $.<
$>d__0.<>1__state"" + IL_0125: ldarg.0 + IL_0126: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_012b: ldloc.s V_4 + IL_012d: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_0132: leave.s IL_0147 + } + IL_0134: ldarg.0 + IL_0135: ldc.i4.s -2 + IL_0137: stfld ""int $.<
$>d__0.<>1__state"" + IL_013c: ldarg.0 + IL_013d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_0142: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_0147: ret +} +"); + } + + [Fact] + public void MissingCreate_01() + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + public override string ToString() => throw null; + public void Dispose() => throw null; + public void AppendLiteral(string value) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics( + // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 2 arguments + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "2").WithLocation(1, 5), + // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 3 arguments + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "3").WithLocation(1, 5) + ); + } + + [Fact] + public void MissingCreate_02() + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + public DefaultInterpolatedStringHandler(int literalLength) => throw null; + public override string ToString() => throw null; + public void Dispose() => throw null; + public void AppendLiteral(string value) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics( + // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 2 arguments + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "2").WithLocation(1, 5), + // (1,5): error CS1729: 'DefaultInterpolatedStringHandler' does not contain a constructor that takes 3 arguments + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "3").WithLocation(1, 5) + ); + } + + [Fact] + public void MissingCreate_03() + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + public DefaultInterpolatedStringHandler(ref int literalLength, int formattedCount) => throw null; + public override string ToString() => throw null; + public void Dispose() => throw null; + public void AppendLiteral(string value) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics( + // (1,5): error CS1620: Argument 1 must be passed with the 'ref' keyword + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BadArgRef, @"$""{(object)1}""").WithArguments("1", "ref").WithLocation(1, 5) + ); + } + + [Theory] + [InlineData(null)] + [InlineData("public string ToStringAndClear(int literalLength) => throw null;")] + [InlineData("public void ToStringAndClear() => throw null;")] + [InlineData("public static string ToStringAndClear() => throw null;")] + public void MissingWellKnownMethod_ToStringAndClear(string toStringAndClearMethod) + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; + " + toStringAndClearMethod + @" + public override string ToString() => throw null; + public void AppendLiteral(string value) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics(); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0656: Missing compiler required member 'System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear' + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", "ToStringAndClear").WithLocation(1, 5) + ); + } + + [Fact] + public void ObsoleteCreateMethod() + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + [System.Obsolete(""Constructor is obsolete"", error: true)] + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; + public void Dispose() => throw null; + public override string ToString() => throw null; + public void AppendLiteral(string value) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics( + // (1,5): error CS0619: 'DefaultInterpolatedStringHandler.DefaultInterpolatedStringHandler(int, int)' is obsolete: 'Constructor is obsolete' + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.DefaultInterpolatedStringHandler(int, int)", "Constructor is obsolete").WithLocation(1, 5) + ); + } + + [Fact] + public void ObsoleteAppendLiteralMethod() + { + var code = @"_ = $""base{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; + public void Dispose() => throw null; + public override string ToString() => throw null; + [System.Obsolete(""AppendLiteral is obsolete"", error: true)] + public void AppendLiteral(string value) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics( + // (1,7): error CS0619: 'DefaultInterpolatedStringHandler.AppendLiteral(string)' is obsolete: 'AppendLiteral is obsolete' + // _ = $"base{(object)1}"; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "base").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "AppendLiteral is obsolete").WithLocation(1, 7) + ); + } + + [Fact] + public void ObsoleteAppendFormattedMethod() + { + var code = @"_ = $""base{(object)1}"";"; + + var interpolatedStringBuilder = @" +namespace System.Runtime.CompilerServices +{ + public ref struct DefaultInterpolatedStringHandler + { + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) => throw null; + public void Dispose() => throw null; + public override string ToString() => throw null; + public void AppendLiteral(string value) => throw null; + [System.Obsolete(""AppendFormatted is obsolete"", error: true)] + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, interpolatedStringBuilder }); + comp.VerifyDiagnostics( + // (1,11): error CS0619: 'DefaultInterpolatedStringHandler.AppendFormatted(T, int, string)' is obsolete: 'AppendFormatted is obsolete' + // _ = $"base{(object)1}"; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "{(object)1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(T, int, string)", "AppendFormatted is obsolete").WithLocation(1, 11) + ); + } + + private const string UnmanagedCallersOnlyIl = @" +.class public auto ansi sealed beforefieldinit System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute extends [mscorlib]System.Attribute +{ + .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( + 01 00 40 00 00 00 01 00 54 02 09 49 6e 68 65 72 + 69 74 65 64 00 + ) + .field public class [mscorlib]System.Type[] CallConvs + .field public string EntryPoint + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + ldarg.0 + call instance void [mscorlib]System.Attribute::.ctor() + ret + } +}"; + + [Fact] + public void UnmanagedCallersOnlyAppendFormattedMethod() + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" +.class public sequential ansi sealed beforefieldinit System.Runtime.CompilerServices.DefaultInterpolatedStringHandler + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d + 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 + 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 + 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 + 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 + 69 6c 65 72 2e 01 00 00 + ) + .pack 0 + .size 1 + + .method public hidebysig specialname rtspecialname + instance void .ctor ( + int32 literalLength, + int32 formattedCount + ) cil managed + { + ldnull + throw + } + + .method public hidebysig + instance void Dispose () cil managed + { + ldnull + throw + } + + .method public hidebysig virtual + instance string ToString () cil managed + { + ldnull + throw + } + + .method public hidebysig + instance void AppendLiteral ( + string 'value' + ) cil managed + { + ldnull + throw + } + + .method public hidebysig + instance void AppendFormatted ( + !!T hole, + [opt] int32 'alignment', + [opt] string format + ) cil managed + { + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + .param [2] = int32(0) + .param [3] = nullref + ldnull + throw + } +} +"; + + var comp = CreateCompilationWithIL(code, ilSource: interpolatedStringBuilder + UnmanagedCallersOnlyIl); + comp.VerifyDiagnostics( + // (1,7): error CS0570: 'DefaultInterpolatedStringHandler.AppendFormatted(T, int, string)' is not supported by the language + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BindToBogus, "{(object)1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(T, int, string)").WithLocation(1, 7) + ); + } + + [Fact] + public void UnmanagedCallersOnlyToStringMethod() + { + var code = @"_ = $""{(object)1}"";"; + + var interpolatedStringBuilder = @" + +.class public sequential ansi sealed beforefieldinit System.Runtime.CompilerServices.DefaultInterpolatedStringHandler + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.IsByRefLikeAttribute::.ctor() = ( + 01 00 00 00 + ) + .custom instance void [mscorlib]System.ObsoleteAttribute::.ctor(string, bool) = ( + 01 00 52 54 79 70 65 73 20 77 69 74 68 20 65 6d + 62 65 64 64 65 64 20 72 65 66 65 72 65 6e 63 65 + 73 20 61 72 65 20 6e 6f 74 20 73 75 70 70 6f 72 + 74 65 64 20 69 6e 20 74 68 69 73 20 76 65 72 73 + 69 6f 6e 20 6f 66 20 79 6f 75 72 20 63 6f 6d 70 + 69 6c 65 72 2e 01 00 00 + ) + .pack 0 + .size 1 + + .method public hidebysig specialname rtspecialname + instance void .ctor ( + int32 literalLength, + int32 formattedCount + ) cil managed + { + ldnull + throw + } + + .method public hidebysig instance string ToStringAndClear () cil managed + { + .custom instance void System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + ldnull + throw + } + + .method public hidebysig + instance void AppendLiteral ( + string 'value' + ) cil managed + { + ldnull + throw + } + + .method public hidebysig + instance void AppendFormatted ( + !!T hole, + [opt] int32 'alignment', + [opt] string format + ) cil managed + { + .param [2] = int32(0) + .param [3] = nullref + ldnull + throw + } +} +"; + + var comp = CreateCompilationWithIL(code, ilSource: interpolatedStringBuilder + UnmanagedCallersOnlyIl); + comp.VerifyDiagnostics(); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0570: 'DefaultInterpolatedStringHandler.ToStringAndClear()' is not supported by the language + // _ = $"{(object)1}"; + Diagnostic(ErrorCode.ERR_BindToBogus, @"$""{(object)1}""").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()").WithLocation(1, 5) + ); + } + + [Fact] + public void UnsupportedArgumentType() + { + var source = @" +unsafe +{ + int* i = null; + var s = new S(); + _ = $""{i}{s}""; +} +ref struct S +{ +}"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: true, useBoolReturns: false); + + var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (6,11): error CS0306: The type 'int*' may not be used as a type argument + // _ = $"{i}{s}"; + Diagnostic(ErrorCode.ERR_BadTypeArgument, "{i}").WithArguments("int*").WithLocation(6, 11), + // (6,14): error CS0306: The type 'S' may not be used as a type argument + // _ = $"{i}{s}"; + Diagnostic(ErrorCode.ERR_BadTypeArgument, "{s}").WithArguments("S").WithLocation(6, 14) + ); + } + + [Fact] + public void TargetTypedInterpolationHoles() + { + var source = @" +bool b = true; +System.Console.WriteLine($""{b switch { true => 1, false => null }}{(!b ? null : 2)}{default}{null}"");"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +value:1 +value:2 +value: +value:"); + + verifier.VerifyIL("", @" +{ + // Code size 81 (0x51) + .maxstack 3 + .locals init (bool V_0, //b + object V_1, + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_2 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.4 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloc.0 + IL_000c: brfalse.s IL_0017 + IL_000e: ldc.i4.1 + IL_000f: box ""int"" + IL_0014: stloc.1 + IL_0015: br.s IL_0019 + IL_0017: ldnull + IL_0018: stloc.1 + IL_0019: ldloca.s V_2 + IL_001b: ldloc.1 + IL_001c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)"" + IL_0021: ldloca.s V_2 + IL_0023: ldloc.0 + IL_0024: brfalse.s IL_002e + IL_0026: ldc.i4.2 + IL_0027: box ""int"" + IL_002c: br.s IL_002f + IL_002e: ldnull + IL_002f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)"" + IL_0034: ldloca.s V_2 + IL_0036: ldnull + IL_0037: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_003c: ldloca.s V_2 + IL_003e: ldnull + IL_003f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(string)"" + IL_0044: ldloca.s V_2 + IL_0046: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_004b: call ""void System.Console.WriteLine(string)"" + IL_0050: ret +} +"); + } + + [Fact] + public void TargetTypedInterpolationHoles_Errors() + { + var source = @"System.Console.WriteLine($""{(null, default)}{new()}"");"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (1,29): error CS1503: Argument 1: cannot convert from '(, default)' to 'object' + // System.Console.WriteLine($"{(null, default)}{new()}"); + Diagnostic(ErrorCode.ERR_BadArgType, "(null, default)").WithArguments("1", "(, default)", "object").WithLocation(1, 29), + // (1,29): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. + // System.Console.WriteLine($"{(null, default)}{new()}"); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(null, default)").WithArguments("interpolated string handlers", "10.0").WithLocation(1, 29), + // (1,46): error CS1729: 'string' does not contain a constructor that takes 0 arguments + // System.Console.WriteLine($"{(null, default)}{new()}"); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "new()").WithArguments("string", "0").WithLocation(1, 46) + ); + } + + [Fact] + public void RefTernary() + { + var source = @" +bool b = true; +int i = 1; +System.Console.WriteLine($""{(!b ? ref i : ref i)}"");"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @"value:1"); + } + + [Fact] + public void NestedInterpolatedStrings() + { + var source = @" +int i = 1; +System.Console.WriteLine($""{$""{i}""}"");"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @"value:1"); + + verifier.VerifyIL("", @" +{ + // Code size 32 (0x20) + .maxstack 3 + .locals init (int V_0, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.1 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldloc.0 + IL_000e: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0013: ldloca.s V_1 + IL_0015: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_001a: call ""void System.Console.WriteLine(string)"" + IL_001f: ret +} +"); + } + + [Fact] + public void ExceptionFilter_01() + { + var source = @" +using System; + +int i = 1; +try +{ + Console.WriteLine(""Starting try""); + throw new MyException { Prop = i }; +} +// Test DefaultInterpolatedStringHandler renders specially, so we're actually comparing to ""value:Prop"" plus some whitespace +catch (MyException e) when (e.ToString() == $""{i}"".Trim()) +{ + Console.WriteLine(""Caught""); +} + +class MyException : Exception +{ + public int Prop { get; set; } + public override string ToString() => ""value:"" + Prop.ToString(); +}"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +Starting try +Caught"); + + verifier.VerifyIL("", @" +{ + // Code size 95 (0x5f) + .maxstack 4 + .locals init (int V_0, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + .try + { + IL_0002: ldstr ""Starting try"" + IL_0007: call ""void System.Console.WriteLine(string)"" + IL_000c: newobj ""MyException..ctor()"" + IL_0011: dup + IL_0012: ldloc.0 + IL_0013: callvirt ""void MyException.Prop.set"" + IL_0018: throw + } + filter + { + IL_0019: isinst ""MyException"" + IL_001e: dup + IL_001f: brtrue.s IL_0025 + IL_0021: pop + IL_0022: ldc.i4.0 + IL_0023: br.s IL_004f + IL_0025: callvirt ""string object.ToString()"" + IL_002a: ldloca.s V_1 + IL_002c: ldc.i4.0 + IL_002d: ldc.i4.1 + IL_002e: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0033: ldloca.s V_1 + IL_0035: ldloc.0 + IL_0036: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_003b: ldloca.s V_1 + IL_003d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0042: callvirt ""string string.Trim()"" + IL_0047: call ""bool string.op_Equality(string, string)"" + IL_004c: ldc.i4.0 + IL_004d: cgt.un + IL_004f: endfilter + } // end filter + { // handler + IL_0051: pop + IL_0052: ldstr ""Caught"" + IL_0057: call ""void System.Console.WriteLine(string)"" + IL_005c: leave.s IL_005e + } + IL_005e: ret +} +"); + } + + [ConditionalFact(typeof(MonoOrCoreClrOnly), typeof(NoIOperationValidation))] + public void ExceptionFilter_02() + { + var source = @" +using System; + +ReadOnlySpan s = new char[] { 'i' }; +try +{ + Console.WriteLine(""Starting try""); + throw new MyException { Prop = s.ToString() }; +} +// Test DefaultInterpolatedStringHandler renders specially, so we're actually comparing to ""value:Prop"" plus some whitespace +catch (MyException e) when (e.ToString() == $""{s}"".Trim()) +{ + Console.WriteLine(""Caught""); +} + +class MyException : Exception +{ + public string Prop { get; set; } + public override string ToString() => ""value:"" + Prop.ToString(); +}"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, targetFramework: TargetFramework.NetCoreApp, expectedOutput: @" +Starting try +Caught"); + + + verifier.VerifyIL("", @" +{ + // Code size 122 (0x7a) + .maxstack 4 + .locals init (System.ReadOnlySpan V_0, //s + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: newarr ""char"" + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.s 105 + IL_000a: stelem.i2 + IL_000b: call ""System.ReadOnlySpan System.ReadOnlySpan.op_Implicit(char[])"" + IL_0010: stloc.0 + .try + { + IL_0011: ldstr ""Starting try"" + IL_0016: call ""void System.Console.WriteLine(string)"" + IL_001b: newobj ""MyException..ctor()"" + IL_0020: dup + IL_0021: ldloca.s V_0 + IL_0023: constrained. ""System.ReadOnlySpan"" + IL_0029: callvirt ""string object.ToString()"" + IL_002e: callvirt ""void MyException.Prop.set"" + IL_0033: throw + } + filter + { + IL_0034: isinst ""MyException"" + IL_0039: dup + IL_003a: brtrue.s IL_0040 + IL_003c: pop + IL_003d: ldc.i4.0 + IL_003e: br.s IL_006a + IL_0040: callvirt ""string object.ToString()"" + IL_0045: ldloca.s V_1 + IL_0047: ldc.i4.0 + IL_0048: ldc.i4.1 + IL_0049: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_004e: ldloca.s V_1 + IL_0050: ldloc.0 + IL_0051: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan)"" + IL_0056: ldloca.s V_1 + IL_0058: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_005d: callvirt ""string string.Trim()"" + IL_0062: call ""bool string.op_Equality(string, string)"" + IL_0067: ldc.i4.0 + IL_0068: cgt.un + IL_006a: endfilter + } // end filter + { // handler + IL_006c: pop + IL_006d: ldstr ""Caught"" + IL_0072: call ""void System.Console.WriteLine(string)"" + IL_0077: leave.s IL_0079 + } + IL_0079: ret +} +"); + } + + [ConditionalFact(typeof(MonoOrCoreClrOnly), typeof(NoIOperationValidation))] + public void ImplicitUserDefinedConversionInHole() + { + var source = @" +using System; + +S s = default; +C c = new C(); +Console.WriteLine($""{s}{c}""); + +ref struct S +{ + public static implicit operator ReadOnlySpan(S s) => ""S converted""; +} +class C +{ + public static implicit operator ReadOnlySpan(C s) => ""C converted""; +}"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false); + + var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, + targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:S converted +value:C"); + + verifier.VerifyIL("", @" +{ + // Code size 57 (0x39) + .maxstack 3 + .locals init (S V_0, //s + C V_1, //c + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) + IL_0000: ldloca.s V_0 + IL_0002: initobj ""S"" + IL_0008: newobj ""C..ctor()"" + IL_000d: stloc.1 + IL_000e: ldloca.s V_2 + IL_0010: ldc.i4.0 + IL_0011: ldc.i4.2 + IL_0012: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0017: ldloca.s V_2 + IL_0019: ldloc.0 + IL_001a: call ""System.ReadOnlySpan S.op_Implicit(S)"" + IL_001f: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan)"" + IL_0024: ldloca.s V_2 + IL_0026: ldloc.1 + IL_0027: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(C)"" + IL_002c: ldloca.s V_2 + IL_002e: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0033: call ""void System.Console.WriteLine(string)"" + IL_0038: ret +} +"); + } + + [Fact] + public void ExplicitUserDefinedConversionInHole() + { + var source = @" +using System; + +S s = default; +Console.WriteLine($""{s}""); + +ref struct S +{ + public static explicit operator ReadOnlySpan(S s) => ""S converted""; +} +"; + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters: false, useBoolReturns: false); + + var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (5,21): error CS0306: The type 'S' may not be used as a type argument + // Console.WriteLine($"{s}"); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "{s}").WithArguments("S").WithLocation(5, 21) + ); + } + + [Fact] + public void ImplicitUserDefinedConversionInLiteral() + { + var source = @" +using System; + +Console.WriteLine($""Text{1}""); + +public struct CustomStruct +{ + public static implicit operator CustomStruct(string s) => new CustomStruct { S = s }; + public string S { get; set; } + public override string ToString() => ""literal:"" + S; +} + +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public string ToStringAndClear() => _builder.ToString(); + public void AppendLiteral(CustomStruct s) => _builder.AppendLine(s.ToString()); + public void AppendFormatted(object o) => _builder.AppendLine(""value:"" + o.ToString()); + } +}"; + + var verifier = CompileAndVerify(source, expectedOutput: @" +literal:Text +value:1"); + verifier.VerifyIL("", @" +{ + // Code size 52 (0x34) + .maxstack 3 + .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.4 + IL_0003: ldc.i4.1 + IL_0004: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldstr ""Text"" + IL_0010: call ""CustomStruct CustomStruct.op_Implicit(string)"" + IL_0015: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(CustomStruct)"" + IL_001a: ldloca.s V_0 + IL_001c: ldc.i4.1 + IL_001d: box ""int"" + IL_0022: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)"" + IL_0027: ldloca.s V_0 + IL_0029: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_002e: call ""void System.Console.WriteLine(string)"" + IL_0033: ret +} +"); + } + + [Fact] + public void ExplicitUserDefinedConversionInLiteral() + { + var source = @" +using System; + +Console.WriteLine($""Text{1}""); + +public struct CustomStruct +{ + public static explicit operator CustomStruct(string s) => new CustomStruct { S = s }; + public string S { get; set; } + public override string ToString() => ""literal:"" + S; +} + +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public string ToStringAndClear() => _builder.ToString(); + public void AppendLiteral(CustomStruct s) => _builder.AppendLine(s.ToString()); + public void AppendFormatted(object o) => _builder.AppendLine(""value:"" + o.ToString()); + } +}"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,21): error CS1503: Argument 1: cannot convert from 'string' to 'CustomStruct' + // Console.WriteLine($"Text{1}"); + Diagnostic(ErrorCode.ERR_BadArgType, "Text").WithArguments("1", "string", "CustomStruct").WithLocation(4, 21) + ); + } + + [Fact] + public void InvalidBuilderReturnType() + { + var source = @" +using System; + +Console.WriteLine($""Text{1}""); + +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public string ToStringAndClear() => _builder.ToString(); + public int AppendLiteral(string s) => 0; + public int AppendFormatted(object o) => 0; + } +}"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,21): error CS8941: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendLiteral(string)' is malformed. It does not return 'void' or 'bool'. + // Console.WriteLine($"Text{1}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)").WithLocation(4, 21), + // (4,25): error CS8941: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendFormatted(object)' is malformed. It does not return 'void' or 'bool'. + // Console.WriteLine($"Text{1}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)").WithLocation(4, 25) + ); + } + + [Fact] + public void MixedBuilderReturnTypes_01() + { + var source = @" +using System; + +Console.WriteLine($""Text{1}""); +Console.WriteLine($""{1}Text""); + +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public string ToStringAndClear() => _builder.ToString(); + public bool AppendLiteral(string s) => true; + public void AppendFormatted(object o) { } + } +}"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,25): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendFormatted(object)' has inconsistent return type. Expected to return 'bool'. + // Console.WriteLine($"Text{1}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)", "bool").WithLocation(4, 25), + // (5,24): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendLiteral(string)' has inconsistent return type. Expected to return 'void'. + // Console.WriteLine($"{1}Text"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "void").WithLocation(5, 24) + ); + } + + [Fact] + public void MixedBuilderReturnTypes_02() + { + var source = @" +using System; + +Console.WriteLine($""Text{1}""); +Console.WriteLine($""{1}Text""); + +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public string ToStringAndClear() => _builder.ToString(); + public void AppendLiteral(string s) { } + public bool AppendFormatted(object o) => true; + } +}"; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,25): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendFormatted(object)' has inconsistent return type. Expected to return 'void'. + // Console.WriteLine($"Text{1}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)", "void").WithLocation(4, 25), + // (5,24): error CS8942: Interpolated string handler method 'DefaultInterpolatedStringHandler.AppendLiteral(string)' has inconsistent return type. Expected to return 'bool'. + // Console.WriteLine($"{1}Text"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "bool").WithLocation(5, 24) + ); + } + + [Fact] + public void MixedBuilderReturnTypes_03() + { + var source = @" +using System; + +Console.WriteLine($""{1}""); + +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public string ToStringAndClear() => _builder.ToString(); + public bool AppendLiteral(string s) => true; + public void AppendFormatted(object o) + { + _builder.AppendLine(""value:"" + o.ToString()); + } + } +}"; + + CompileAndVerify(source, expectedOutput: "value:1"); + } + + [Fact] + public void MixedBuilderReturnTypes_04() + { + var source = @" +using System; +using System.Text; +using System.Runtime.CompilerServices; + +Console.WriteLine((CustomHandler)$""l""); + +[InterpolatedStringHandler] +public class CustomHandler +{ + private readonly StringBuilder _builder; + public CustomHandler(int literalLength, int formattedCount) + { + _builder = new StringBuilder(); + } + public override string ToString() => _builder.ToString(); + public bool AppendFormatted(object o) => true; + public void AppendLiteral(string s) + { + _builder.AppendLine(""literal:"" + s.ToString()); + } +} +"; + + CompileAndVerify(new[] { source, InterpolatedStringHandlerAttribute }, expectedOutput: "literal:l"); + } + + private static void VerifyInterpolatedStringExpression(CSharpCompilation comp, string handlerType = "CustomHandler") + { + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var interpolatedString = tree.GetRoot().DescendantNodes().OfType().Single(); + var semanticInfo = model.GetSemanticInfoSummary(interpolatedString); + + Assert.Equal(SpecialType.System_String, semanticInfo.Type.SpecialType); + Assert.Equal(handlerType, semanticInfo.ConvertedType.ToTestDisplayString()); + Assert.Equal(ConversionKind.InterpolatedStringHandler, semanticInfo.ImplicitConversion.Kind); + Assert.True(semanticInfo.ImplicitConversion.Exists); + Assert.True(semanticInfo.ImplicitConversion.IsValid); + Assert.True(semanticInfo.ImplicitConversion.IsInterpolatedStringHandler); + Assert.Null(semanticInfo.ImplicitConversion.Method); + + // https://github.com/dotnet/roslyn/issues/54505 Assert IConversionOperation.IsImplicit when IOperation is implemented for interpolated strings. + } + + private CompilationVerifier CompileAndVerifyOnCorrectPlatforms(CSharpCompilation compilation, string expectedOutput) + => CompileAndVerify( + compilation, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null, + verify: ExecutionConditionUtil.IsMonoOrCoreClr ? Verification.Passes : Verification.Skipped); + + [Theory] + [CombinatorialData] + public void CustomHandlerLocal([CombinatorialValues("class", "struct")] string type, bool useBoolReturns) + { + var code = @" +CustomHandler builder = $""Literal{1,2:f}""; +System.Console.WriteLine(builder.ToString());"; + + var builder = GetInterpolatedStringCustomHandlerType("CustomHandler", type, useBoolReturns); + var comp = CreateCompilation(new[] { code, builder }); + VerifyInterpolatedStringExpression(comp); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +literal:Literal +value:1 +alignment:2 +format:f"); + + verifier.VerifyIL("", getIl()); + + string getIl() => (type, useBoolReturns) switch + { + (type: "struct", useBoolReturns: true) => @" +{ + // Code size 67 (0x43) + .maxstack 4 + .locals init (CustomHandler V_0, //builder + CustomHandler V_1) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_1 + IL_000b: ldstr ""Literal"" + IL_0010: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0015: brfalse.s IL_002c + IL_0017: ldloca.s V_1 + IL_0019: ldc.i4.1 + IL_001a: box ""int"" + IL_001f: ldc.i4.2 + IL_0020: ldstr ""f"" + IL_0025: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_002a: br.s IL_002d + IL_002c: ldc.i4.0 + IL_002d: pop + IL_002e: ldloc.1 + IL_002f: stloc.0 + IL_0030: ldloca.s V_0 + IL_0032: constrained. ""CustomHandler"" + IL_0038: callvirt ""string object.ToString()"" + IL_003d: call ""void System.Console.WriteLine(string)"" + IL_0042: ret +} +", + (type: "struct", useBoolReturns: false) => @" +{ + // Code size 61 (0x3d) + .maxstack 4 + .locals init (CustomHandler V_0, //builder + CustomHandler V_1) + IL_0000: ldloca.s V_1 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_1 + IL_000b: ldstr ""Literal"" + IL_0010: call ""void CustomHandler.AppendLiteral(string)"" + IL_0015: ldloca.s V_1 + IL_0017: ldc.i4.1 + IL_0018: box ""int"" + IL_001d: ldc.i4.2 + IL_001e: ldstr ""f"" + IL_0023: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0028: ldloc.1 + IL_0029: stloc.0 + IL_002a: ldloca.s V_0 + IL_002c: constrained. ""CustomHandler"" + IL_0032: callvirt ""string object.ToString()"" + IL_0037: call ""void System.Console.WriteLine(string)"" + IL_003c: ret +} +", + (type: "class", useBoolReturns: true) => @" +{ + // Code size 55 (0x37) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldstr ""Literal"" + IL_000e: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0013: brfalse.s IL_0029 + IL_0015: ldloc.0 + IL_0016: ldc.i4.1 + IL_0017: box ""int"" + IL_001c: ldc.i4.2 + IL_001d: ldstr ""f"" + IL_0022: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: callvirt ""string object.ToString()"" + IL_0031: call ""void System.Console.WriteLine(string)"" + IL_0036: ret +} +", + (type: "class", useBoolReturns: false) => @" +{ + // Code size 47 (0x2f) + .maxstack 5 + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: dup + IL_0008: ldstr ""Literal"" + IL_000d: callvirt ""void CustomHandler.AppendLiteral(string)"" + IL_0012: dup + IL_0013: ldc.i4.1 + IL_0014: box ""int"" + IL_0019: ldc.i4.2 + IL_001a: ldstr ""f"" + IL_001f: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0024: callvirt ""string object.ToString()"" + IL_0029: call ""void System.Console.WriteLine(string)"" + IL_002e: ret +} +", + _ => throw ExceptionUtilities.Unreachable + }; + } + + [Fact] + public void CustomHandlerMethodArgument() + { + var code = @" +M($""{1,2:f}Literal""); +void M(CustomHandler b) +{ + System.Console.WriteLine(b.ToString()); +}"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL(@"", @" +{ + // Code size 50 (0x32) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_0031: ret +} +"); + } + + [Fact] + public void ExplicitHandlerCast_InCode() + { + var code = @"System.Console.WriteLine((CustomHandler)$""{1,2:f}Literal"");"; + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + SyntaxNode syntax = tree.GetRoot().DescendantNodes().OfType().Single(); + var semanticInfo = model.GetSemanticInfoSummary(syntax); + Assert.Equal("CustomHandler", semanticInfo.Type.ToTestDisplayString()); + Assert.Equal(SpecialType.System_Object, semanticInfo.ConvertedType.SpecialType); + Assert.Equal(ConversionKind.ImplicitReference, semanticInfo.ImplicitConversion.Kind); + + syntax = ((CastExpressionSyntax)syntax).Expression; + Assert.IsType(syntax); + semanticInfo = model.GetSemanticInfoSummary(syntax); + Assert.Equal(SpecialType.System_String, semanticInfo.Type.SpecialType); + Assert.Equal(SpecialType.System_String, semanticInfo.ConvertedType.SpecialType); + Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind); + + // https://github.com/dotnet/roslyn/issues/54505 Assert cast is explicit after IOperation is implemented + + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 42 (0x2a) + .maxstack 5 + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: dup + IL_0008: ldc.i4.1 + IL_0009: box ""int"" + IL_000e: ldc.i4.2 + IL_000f: ldstr ""f"" + IL_0014: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0019: dup + IL_001a: ldstr ""Literal"" + IL_001f: callvirt ""void CustomHandler.AppendLiteral(string)"" + IL_0024: call ""void System.Console.WriteLine(object)"" + IL_0029: ret +} +"); + } + + [Fact] + public void HandlerConversionPreferredOverStringForNonConstant() + { + var code = @" +C.M($""{1,2:f}Literal""); +class C +{ + public static void M(CustomHandler b) + { + System.Console.WriteLine(b.ToString()); + } + public static void M(string s) + { + throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL(@"", @" +{ + // Code size 50 (0x32) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: call ""void C.M(CustomHandler)"" + IL_0031: ret +} +"); + } + + [Fact] + public void StringPreferredOverHandlerConversionForConstant() + { + var code = @" +C.M($""{""Literal""}""); +class C +{ + public static void M(CustomHandler b) + { + throw null; + } + public static void M(string s) + { + System.Console.WriteLine(s); + } +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + var verifier = CompileAndVerify(comp, expectedOutput: @"Literal"); + + verifier.VerifyIL(@"", @" +{ + // Code size 11 (0xb) + .maxstack 1 + IL_0000: ldstr ""Literal"" + IL_0005: call ""void C.M(string)"" + IL_000a: ret +} +"); + } + + [Fact] + public void HandlerConversionPreferredOverStringForNonConstant_AttributeConstructor() + { + var code = @" +using System; + +[Attr($""{1}"")] +class Attr : Attribute +{ + public Attr(string s) {} + public Attr(CustomHandler c) {} +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + comp.VerifyDiagnostics( + // (4,2): error CS0181: Attribute constructor parameter 'c' has type 'CustomHandler', which is not a valid attribute parameter type + // [Attr($"{1}")] + Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Attr").WithArguments("c", "CustomHandler").WithLocation(4, 2) + ); + VerifyInterpolatedStringExpression(comp); + + var attr = comp.SourceAssembly.SourceModule.GlobalNamespace.GetTypeMember("Attr"); + Assert.Equal("Attr..ctor(CustomHandler c)", attr.GetAttributes().Single().AttributeConstructor.ToTestDisplayString()); + } + + [Fact] + public void StringPreferredOverHandlerConversionForConstant_AttributeConstructor() + { + var code = @" +using System; + +[Attr($""{""Literal""}"")] +class Attr : Attribute +{ + public Attr(string s) {} + public Attr(CustomHandler c) {} +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate); + + void validate(ModuleSymbol m) + { + var attr = m.GlobalNamespace.GetTypeMember("Attr"); + Assert.Equal("Attr..ctor(System.String s)", attr.GetAttributes().Single().AttributeConstructor.ToTestDisplayString()); + } + } + + [Fact] + public void MultipleBuilderTypes() + { + var code = @" +C.M($""""); + +class C +{ + public static void M(CustomHandler1 c) => throw null; + public static void M(CustomHandler2 c) => throw null; +}"; + + var comp = CreateCompilation(new[] + { + code, + GetInterpolatedStringCustomHandlerType("CustomHandler1", "struct", useBoolReturns: false), + GetInterpolatedStringCustomHandlerType("CustomHandler2", "struct", useBoolReturns: false, includeOneTimeHelpers: false) + }); + + comp.VerifyDiagnostics( + // (2,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(CustomHandler1)' and 'C.M(CustomHandler2)' + // C.M($""); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(CustomHandler1)", "C.M(CustomHandler2)").WithLocation(2, 3) + ); + } + + [Fact] + public void GenericOverloadResolution_01() + { + var code = @" +using System; + +C.M($""{1,2:f}Literal""); + +class C +{ + public static void M(T t) => throw null; + public static void M(CustomHandler c) => Console.WriteLine(c); +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 50 (0x32) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: call ""void C.M(CustomHandler)"" + IL_0031: ret +} +"); + } + + [Fact] + public void GenericOverloadResolution_02() + { + var code = @" +using System; + +C.M($""{1,2:f}Literal""); + +class C +{ + public static void M(T t) where T : CustomHandler => throw null; + public static void M(CustomHandler c) => Console.WriteLine(c); +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 50 (0x32) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: call ""void C.M(CustomHandler)"" + IL_0031: ret +} +"); + } + + [Fact] + public void GenericOverloadResolution_03() + { + var code = @" +C.M($""{1,2:f}Literal""); + +class C +{ + public static void M(T t) where T : CustomHandler => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + comp.VerifyDiagnostics( + // (2,3): error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'C.M(T)'. There is no implicit reference conversion from 'string' to 'CustomHandler'. + // C.M($"{1,2:f}Literal"); + Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedRefType, "M").WithArguments("C.M(T)", "CustomHandler", "T", "string").WithLocation(2, 3) + ); + } + + [Fact] + public void GenericInference_01() + { + var code = @" +C.M($""{1,2:f}Literal"", default(CustomHandler)); +C.M(default(CustomHandler), $""{1,2:f}Literal""); + +class C +{ + public static void M(T t1, T t2) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics( + // (2,3): error CS0411: The type arguments for method 'C.M(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // C.M($"{1,2:f}Literal", default(CustomHandler)); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M(T, T)").WithLocation(2, 3), + // (3,3): error CS0411: The type arguments for method 'C.M(T, T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // C.M(default(CustomHandler), $"{1,2:f}Literal"); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M(T, T)").WithLocation(3, 3) + ); + } + + [Fact] + public void GenericInference_02() + { + var code = @" +using System; +C.M(default(CustomHandler), () => $""{1,2:f}Literal""); + +class C +{ + public static void M(T t1, Func t2) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + comp.VerifyDiagnostics( + // (3,3): error CS0411: The type arguments for method 'C.M(T, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // C.M(default(CustomHandler), () => $"{1,2:f}Literal"); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("C.M(T, System.Func)").WithLocation(3, 3) + ); + } + + [Fact] + public void GenericInference_03() + { + var code = @" +using System; +C.M($""{1,2:f}Literal"", default(CustomHandler)); + +class C +{ + public static void M(T t1, T t2) => Console.WriteLine(t1); +} + +partial class CustomHandler +{ + public static implicit operator CustomHandler(string s) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 51 (0x33) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: ldnull + IL_002d: call ""void C.M(CustomHandler, CustomHandler)"" + IL_0032: ret +} +"); + } + + [Fact] + public void GenericInference_04() + { + var code = @" +using System; +C.M(default(CustomHandler), () => $""{1,2:f}Literal""); + +class C +{ + public static void M(T t1, Func t2) => Console.WriteLine(t2()); +} + +partial class CustomHandler +{ + public static implicit operator CustomHandler(string s) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("$.<>c.<
$>b__0_0()", @" +{ + // Code size 45 (0x2d) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_01() + { + var code = @" +using System; +Func f = () => $""{1,2:f}Literal""; +Console.WriteLine(f()); +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL(@"$.<>c.<
$>b__0_0()", @" +{ + // Code size 45 (0x2d) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldc.i4.1 + IL_000a: box ""int"" + IL_000f: ldc.i4.2 + IL_0010: ldstr ""f"" + IL_0015: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: brfalse.s IL_0029 + IL_001c: ldloc.0 + IL_001d: ldstr ""Literal"" + IL_0022: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.0 + IL_002c: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_02() + { + var code = @" +using System; +CultureInfoNormalizer.Normalize(); +C.M(() => $""{1,2:f}Literal""); + +class C +{ + public static void M(Func f) => Console.WriteLine(f()); + public static void M(Func f) => throw null; +} +"; + + // Interpolated string handler conversions are not considered when determining the natural type of an expression: the natural return type of this lambda is string, + // so we don't even consider that there is a conversion from interpolated string expression to CustomHandler here (Sections 12.6.3.13 and 12.6.3.15 of the spec). + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: true) }); + var verifier = CompileAndVerify(comp, expectedOutput: @"1.00Literal"); + + // No DefaultInterpolatedStringHandler was included in the compilation, so it falls back to string.Format + verifier.VerifyIL(@"$.<>c.<
$>b__0_0()", @" +{ + // Code size 17 (0x11) + .maxstack 2 + IL_0000: ldstr ""{0,2:f}Literal"" + IL_0005: ldc.i4.1 + IL_0006: box ""int"" + IL_000b: call ""string string.Format(string, object)"" + IL_0010: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_03() + { + // Same as 2, but using a type that isn't allowed in an interpolated string. There is an implicit conversion error on the ref struct + // when converting to a string, because S cannot be a component of an interpolated string. This conversion error causes the lambda to + // fail to bind as Func, even though the natural return type is string, and the only successful bind is Func. + + var code = @" +using System; +C.M(() => $""{new S { Field = ""Field"" }}""); + +static class C +{ + public static void M(Func f) => throw null; + public static void M(Func f) => Console.WriteLine(f()); +} + +public partial class CustomHandler +{ + public void AppendFormatted(S value) => _builder.AppendLine(""value:"" + value.Field); +} +public ref struct S +{ + public string Field { get; set; } +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"value:Field"); + + verifier.VerifyIL(@"$.<>c.<
$>b__0_0()", @" +{ + // Code size 35 (0x23) + .maxstack 4 + .locals init (S V_0) + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: dup + IL_0008: ldloca.s V_0 + IL_000a: initobj ""S"" + IL_0010: ldloca.s V_0 + IL_0012: ldstr ""Field"" + IL_0017: call ""void S.Field.set"" + IL_001c: ldloc.0 + IL_001d: callvirt ""void CustomHandler.AppendFormatted(S)"" + IL_0022: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_04() + { + // Same as 3, but with S added to DefaultInterpolatedStringHandler (which then allows the lambda to be bound as Func, matching the natural return type) + + var code = @" +using System; +C.M(() => $""{new S { Field = ""Field"" }}""); + +static class C +{ + public static void M(Func f) => Console.WriteLine(f()); + public static void M(Func f) => throw null; +} + +public partial class CustomHandler +{ + public void AppendFormatted(S value) => throw null; +} +public ref struct S +{ + public string Field { get; set; } +} +namespace System.Runtime.CompilerServices +{ + public ref partial struct DefaultInterpolatedStringHandler + { + public void AppendFormatted(S value) => _builder.AppendLine(""value:"" + value.Field); + } +} +"; + + string[] source = new[] { + code, + GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: false), + GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: true, useBoolReturns: false) + }; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (3,11): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. + // C.M(() => $"{new S { Field = "Field" }}"); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, @"$""{new S { Field = ""Field"" }}""").WithArguments("interpolated string handlers", "10.0").WithLocation(3, 11), + // (3,14): error CS8773: Feature 'interpolated string handlers' is not available in C# 9.0. Please use language version 10.0 or greater. + // C.M(() => $"{new S { Field = "Field" }}"); + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, @"new S { Field = ""Field"" }").WithArguments("interpolated string handlers", "10.0").WithLocation(3, 14) + ); + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"value:Field"); + + verifier.VerifyIL(@"$.<>c.<
$>b__0_0()", @" +{ + // Code size 45 (0x2d) + .maxstack 3 + .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_0, + S V_1) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.1 + IL_0004: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldloca.s V_1 + IL_000d: initobj ""S"" + IL_0013: ldloca.s V_1 + IL_0015: ldstr ""Field"" + IL_001a: call ""void S.Field.set"" + IL_001f: ldloc.1 + IL_0020: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(S)"" + IL_0025: ldloca.s V_0 + IL_0027: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_002c: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_05() + { + var code = @" +using System; +C.M(b => + { + if (b) return default(CustomHandler); + else return $""{1,2:f}Literal""; + }); + +static class C +{ + public static void M(Func f) => throw null; + public static void M(Func f) => Console.WriteLine(f(false)); +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL(@"$.<>c.<
$>b__0_0(bool)", @" +{ + // Code size 55 (0x37) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldarg.1 + IL_0001: brfalse.s IL_000d + IL_0003: ldloca.s V_0 + IL_0005: initobj ""CustomHandler"" + IL_000b: ldloc.0 + IL_000c: ret + IL_000d: ldloca.s V_0 + IL_000f: ldc.i4.7 + IL_0010: ldc.i4.1 + IL_0011: call ""CustomHandler..ctor(int, int)"" + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.1 + IL_0019: box ""int"" + IL_001e: ldc.i4.2 + IL_001f: ldstr ""f"" + IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0029: ldloca.s V_0 + IL_002b: ldstr ""Literal"" + IL_0030: call ""void CustomHandler.AppendLiteral(string)"" + IL_0035: ldloc.0 + IL_0036: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_06() + { + // Same as 5, but with an implicit conversion from the builder type to string. This implicit conversion + // means that a best common type can be inferred for all branches of the lambda expression (Section 12.6.3.15 of the spec) + // and because there is a best common type, the inferred return type of the lambda is string. Since the inferred return type + // has an identity conversion to the return type of Func, that is preferred. + var code = @" +using System; +CultureInfoNormalizer.Normalize(); +C.M(b => + { + if (b) return default(CustomHandler); + else return $""{1,2:f}Literal""; + }); + +static class C +{ + public static void M(Func f) => Console.WriteLine(f(false)); + public static void M(Func f) => throw null; +} +public partial struct CustomHandler +{ + public static implicit operator string(CustomHandler c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); + + verifier.VerifyIL(@"$.<>c.<
$>b__0_0(bool)", @" +{ + // Code size 35 (0x23) + .maxstack 2 + .locals init (CustomHandler V_0) + IL_0000: ldarg.1 + IL_0001: brfalse.s IL_0012 + IL_0003: ldloca.s V_0 + IL_0005: initobj ""CustomHandler"" + IL_000b: ldloc.0 + IL_000c: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_0011: ret + IL_0012: ldstr ""{0,2:f}Literal"" + IL_0017: ldc.i4.1 + IL_0018: box ""int"" + IL_001d: call ""string string.Format(string, object)"" + IL_0022: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_07() + { + // Same as 5, but with an implicit conversion from string to the builder type. + var code = @" +using System; +C.M(b => + { + if (b) return default(CustomHandler); + else return $""{1,2:f}Literal""; + }); + +static class C +{ + public static void M(Func f) => Console.WriteLine(f(false)); + public static void M(Func f) => Console.WriteLine(f(false)); +} +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string s) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL(@"$.<>c.<
$>b__0_0(bool)", @" +{ + // Code size 55 (0x37) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldarg.1 + IL_0001: brfalse.s IL_000d + IL_0003: ldloca.s V_0 + IL_0005: initobj ""CustomHandler"" + IL_000b: ldloc.0 + IL_000c: ret + IL_000d: ldloca.s V_0 + IL_000f: ldc.i4.7 + IL_0010: ldc.i4.1 + IL_0011: call ""CustomHandler..ctor(int, int)"" + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.1 + IL_0019: box ""int"" + IL_001e: ldc.i4.2 + IL_001f: ldstr ""f"" + IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0029: ldloca.s V_0 + IL_002b: ldstr ""Literal"" + IL_0030: call ""void CustomHandler.AppendLiteral(string)"" + IL_0035: ldloc.0 + IL_0036: ret +} +"); + } + + [Fact] + public void LambdaReturnInference_08() + { + // Same as 5, but with an implicit conversion from the builder type to string and from string to the builder type. + var code = @" +using System; +C.M(b => + { + if (b) return default(CustomHandler); + else return $""{1,2:f}Literal""; + }); + +static class C +{ + public static void M(Func f) => Console.WriteLine(f(false)); + public static void M(Func f) => throw null; +} +public partial struct CustomHandler +{ + public static implicit operator string(CustomHandler c) => throw null; + public static implicit operator CustomHandler(string c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(Func)' and 'C.M(Func)' + // C.M(b => + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.Func)", "C.M(System.Func)").WithLocation(3, 3) + ); + } + + [Fact] + public void LambdaInference_AmbiguousInOlderLangVersions() + { + var code = @" +using System; +C.M(param => + { + param = $""{1}""; + }); + +static class C +{ + public static void M(Action f) => throw null; + public static void M(Action f) => throw null; +} +"; + + var source = new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + + // This successful emit is being caused by https://github.com/dotnet/roslyn/issues/53761, along with the duplicate diagnostics in LambdaReturnInference_04 + // We should not be changing binding behavior based on LangVersion. + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); + comp.VerifyDiagnostics( + // (3,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(Action)' and 'C.M(Action)' + // C.M(param => + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.Action)", "C.M(System.Action)").WithLocation(3, 3) + ); + } + + [Fact] + public void TernaryTypes_01() + { + var code = @" +using System; + +var x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +Console.WriteLine(x); +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 76 (0x4c) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.0 + IL_0001: box ""bool"" + IL_0006: unbox.any ""bool"" + IL_000b: brtrue.s IL_0038 + IL_000d: ldloca.s V_0 + IL_000f: ldc.i4.7 + IL_0010: ldc.i4.1 + IL_0011: call ""CustomHandler..ctor(int, int)"" + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.1 + IL_0019: box ""int"" + IL_001e: ldc.i4.2 + IL_001f: ldstr ""f"" + IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0029: ldloca.s V_0 + IL_002b: ldstr ""Literal"" + IL_0030: call ""void CustomHandler.AppendLiteral(string)"" + IL_0035: ldloc.0 + IL_0036: br.s IL_0041 + IL_0038: ldloca.s V_0 + IL_003a: initobj ""CustomHandler"" + IL_0040: ldloc.0 + IL_0041: box ""CustomHandler"" + IL_0046: call ""void System.Console.WriteLine(object)"" + IL_004b: ret +} +"); + } + + [Fact] + public void TernaryTypes_02() + { + // Same as 01, but with a conversion from CustomHandler to string. The rules here are similar to LambdaReturnInference_06 + var code = @" +using System; + +CultureInfoNormalizer.Normalize(); +var x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator string(CustomHandler c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 56 (0x38) + .maxstack 2 + .locals init (CustomHandler V_0) + IL_0000: call ""void CultureInfoNormalizer.Normalize()"" + IL_0005: ldc.i4.0 + IL_0006: box ""bool"" + IL_000b: unbox.any ""bool"" + IL_0010: brtrue.s IL_0024 + IL_0012: ldstr ""{0,2:f}Literal"" + IL_0017: ldc.i4.1 + IL_0018: box ""int"" + IL_001d: call ""string string.Format(string, object)"" + IL_0022: br.s IL_0032 + IL_0024: ldloca.s V_0 + IL_0026: initobj ""CustomHandler"" + IL_002c: ldloc.0 + IL_002d: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_0032: call ""void System.Console.WriteLine(string)"" + IL_0037: ret +} +"); + } + + [Fact] + public void TernaryTypes_03() + { + // Same as 02, but with a target-type + var code = @" +using System; + +CustomHandler x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator string(CustomHandler c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (4,19): error CS0029: Cannot implicitly convert type 'string' to 'CustomHandler' + // CustomHandler x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"(bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""").WithArguments("string", "CustomHandler").WithLocation(4, 19) + ); + } + + [Fact] + public void TernaryTypes_04() + { + // Same 01, but with a conversion from string to CustomHandler. The rules here are similar to LambdaReturnInference_07 + var code = @" +using System; + +var x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 76 (0x4c) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.0 + IL_0001: box ""bool"" + IL_0006: unbox.any ""bool"" + IL_000b: brtrue.s IL_0038 + IL_000d: ldloca.s V_0 + IL_000f: ldc.i4.7 + IL_0010: ldc.i4.1 + IL_0011: call ""CustomHandler..ctor(int, int)"" + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.1 + IL_0019: box ""int"" + IL_001e: ldc.i4.2 + IL_001f: ldstr ""f"" + IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0029: ldloca.s V_0 + IL_002b: ldstr ""Literal"" + IL_0030: call ""void CustomHandler.AppendLiteral(string)"" + IL_0035: ldloc.0 + IL_0036: br.s IL_0041 + IL_0038: ldloca.s V_0 + IL_003a: initobj ""CustomHandler"" + IL_0040: ldloc.0 + IL_0041: box ""CustomHandler"" + IL_0046: call ""void System.Console.WriteLine(object)"" + IL_004b: ret +} +"); + } + + [Fact] + public void TernaryTypes_05() + { + // Same 01, but with a conversion from string to CustomHandler and CustomHandler to string. + var code = @" +using System; + +var x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string c) => throw null; + public static implicit operator string(CustomHandler c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (4,9): error CS0172: Type of conditional expression cannot be determined because 'CustomHandler' and 'string' implicitly convert to one another + // var x = (bool)(object)false ? default(CustomHandler) : $"{1,2:f}Literal"; + Diagnostic(ErrorCode.ERR_AmbigQM, @"(bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""").WithArguments("CustomHandler", "string").WithLocation(4, 9) + ); + } + + [Fact] + public void TernaryTypes_06() + { + // Same 05, but with a target type + var code = @" +using System; + +CustomHandler x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string c) => throw null; + public static implicit operator string(CustomHandler c) => c.ToString(); +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 76 (0x4c) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.0 + IL_0001: box ""bool"" + IL_0006: unbox.any ""bool"" + IL_000b: brtrue.s IL_0038 + IL_000d: ldloca.s V_0 + IL_000f: ldc.i4.7 + IL_0010: ldc.i4.1 + IL_0011: call ""CustomHandler..ctor(int, int)"" + IL_0016: ldloca.s V_0 + IL_0018: ldc.i4.1 + IL_0019: box ""int"" + IL_001e: ldc.i4.2 + IL_001f: ldstr ""f"" + IL_0024: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0029: ldloca.s V_0 + IL_002b: ldstr ""Literal"" + IL_0030: call ""void CustomHandler.AppendLiteral(string)"" + IL_0035: ldloc.0 + IL_0036: br.s IL_0041 + IL_0038: ldloca.s V_0 + IL_003a: initobj ""CustomHandler"" + IL_0040: ldloc.0 + IL_0041: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_0046: call ""void System.Console.WriteLine(string)"" + IL_004b: ret +} +"); + } + + [Fact] + public void SwitchTypes_01() + { + // Switch expressions infer a best type based on _types_, not based on expressions (section 12.6.3.15 of the spec). Because this is based on types + // and not on expression conversions, no best type can be found for this switch expression. + + var code = @" +using System; + +var x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +Console.WriteLine(x); +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (4,29): error CS8506: No best type was found for the switch expression. + // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; + Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(4, 29) + ); + } + + [Fact] + public void SwitchTypes_02() + { + // Same as 01, but with a conversion from CustomHandler. This allows the switch expression to infer a best-common type, which is string. + var code = @" +using System; + +CultureInfoNormalizer.Normalize(); +var x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator string(CustomHandler c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 59 (0x3b) + .maxstack 2 + .locals init (string V_0, + CustomHandler V_1) + IL_0000: call ""void CultureInfoNormalizer.Normalize()"" + IL_0005: ldc.i4.0 + IL_0006: box ""bool"" + IL_000b: unbox.any ""bool"" + IL_0010: brfalse.s IL_0023 + IL_0012: ldloca.s V_1 + IL_0014: initobj ""CustomHandler"" + IL_001a: ldloc.1 + IL_001b: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_0020: stloc.0 + IL_0021: br.s IL_0034 + IL_0023: ldstr ""{0,2:f}Literal"" + IL_0028: ldc.i4.1 + IL_0029: box ""int"" + IL_002e: call ""string string.Format(string, object)"" + IL_0033: stloc.0 + IL_0034: ldloc.0 + IL_0035: call ""void System.Console.WriteLine(string)"" + IL_003a: ret +} +"); + } + + [Fact] + public void SwitchTypes_03() + { + // Same 02, but with a target-type. The natural type will fail to compile, so the switch will use a target type (unlike TernaryTypes_03, which fails to compile). + var code = @" +using System; + +CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator string(CustomHandler c) => c.ToString(); +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (CustomHandler V_0, + CustomHandler V_1) + IL_0000: ldc.i4.0 + IL_0001: box ""bool"" + IL_0006: unbox.any ""bool"" + IL_000b: brfalse.s IL_0019 + IL_000d: ldloca.s V_1 + IL_000f: initobj ""CustomHandler"" + IL_0015: ldloc.1 + IL_0016: stloc.0 + IL_0017: br.s IL_0043 + IL_0019: ldloca.s V_1 + IL_001b: ldc.i4.7 + IL_001c: ldc.i4.1 + IL_001d: call ""CustomHandler..ctor(int, int)"" + IL_0022: ldloca.s V_1 + IL_0024: ldc.i4.1 + IL_0025: box ""int"" + IL_002a: ldc.i4.2 + IL_002b: ldstr ""f"" + IL_0030: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0035: ldloca.s V_1 + IL_0037: ldstr ""Literal"" + IL_003c: call ""void CustomHandler.AppendLiteral(string)"" + IL_0041: ldloc.1 + IL_0042: stloc.0 + IL_0043: ldloc.0 + IL_0044: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_0049: call ""void System.Console.WriteLine(string)"" + IL_004e: ret +} +"); + } + + [Fact] + public void SwitchTypes_04() + { + // Same as 01, but with a conversion to CustomHandler. This allows the switch expression to infer a best-common type, which is CustomHandler. + var code = @" +using System; + +var x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (CustomHandler V_0, + CustomHandler V_1) + IL_0000: ldc.i4.0 + IL_0001: box ""bool"" + IL_0006: unbox.any ""bool"" + IL_000b: brfalse.s IL_0019 + IL_000d: ldloca.s V_1 + IL_000f: initobj ""CustomHandler"" + IL_0015: ldloc.1 + IL_0016: stloc.0 + IL_0017: br.s IL_0043 + IL_0019: ldloca.s V_1 + IL_001b: ldc.i4.7 + IL_001c: ldc.i4.1 + IL_001d: call ""CustomHandler..ctor(int, int)"" + IL_0022: ldloca.s V_1 + IL_0024: ldc.i4.1 + IL_0025: box ""int"" + IL_002a: ldc.i4.2 + IL_002b: ldstr ""f"" + IL_0030: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0035: ldloca.s V_1 + IL_0037: ldstr ""Literal"" + IL_003c: call ""void CustomHandler.AppendLiteral(string)"" + IL_0041: ldloc.1 + IL_0042: stloc.0 + IL_0043: ldloc.0 + IL_0044: box ""CustomHandler"" + IL_0049: call ""void System.Console.WriteLine(object)"" + IL_004e: ret +} +"); + } + + [Fact] + public void SwitchTypes_05() + { + // Same as 01, but with conversions in both directions. No best common type can be found. + var code = @" +using System; + +var x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string c) => throw null; + public static implicit operator string(CustomHandler c) => throw null; +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (4,29): error CS8506: No best type was found for the switch expression. + // var x = (bool)(object)false switch { true => default(CustomHandler), false => $"{1,2:f}Literal" }; + Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(4, 29) + ); + } + + [Fact] + public void SwitchTypes_06() + { + // Same as 05, but with a target type. + var code = @" +using System; + +CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +Console.WriteLine(x); + +public partial struct CustomHandler +{ + public static implicit operator CustomHandler(string c) => throw null; + public static implicit operator string(CustomHandler c) => c.ToString(); +} +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 79 (0x4f) + .maxstack 4 + .locals init (CustomHandler V_0, + CustomHandler V_1) + IL_0000: ldc.i4.0 + IL_0001: box ""bool"" + IL_0006: unbox.any ""bool"" + IL_000b: brfalse.s IL_0019 + IL_000d: ldloca.s V_1 + IL_000f: initobj ""CustomHandler"" + IL_0015: ldloc.1 + IL_0016: stloc.0 + IL_0017: br.s IL_0043 + IL_0019: ldloca.s V_1 + IL_001b: ldc.i4.7 + IL_001c: ldc.i4.1 + IL_001d: call ""CustomHandler..ctor(int, int)"" + IL_0022: ldloca.s V_1 + IL_0024: ldc.i4.1 + IL_0025: box ""int"" + IL_002a: ldc.i4.2 + IL_002b: ldstr ""f"" + IL_0030: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0035: ldloca.s V_1 + IL_0037: ldstr ""Literal"" + IL_003c: call ""void CustomHandler.AppendLiteral(string)"" + IL_0041: ldloc.1 + IL_0042: stloc.0 + IL_0043: ldloc.0 + IL_0044: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_0049: call ""void System.Console.WriteLine(string)"" + IL_004e: ret +} +"); + } + + [Fact] + public void PassAsRefWithoutKeyword_01() + { + var code = @" +M($""{1,2:f}Literal""); + +void M(ref CustomHandler c) => System.Console.WriteLine(c);"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 48 (0x30) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: ldc.i4.2 + IL_0012: ldstr ""f"" + IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_001c: ldloca.s V_0 + IL_001e: ldstr ""Literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloca.s V_0 + IL_002a: call ""void $.<
$>g__M|0_0(ref CustomHandler)"" + IL_002f: ret +} +"); + } + + [Fact] + public void PassAsRefWithoutKeyword_02() + { + var code = @" +M($""{1,2:f}Literal""); +M(ref $""{1,2:f}Literal""); + +void M(ref CustomHandler c) => System.Console.WriteLine(c);"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (2,3): error CS1620: Argument 1 must be passed with the 'ref' keyword + // M($"{1,2:f}Literal"); + Diagnostic(ErrorCode.ERR_BadArgRef, @"$""{1,2:f}Literal""").WithArguments("1", "ref").WithLocation(2, 3), + // (3,7): error CS1510: A ref or out value must be an assignable variable + // M(ref $"{1,2:f}Literal"); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, @"$""{1,2:f}Literal""").WithLocation(3, 7) + ); + } + + [Fact] + public void PassAsRefWithoutKeyword_03() + { + var code = @" +M($""{1,2:f}Literal""); + +void M(in CustomHandler c) => System.Console.WriteLine(c);"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 45 (0x2d) + .maxstack 5 + .locals init (CustomHandler V_0) + IL_0000: ldc.i4.7 + IL_0001: ldc.i4.1 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: dup + IL_0008: ldc.i4.1 + IL_0009: box ""int"" + IL_000e: ldc.i4.2 + IL_000f: ldstr ""f"" + IL_0014: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0019: dup + IL_001a: ldstr ""Literal"" + IL_001f: callvirt ""void CustomHandler.AppendLiteral(string)"" + IL_0024: stloc.0 + IL_0025: ldloca.s V_0 + IL_0027: call ""void $.<
$>g__M|0_0(in CustomHandler)"" + IL_002c: ret +} +"); + } + + [Fact] + public void PassAsRefWithoutKeyword_04() + { + var code = @" +M($""{1,2:f}Literal""); + +void M(in CustomHandler c) => System.Console.WriteLine(c);"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 48 (0x30) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: ldc.i4.2 + IL_0012: ldstr ""f"" + IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_001c: ldloca.s V_0 + IL_001e: ldstr ""Literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloca.s V_0 + IL_002a: call ""void $.<
$>g__M|0_0(in CustomHandler)"" + IL_002f: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void RefOverloadResolution_Struct([CombinatorialValues("in", "ref")] string refKind) + { + var code = @" +C.M($""{1,2:f}Literal""); + +class C +{ + public static void M(CustomHandler c) => System.Console.WriteLine(c); + public static void M(" + refKind + @" CustomHandler c) => System.Console.WriteLine(c); +}"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 47 (0x2f) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: ldc.i4.2 + IL_0012: ldstr ""f"" + IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_001c: ldloca.s V_0 + IL_001e: ldstr ""Literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloc.0 + IL_0029: call ""void C.M(CustomHandler)"" + IL_002e: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void RefOverloadResolution_Class([CombinatorialValues("in", "ref")] string refKind) + { + var code = @" +C.M($""{1,2:f}Literal""); + +class C +{ + public static void M(CustomHandler c) => System.Console.WriteLine(c); + public static void M(" + refKind + @" CustomHandler c) => System.Console.WriteLine(c); +}"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); + VerifyInterpolatedStringExpression(comp); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 47 (0x2f) + .maxstack 4 + .locals init (CustomHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: ldc.i4.2 + IL_0012: ldstr ""f"" + IL_0017: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_001c: ldloca.s V_0 + IL_001e: ldstr ""Literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloc.0 + IL_0029: call ""void C.M(CustomHandler)"" + IL_002e: ret +} +"); + } + + [Fact] + public void RefOverloadResolution_MultipleBuilderTypes() + { + var code = @" +C.M($""{1,2:f}Literal""); + +class C +{ + public static void M(CustomHandler1 c) => System.Console.WriteLine(c); + public static void M(ref CustomHandler2 c) => throw null; +}"; + + var comp = CreateCompilation(new[] + { + code, + GetInterpolatedStringCustomHandlerType("CustomHandler1", "struct", useBoolReturns: false), + GetInterpolatedStringCustomHandlerType("CustomHandler2", "struct", useBoolReturns: false, includeOneTimeHelpers: false) + }); + VerifyInterpolatedStringExpression(comp, "CustomHandler1"); + var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @" +value:1 +alignment:2 +format:f +literal:Literal"); + + verifier.VerifyIL("", @" +{ + // Code size 47 (0x2f) + .maxstack 4 + .locals init (CustomHandler1 V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler1..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: ldc.i4.2 + IL_0012: ldstr ""f"" + IL_0017: call ""void CustomHandler1.AppendFormatted(object, int, string)"" + IL_001c: ldloca.s V_0 + IL_001e: ldstr ""Literal"" + IL_0023: call ""void CustomHandler1.AppendLiteral(string)"" + IL_0028: ldloc.0 + IL_0029: call ""void C.M(CustomHandler1)"" + IL_002e: ret +} +"); + } + + private const string InterpolatedStringHandlerAttributesVB = @" +Namespace System.Runtime.CompilerServices + + Public NotInheritable Class InterpolatedStringHandlerAttribute + Inherits Attribute + End Class + + Public NotInheritable Class InterpolatedStringHandlerArgumentAttribute + Inherits Attribute + + Public Sub New(argument As String) + Arguments = { argument } + End Sub + + Public Sub New(ParamArray arguments() as String) + Me.Arguments = arguments + End Sub + + Public ReadOnly Property Arguments As String() + End Class +End Namespace +"; + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_NonHandlerType() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute] string s) {} +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute }); + comp.VerifyDiagnostics( + // (8,27): error CS8946: 'string' is not an interpolated string handler type. + // public static void M([InterpolatedStringHandlerArgumentAttribute] string s) {} + Diagnostic(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, "InterpolatedStringHandlerArgumentAttribute").WithArguments("string").WithLocation(8, 27) + ); + + var sParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + sParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(sParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(sParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_NonHandlerType_Metadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M(i as Integer, c As String) + End Sub +End Class +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + // Note: there is no compilation error here because the natural type of a string is still string, and + // we just bind to that method without checking the handler attribute. + var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics(); + + var sParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + sParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(sParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(sParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_InvalidArgument() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute(1)] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (8,70): error CS1503: Argument 1: cannot convert from 'int' to 'string' + // public static void M([InterpolatedStringHandlerArgumentAttribute(1)] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("1", "int", "string").WithLocation(8, 70) + ); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_01() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute(""NonExistant"")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 5), + // (8,27): error CS8945: 'NonExistant' is not a valid parameter name from 'C.M(CustomHandler)'. + // public static void M([InterpolatedStringHandlerArgumentAttribute("NonExistant")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""NonExistant"")").WithArguments("NonExistant", "C.M(CustomHandler)").WithLocation(8, 27) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_01_FromMetadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M( c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 5), + // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 5), + // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 5) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_02() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M(1, $""""); + +class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"", ""NonExistant"")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 8), + // (8,34): error CS8945: 'NonExistant' is not a valid parameter name from 'C.M(int, CustomHandler)'. + // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("i", "NonExistant")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""i"", ""NonExistant"")").WithArguments("NonExistant", "C.M(int, CustomHandler)").WithLocation(8, 34) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_02_FromMetadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M(i As Integer, c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_03() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M(1, $""""); + +class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""NonExistant1"", ""NonExistant2"")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 8), + // (8,34): error CS8945: 'NonExistant1' is not a valid parameter name from 'C.M(int, CustomHandler)'. + // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("NonExistant1", "NonExistant2")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""NonExistant1"", ""NonExistant2"")").WithArguments("NonExistant1", "C.M(int, CustomHandler)").WithLocation(8, 34), + // (8,34): error CS8945: 'NonExistant2' is not a valid parameter name from 'C.M(int, CustomHandler)'. + // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("NonExistant1", "NonExistant2")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_InvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute(""NonExistant1"", ""NonExistant2"")").WithArguments("NonExistant2", "C.M(int, CustomHandler)").WithLocation(8, 34) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_03_FromMetadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M(i As Integer, c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_ReferenceSelf() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M(1, $""""); + +class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""c"")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 8), + // (8,34): error CS8948: InterpolatedStringHandlerArgumentAttribute arguments cannot refer to the parameter the attribute is used on. + // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute("c")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_CannotUseSelfAsInterpolatedStringHandlerArgument, @"InterpolatedStringHandlerArgumentAttribute(""c"")").WithLocation(8, 34) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_ReferencesSelf_FromMetadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M( c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 5), + // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 5), + // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 5) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_NullConstant() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M(1, $""""); + +class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(new string[] { null })] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 8), + // (8,34): error CS8943: null is not a valid parameter name. To get access to the receiver of an instance method, use the empty string as the parameter name. + // public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(new string[] { null })] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_NullInvalidInterpolatedStringHandlerArgumentName, "InterpolatedStringHandlerArgumentAttribute(new string[] { null })").WithLocation(8, 34) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_01() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M(i As Integer, c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_02() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M(i As Integer, c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_NullConstant_FromMetadata_03() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M(i As Integer, c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M(1, $"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,8): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_ThisOnStaticMethod() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute("""")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 5), + // (8,27): error CS8944: 'C.M(CustomHandler)' is not an instance method, the receiver cannot be an interpolated string handler argument. + // public static void M([InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute("""")").WithArguments("C.M(CustomHandler)").WithLocation(8, 27) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Theory] + [InlineData(@"{""""}")] + [InlineData(@"""""")] + public void InterpolatedStringHandlerArgumentAttributeError_ThisOnStaticMethod_FromMetadata(string arg) + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M( c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,5): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 5), + // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 5), + // (1,5): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 5) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_ThisOnConstructor() + { + var code = @" +using System.Runtime.CompilerServices; + +_ = new C($""""); + +class C +{ + public C([InterpolatedStringHandlerArgumentAttribute("""")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (4,11): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // _ = new C($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 11), + // (8,15): error CS8944: 'C.C(CustomHandler)' is not an instance method, the receiver cannot be an interpolated string handler argument. + // public C([InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c) {} + Diagnostic(ErrorCode.ERR_NotInstanceInvalidInterpolatedStringHandlerArgumentName, @"InterpolatedStringHandlerArgumentAttribute("""")").WithArguments("C.C(CustomHandler)").WithLocation(8, 15) + ); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod(".ctor").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Theory] + [InlineData(@"{""""}")] + [InlineData(@"""""")] + public void InterpolatedStringHandlerArgumentAttributeError_ThisOnConstructor_FromMetadata(string arg) + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Sub New( c As CustomHandler) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"_ = new C($"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,11): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // _ = new C($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 11), + // (1,11): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // _ = new C($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 11), + // (1,11): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // _ = new C($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 11) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod(".ctor").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerAttributeArgumentError_SubstitutedTypeSymbol() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +public class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute] T t) { } +} +"; + + var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, customHandler }); + comp.VerifyDiagnostics( + // (4,20): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(4, 20), + // (8,27): error CS8946: 'T' is not an interpolated string handler type. + // public static void M([InterpolatedStringHandlerArgumentAttribute] T t) { } + Diagnostic(ErrorCode.ERR_TypeIsNotAnInterpolatedStringHandlerType, "InterpolatedStringHandlerArgumentAttribute").WithArguments("T").WithLocation(8, 27) + ); + + var c = comp.SourceModule.GlobalNamespace.GetTypeMember("C"); + var handler = comp.SourceModule.GlobalNamespace.GetTypeMember("CustomHandler"); + + var substitutedC = c.WithTypeArguments(ImmutableArray.Create(TypeWithAnnotations.Create(handler))); + + var cParam = substitutedC.GetMethod("M").Parameters.Single(); + Assert.IsType(cParam); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_SubstitutedTypeSymbol_FromMetadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C(Of T) + Public Shared Sub M( c As T) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation(@"C.M($"""");", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics( + // (1,20): error CS8949: The InterpolatedStringHandlerArgumentAttribute applied to parameter 'CustomHandler' is malformed and cannot be interpreted. Construct an instance of 'CustomHandler' manually. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, @"$""""").WithArguments("CustomHandler", "CustomHandler").WithLocation(1, 20), + // (1,20): error CS1729: 'CustomHandler' does not contain a constructor that takes 2 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "2").WithLocation(1, 20), + // (1,20): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M($""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 20) + ); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C`1").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttributeWarn_ParameterAfterHandler(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; +public class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c, int i) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i:"" + i.ToString()); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var goodCode = @" +int i = 10; +C.M(i: i, c: $""text""); +"; + + var comp = CreateCompilation(new[] { code, goodCode, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validate, symbolValidator: validate, expectedOutput: @" +i:10 +literal:text +"); + verifier.VerifyDiagnostics( + // (6,27): warning CS8947: Parameter i occurs after CustomHandler in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller + // to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, int i) => Console.WriteLine(c.ToString()); + Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "CustomHandler").WithLocation(6, 27) + + ); + + verifyIL(verifier); + + var badCode = @"C.M($"""", 1);"; + + comp = CreateCompilation(new[] { code, badCode, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (1,10): error CS8950: Parameter 'i' is an argument to the interpolated string handler conversion on parameter 'c', but is specified after the interpolated string constant. Reorder the arguments to move 'i' before 'c'. + // C.M($"", 1); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, "1").WithArguments("i", "c").WithLocation(1, 10), + // (6,27): warning CS8947: Parameter i occurs after CustomHandler in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller + // to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, int i) => Console.WriteLine(c.ToString()); + Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "CustomHandler").WithLocation(6, 27) + ); + + static void validate(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.First(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(1, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); + } + + void verifyIL(CompilationVerifier verifier) + { + verifier.VerifyIL("", extraConstructorArg == "" + ? @" +{ + // Code size 36 (0x24) + .maxstack 4 + .locals init (int V_0, + int V_1, + CustomHandler V_2) + IL_0000: ldc.i4.s 10 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: stloc.1 + IL_0005: ldloca.s V_2 + IL_0007: ldc.i4.4 + IL_0008: ldc.i4.0 + IL_0009: ldloc.0 + IL_000a: call ""CustomHandler..ctor(int, int, int)"" + IL_000f: ldloca.s V_2 + IL_0011: ldstr ""text"" + IL_0016: call ""bool CustomHandler.AppendLiteral(string)"" + IL_001b: pop + IL_001c: ldloc.2 + IL_001d: ldloc.1 + IL_001e: call ""void C.M(CustomHandler, int)"" + IL_0023: ret +} +" + : @" +{ + // Code size 43 (0x2b) + .maxstack 4 + .locals init (int V_0, + int V_1, + CustomHandler V_2, + bool V_3) + IL_0000: ldc.i4.s 10 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: stloc.1 + IL_0005: ldc.i4.4 + IL_0006: ldc.i4.0 + IL_0007: ldloc.0 + IL_0008: ldloca.s V_3 + IL_000a: newobj ""CustomHandler..ctor(int, int, int, out bool)"" + IL_000f: stloc.2 + IL_0010: ldloc.3 + IL_0011: brfalse.s IL_0021 + IL_0013: ldloca.s V_2 + IL_0015: ldstr ""text"" + IL_001a: call ""bool CustomHandler.AppendLiteral(string)"" + IL_001f: br.s IL_0022 + IL_0021: ldc.i4.0 + IL_0022: pop + IL_0023: ldloc.2 + IL_0024: ldloc.1 + IL_0025: call ""void C.M(CustomHandler, int)"" + IL_002a: ret +} +"); + } + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeWarn_ParameterAfterHandler_FromMetadata() + { + var vbCode = @" +Imports System.Runtime.CompilerServices +Public Class C + Public Shared Sub M( c As CustomHandler, i As Integer) + End Sub +End Class + +Public Structure CustomHandler +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vbCode, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var comp = CreateCompilation("", references: new[] { vbComp.EmitToImageReference() }); + comp.VerifyEmitDiagnostics(); + + var customHandler = comp.GetTypeByMetadataName("CustomHandler"); + Assert.True(customHandler.IsInterpolatedStringHandlerType); + + var cParam = comp.GetTypeByMetadataName("C").GetMethod("M").Parameters.First(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(1, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_OptionalNotSpecifiedAtCallsite() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +public class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c, int i = 0) { } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i) : this(literalLength, formattedCount) + { + } +} +"; + + var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, customHandler }); + comp.VerifyDiagnostics( + // (4,5): error CS8951: Parameter 'i' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter 'c'. Specify the value of 'i' before 'c'. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, @"$""""").WithArguments("i", "c").WithLocation(4, 5), + // (8,27): warning CS8947: Parameter i occurs after CustomHandler in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, params int[] i) { } + Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "CustomHandler").WithLocation(8, 27) + ); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttributeError_ParamsNotSpecifiedAtCallsite() + { + var code = @" +using System.Runtime.CompilerServices; + +C.M($""""); + +public class C +{ + public static void M([InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c, params int[] i) { } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int[] i) : this(literalLength, formattedCount) + { + } +} +"; + + var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, customHandler }); + comp.VerifyDiagnostics( + // (4,5): error CS8951: Parameter 'i' is not explicitly provided, but is used as an argument to the interpolated string handler conversion on parameter 'c'. Specify the value of 'i' before 'c'. + // C.M($""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, @"$""""").WithArguments("i", "c").WithLocation(4, 5), + // (8,27): warning CS8947: Parameter i occurs after CustomHandler in the parameter list, but is used as an argument for interpolated string handler conversions. This will require the caller to reorder parameters with named arguments at the call site. Consider putting the interpolated string handler parameter after all arguments involved. + // public static void M([InterpolatedStringHandlerArgumentAttribute("i")] CustomHandler c, params int[] i) { } + Diagnostic(ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, @"InterpolatedStringHandlerArgumentAttribute(""i"")").WithArguments("i", "CustomHandler").WithLocation(8, 27) + ); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MissingConstructor() + { + var code = @" +using System.Runtime.CompilerServices; +public class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + CompileAndVerify(comp, sourceSymbolValidator: validate, symbolValidator: validate).VerifyDiagnostics(); + + CreateCompilation(@"C.M(1, $"""");", new[] { comp.ToMetadataReference() }).VerifyDiagnostics( + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 4 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "4").WithLocation(1, 8) + ); + + static void validate(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); + } + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_InaccessibleConstructor_01() + { + var code = @" +using System.Runtime.CompilerServices; +public class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) {} +} + +public partial struct CustomHandler +{ + private CustomHandler(int literalLength, int formattedCount, int i) : this() {} + + static void InCustomHandler() + { + C.M(1, $""""); + } +} +"; + + var executableCode = @"C.M(1, $"""");"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (1,8): error CS0122: 'CustomHandler.CustomHandler(int, int, int)' is inaccessible due to its protection level + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadAccess, @"$""""").WithArguments("CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 8) + ); + + var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + + // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. + CompileAndVerify(dependency, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + comp = CreateCompilation(executableCode, new[] { dependency.EmitToImageReference() }); + comp.VerifyDiagnostics( + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 4 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "4").WithLocation(1, 8), + // (1,8): error CS1729: 'CustomHandler' does not contain a constructor that takes 3 arguments + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"$""""").WithArguments("CustomHandler", "3").WithLocation(1, 8) + ); + + comp = CreateCompilation(executableCode, new[] { dependency.ToMetadataReference() }); + comp.VerifyDiagnostics( + // (1,8): error CS0122: 'CustomHandler.CustomHandler(int, int, int)' is inaccessible due to its protection level + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadAccess, @"$""""").WithArguments("CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 8) + ); + + static void validate(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + } + } + + private void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(string mRef, string customHandlerRef, params DiagnosticDescription[] expectedDiagnostics) + { + var code = @" +using System.Runtime.CompilerServices; + +int i = 0; +C.M(" + mRef + @" i, $""""); + +public class C +{ + public static void M(" + mRef + @" int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) { " + (mRef == "out" ? "i = 0;" : "") + @" } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, " + customHandlerRef + @" int i) : this() { " + (customHandlerRef == "out" ? "i = 0;" : "") + @" } +} +"; + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics(expectedDiagnostics); + + var cParam = comp.SourceModule.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefNone() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "", + // (5,9): error CS1615: Argument 3 may not be passed with the 'ref' keyword + // C.M(ref i, $""); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "ref").WithLocation(5, 9)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefOut() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "out", + // (5,9): error CS1620: Argument 3 must be passed with the 'out' keyword + // C.M(ref i, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "out").WithLocation(5, 9)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefIn() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "in", + // (5,9): error CS1615: Argument 3 may not be passed with the 'ref' keyword + // C.M(ref i, $""); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "ref").WithLocation(5, 9)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InNone() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "", + // (5,8): error CS1615: Argument 3 may not be passed with the 'in' keyword + // C.M(in i, $""); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "in").WithLocation(5, 8)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InOut() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "out", + // (5,8): error CS1620: Argument 3 must be passed with the 'out' keyword + // C.M(in i, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "out").WithLocation(5, 8)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InRef() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "ref", + // (5,8): error CS1620: Argument 3 must be passed with the 'ref' keyword + // C.M(in i, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(5, 8)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_OutNone() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("out", "", + // (5,9): error CS1615: Argument 3 may not be passed with the 'out' keyword + // C.M(out i, $""); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("3", "out").WithLocation(5, 9)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_OutRef() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("out", "ref", + // (5,9): error CS1620: Argument 3 must be passed with the 'ref' keyword + // C.M(out i, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(5, 9)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_NoneRef() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("", "ref", + // (5,6): error CS1620: Argument 3 must be passed with the 'ref' keyword + // C.M( i, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "ref").WithLocation(5, 6)); + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_NoneOut() + { + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("", "out", + // (5,6): error CS1620: Argument 3 must be passed with the 'out' keyword + // C.M( i, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, "i").WithArguments("3", "out").WithLocation(5, 6)); + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedType(string extraConstructorArg) + { + var code = @" +using System.Runtime.CompilerServices; +public class C +{ + public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) {} +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, string s" + extraConstructorArg + @") : this() + { +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var executableCode = @"C.M(1, $"""");"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var expectedDiagnostics = extraConstructorArg == "" + ? new DiagnosticDescription[] + { + // (1,5): error CS1503: Argument 3: cannot convert from 'int' to 'string' + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("3", "int", "string").WithLocation(1, 5) + } + : new DiagnosticDescription[] + { + // (1,5): error CS1503: Argument 3: cannot convert from 'int' to 'string' + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_BadArgType, "1").WithArguments("3", "int", "string").WithLocation(1, 5), + // (1,8): error CS7036: There is no argument given that corresponds to the required formal parameter 'success' of 'CustomHandler.CustomHandler(int, int, string, out bool)' + // C.M(1, $""); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("success", "CustomHandler.CustomHandler(int, int, string, out bool)").WithLocation(1, 8) + }; + + var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics(expectedDiagnostics); + + // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. + var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + CompileAndVerify(dependency, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + foreach (var d in new[] { dependency.EmitToImageReference(), dependency.ToMetadataReference() }) + { + comp = CreateCompilation(executableCode, new[] { d }); + comp.VerifyDiagnostics(expectedDiagnostics); + } + + static void validate(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_SingleArg(string extraConstructorArg) + { + var code = @" +using System.Runtime.CompilerServices; + +public class C +{ + public static string M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] CustomHandler c) => c.ToString(); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i:"" + i.ToString()); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var executableCode = @" +using System; + +int i = 10; +Console.WriteLine(C.M(i, $""2"")); +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +i:10 +literal:2"); + + verifier.VerifyDiagnostics(); + verifyIL(extraConstructorArg, verifier); + + var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + + foreach (var d in new[] { dependency.EmitToImageReference(), dependency.ToMetadataReference() }) + { + verifier = CompileAndVerify(executableCode, new[] { d }, expectedOutput: @" +i:10 +literal:2"); + verifier.VerifyDiagnostics(); + verifyIL(extraConstructorArg, verifier); + } + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); + } + + static void verifyIL(string extraConstructorArg, CompilationVerifier verifier) + { + verifier.VerifyIL("", extraConstructorArg == "" + ? @" +{ + // Code size 39 (0x27) + .maxstack 5 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: ldc.i4.s 10 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldloca.s V_1 + IL_0006: ldc.i4.1 + IL_0007: ldc.i4.0 + IL_0008: ldloc.0 + IL_0009: call ""CustomHandler..ctor(int, int, int)"" + IL_000e: ldloca.s V_1 + IL_0010: ldstr ""2"" + IL_0015: call ""bool CustomHandler.AppendLiteral(string)"" + IL_001a: pop + IL_001b: ldloc.1 + IL_001c: call ""string C.M(int, CustomHandler)"" + IL_0021: call ""void System.Console.WriteLine(string)"" + IL_0026: ret +} +" + : @" +{ + // Code size 46 (0x2e) + .maxstack 5 + .locals init (int V_0, + CustomHandler V_1, + bool V_2) + IL_0000: ldc.i4.s 10 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldc.i4.1 + IL_0005: ldc.i4.0 + IL_0006: ldloc.0 + IL_0007: ldloca.s V_2 + IL_0009: newobj ""CustomHandler..ctor(int, int, int, out bool)"" + IL_000e: stloc.1 + IL_000f: ldloc.2 + IL_0010: brfalse.s IL_0020 + IL_0012: ldloca.s V_1 + IL_0014: ldstr ""2"" + IL_0019: call ""bool CustomHandler.AppendLiteral(string)"" + IL_001e: br.s IL_0021 + IL_0020: ldc.i4.0 + IL_0021: pop + IL_0022: ldloc.1 + IL_0023: call ""string C.M(int, CustomHandler)"" + IL_0028: call ""void System.Console.WriteLine(string)"" + IL_002d: ret +} +"); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_MultipleArgs(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; +public class C +{ + public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"")] CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i:"" + i.ToString()); + _builder.AppendLine(""s:"" + s); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var executableCode = @" +int i = 10; +string s = ""arg""; +C.M(i, s, $""literal""); +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); + string expectedOutput = @" +i:10 +s:arg +literal:literal +"; + var verifier = base.CompileAndVerify((Compilation)comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: expectedOutput); + + verifier.VerifyDiagnostics(); + verifyIL(extraConstructorArg, verifier); + + var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + + foreach (var d in new[] { dependency.EmitToImageReference(), dependency.ToMetadataReference() }) + { + verifier = CompileAndVerify(executableCode, new[] { d }, expectedOutput: expectedOutput); + verifier.VerifyDiagnostics(); + verifyIL(extraConstructorArg, verifier); + } + + static void validator(ModuleSymbol verifier) + { + var cParam = verifier.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + + static void verifyIL(string extraConstructorArg, CompilationVerifier verifier) + { + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 44 (0x2c) + .maxstack 7 + .locals init (string V_0, //s + int V_1, + string V_2, + CustomHandler V_3) + IL_0000: ldc.i4.s 10 + IL_0002: ldstr ""arg"" + IL_0007: stloc.0 + IL_0008: stloc.1 + IL_0009: ldloc.1 + IL_000a: ldloc.0 + IL_000b: stloc.2 + IL_000c: ldloc.2 + IL_000d: ldloca.s V_3 + IL_000f: ldc.i4.7 + IL_0010: ldc.i4.0 + IL_0011: ldloc.1 + IL_0012: ldloc.2 + IL_0013: call ""CustomHandler..ctor(int, int, int, string)"" + IL_0018: ldloca.s V_3 + IL_001a: ldstr ""literal"" + IL_001f: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0024: pop + IL_0025: ldloc.3 + IL_0026: call ""void C.M(int, string, CustomHandler)"" + IL_002b: ret +} +" + : @" +{ + // Code size 52 (0x34) + .maxstack 7 + .locals init (string V_0, //s + int V_1, + string V_2, + CustomHandler V_3, + bool V_4) + IL_0000: ldc.i4.s 10 + IL_0002: ldstr ""arg"" + IL_0007: stloc.0 + IL_0008: stloc.1 + IL_0009: ldloc.1 + IL_000a: ldloc.0 + IL_000b: stloc.2 + IL_000c: ldloc.2 + IL_000d: ldc.i4.7 + IL_000e: ldc.i4.0 + IL_000f: ldloc.1 + IL_0010: ldloc.2 + IL_0011: ldloca.s V_4 + IL_0013: newobj ""CustomHandler..ctor(int, int, int, string, out bool)"" + IL_0018: stloc.3 + IL_0019: ldloc.s V_4 + IL_001b: brfalse.s IL_002b + IL_001d: ldloca.s V_3 + IL_001f: ldstr ""literal"" + IL_0024: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0029: br.s IL_002c + IL_002b: ldc.i4.0 + IL_002c: pop + IL_002d: ldloc.3 + IL_002e: call ""void C.M(int, string, CustomHandler)"" + IL_0033: ret +} +"); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_RefKindsMatch(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +int i = 1; +string s = null; +object o; +C.M(i, ref s, out o, $""literal""); +Console.WriteLine(s); +Console.WriteLine(o); + +public class C +{ + public static void M(in int i, ref string s, out object o, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"", ""o"")] CustomHandler c) + { + Console.WriteLine(s); + o = ""o in M""; + s = ""s in M""; + Console.Write(c.ToString()); + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, in int i, ref string s, out object o" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + o = null; + s = ""s in constructor""; + _builder.AppendLine(""i:"" + i.ToString()); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +s in constructor +i:1 +literal:literal +s in M +o in M +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 67 (0x43) + .maxstack 8 + .locals init (int V_0, //i + string V_1, //s + object V_2, //o + int& V_3, + string& V_4, + object& V_5, + CustomHandler V_6) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + IL_0004: ldloca.s V_0 + IL_0006: stloc.3 + IL_0007: ldloc.3 + IL_0008: ldloca.s V_1 + IL_000a: stloc.s V_4 + IL_000c: ldloc.s V_4 + IL_000e: ldloca.s V_2 + IL_0010: stloc.s V_5 + IL_0012: ldloc.s V_5 + IL_0014: ldc.i4.7 + IL_0015: ldc.i4.0 + IL_0016: ldloc.3 + IL_0017: ldloc.s V_4 + IL_0019: ldloc.s V_5 + IL_001b: newobj ""CustomHandler..ctor(int, int, in int, ref string, out object)"" + IL_0020: stloc.s V_6 + IL_0022: ldloca.s V_6 + IL_0024: ldstr ""literal"" + IL_0029: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002e: pop + IL_002f: ldloc.s V_6 + IL_0031: call ""void C.M(in int, ref string, out object, CustomHandler)"" + IL_0036: ldloc.1 + IL_0037: call ""void System.Console.WriteLine(string)"" + IL_003c: ldloc.2 + IL_003d: call ""void System.Console.WriteLine(object)"" + IL_0042: ret +} +" + : @" +{ + // Code size 76 (0x4c) + .maxstack 9 + .locals init (int V_0, //i + string V_1, //s + object V_2, //o + int& V_3, + string& V_4, + object& V_5, + CustomHandler V_6, + bool V_7) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldnull + IL_0003: stloc.1 + IL_0004: ldloca.s V_0 + IL_0006: stloc.3 + IL_0007: ldloc.3 + IL_0008: ldloca.s V_1 + IL_000a: stloc.s V_4 + IL_000c: ldloc.s V_4 + IL_000e: ldloca.s V_2 + IL_0010: stloc.s V_5 + IL_0012: ldloc.s V_5 + IL_0014: ldc.i4.7 + IL_0015: ldc.i4.0 + IL_0016: ldloc.3 + IL_0017: ldloc.s V_4 + IL_0019: ldloc.s V_5 + IL_001b: ldloca.s V_7 + IL_001d: newobj ""CustomHandler..ctor(int, int, in int, ref string, out object, out bool)"" + IL_0022: stloc.s V_6 + IL_0024: ldloc.s V_7 + IL_0026: brfalse.s IL_0036 + IL_0028: ldloca.s V_6 + IL_002a: ldstr ""literal"" + IL_002f: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0034: br.s IL_0037 + IL_0036: ldc.i4.0 + IL_0037: pop + IL_0038: ldloc.s V_6 + IL_003a: call ""void C.M(in int, ref string, out object, CustomHandler)"" + IL_003f: ldloc.1 + IL_0040: call ""void System.Console.WriteLine(string)"" + IL_0045: ldloc.2 + IL_0046: call ""void System.Console.WriteLine(object)"" + IL_004b: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(3).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0, 1, 2 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_ReorderedAttributePositions(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C.M(GetInt(), GetString(), $""literal""); + +int GetInt() +{ + Console.WriteLine(""GetInt""); + return 10; +} + +string GetString() +{ + Console.WriteLine(""GetString""); + return ""str""; +} + +public class C +{ + public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""s"", ""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, string s, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""s:"" + s); + _builder.AppendLine(""i:"" + i.ToString()); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +GetInt +GetString +s:str +i:10 +literal:literal +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 45 (0x2d) + .maxstack 7 + .locals init (string V_0, + int V_1, + CustomHandler V_2) + IL_0000: call ""int $.<
$>g__GetInt|0_0()"" + IL_0005: stloc.1 + IL_0006: ldloc.1 + IL_0007: call ""string $.<
$>g__GetString|0_1()"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: ldloca.s V_2 + IL_0010: ldc.i4.7 + IL_0011: ldc.i4.0 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: call ""CustomHandler..ctor(int, int, string, int)"" + IL_0019: ldloca.s V_2 + IL_001b: ldstr ""literal"" + IL_0020: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0025: pop + IL_0026: ldloc.2 + IL_0027: call ""void C.M(int, string, CustomHandler)"" + IL_002c: ret +} +" + : @" +{ + // Code size 52 (0x34) + .maxstack 7 + .locals init (string V_0, + int V_1, + CustomHandler V_2, + bool V_3) + IL_0000: call ""int $.<
$>g__GetInt|0_0()"" + IL_0005: stloc.1 + IL_0006: ldloc.1 + IL_0007: call ""string $.<
$>g__GetString|0_1()"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: ldc.i4.7 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldloc.1 + IL_0012: ldloca.s V_3 + IL_0014: newobj ""CustomHandler..ctor(int, int, string, int, out bool)"" + IL_0019: stloc.2 + IL_001a: ldloc.3 + IL_001b: brfalse.s IL_002b + IL_001d: ldloca.s V_2 + IL_001f: ldstr ""literal"" + IL_0024: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0029: br.s IL_002c + IL_002b: ldc.i4.0 + IL_002c: pop + IL_002d: ldloc.2 + IL_002e: call ""void C.M(int, string, CustomHandler)"" + IL_0033: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 1, 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_ParametersReordered(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +GetC().M(s: GetString(), i: GetInt(), c: $""literal""); + +C GetC() +{ + Console.WriteLine(""GetC""); + return new C { Field = 5 }; +} + +int GetInt() +{ + Console.WriteLine(""GetInt""); + return 10; +} + +string GetString() +{ + Console.WriteLine(""GetString""); + return ""str""; +} + +public class C +{ + public int Field; + public void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""s"", """", ""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, string s, C c, int i" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""s:"" + s); + _builder.AppendLine(""c.Field:"" + c.Field.ToString()); + _builder.AppendLine(""i:"" + i.ToString()); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +GetC +GetString +GetInt +s:str +c.Field:5 +i:10 +literal:literal +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 56 (0x38) + .maxstack 9 + .locals init (string V_0, + C V_1, + int V_2, + string V_3, + CustomHandler V_4) + IL_0000: call ""C $.<
$>g__GetC|0_0()"" + IL_0005: stloc.1 + IL_0006: ldloc.1 + IL_0007: call ""string $.<
$>g__GetString|0_2()"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: stloc.3 + IL_000f: call ""int $.<
$>g__GetInt|0_1()"" + IL_0014: stloc.2 + IL_0015: ldloc.2 + IL_0016: ldloc.3 + IL_0017: ldloca.s V_4 + IL_0019: ldc.i4.7 + IL_001a: ldc.i4.0 + IL_001b: ldloc.0 + IL_001c: ldloc.1 + IL_001d: ldloc.2 + IL_001e: call ""CustomHandler..ctor(int, int, string, C, int)"" + IL_0023: ldloca.s V_4 + IL_0025: ldstr ""literal"" + IL_002a: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002f: pop + IL_0030: ldloc.s V_4 + IL_0032: callvirt ""void C.M(int, string, CustomHandler)"" + IL_0037: ret +} +" + : @" +{ + // Code size 65 (0x41) + .maxstack 9 + .locals init (string V_0, + C V_1, + int V_2, + string V_3, + CustomHandler V_4, + bool V_5) + IL_0000: call ""C $.<
$>g__GetC|0_0()"" + IL_0005: stloc.1 + IL_0006: ldloc.1 + IL_0007: call ""string $.<
$>g__GetString|0_2()"" + IL_000c: stloc.0 + IL_000d: ldloc.0 + IL_000e: stloc.3 + IL_000f: call ""int $.<
$>g__GetInt|0_1()"" + IL_0014: stloc.2 + IL_0015: ldloc.2 + IL_0016: ldloc.3 + IL_0017: ldc.i4.7 + IL_0018: ldc.i4.0 + IL_0019: ldloc.0 + IL_001a: ldloc.1 + IL_001b: ldloc.2 + IL_001c: ldloca.s V_5 + IL_001e: newobj ""CustomHandler..ctor(int, int, string, C, int, out bool)"" + IL_0023: stloc.s V_4 + IL_0025: ldloc.s V_5 + IL_0027: brfalse.s IL_0037 + IL_0029: ldloca.s V_4 + IL_002b: ldstr ""literal"" + IL_0030: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0035: br.s IL_0038 + IL_0037: ldc.i4.0 + IL_0038: pop + IL_0039: ldloc.s V_4 + IL_003b: callvirt ""void C.M(int, string, CustomHandler)"" + IL_0040: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 1, -1, 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_Duplicated(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C.M(GetInt(), """", $""literal""); + +int GetInt() +{ + Console.WriteLine(""GetInt""); + return 10; +} + +public class C +{ + public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""i"")] CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i1, int i2" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i1:"" + i1.ToString()); + _builder.AppendLine(""i2:"" + i2.ToString()); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +GetInt +i1:10 +i2:10 +literal:literal +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 43 (0x2b) + .maxstack 7 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: call ""int $.<
$>g__GetInt|0_0()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldstr """" + IL_000c: ldloca.s V_1 + IL_000e: ldc.i4.7 + IL_000f: ldc.i4.0 + IL_0010: ldloc.0 + IL_0011: ldloc.0 + IL_0012: call ""CustomHandler..ctor(int, int, int, int)"" + IL_0017: ldloca.s V_1 + IL_0019: ldstr ""literal"" + IL_001e: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0023: pop + IL_0024: ldloc.1 + IL_0025: call ""void C.M(int, string, CustomHandler)"" + IL_002a: ret +} +" + : @" +{ + // Code size 50 (0x32) + .maxstack 7 + .locals init (int V_0, + CustomHandler V_1, + bool V_2) + IL_0000: call ""int $.<
$>g__GetInt|0_0()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldstr """" + IL_000c: ldc.i4.7 + IL_000d: ldc.i4.0 + IL_000e: ldloc.0 + IL_000f: ldloc.0 + IL_0010: ldloca.s V_2 + IL_0012: newobj ""CustomHandler..ctor(int, int, int, int, out bool)"" + IL_0017: stloc.1 + IL_0018: ldloc.2 + IL_0019: brfalse.s IL_0029 + IL_001b: ldloca.s V_1 + IL_001d: ldstr ""literal"" + IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: br.s IL_002a + IL_0029: ldc.i4.0 + IL_002a: pop + IL_002b: ldloc.1 + IL_002c: call ""void C.M(int, string, CustomHandler)"" + IL_0031: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0, 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_EmptyWithMatchingConstructor(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C.M(1, """", $""""); + +public class C +{ + public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute()] CustomHandler c) => Console.WriteLine(c.ToString()); +} +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount" + extraConstructorArg + @") + { +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: "CustomHandler").VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 19 (0x13) + .maxstack 4 + IL_0000: ldc.i4.1 + IL_0001: ldstr """" + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.0 + IL_0008: newobj ""CustomHandler..ctor(int, int)"" + IL_000d: call ""void C.M(int, string, CustomHandler)"" + IL_0012: ret +} +" + : @" +{ + // Code size 21 (0x15) + .maxstack 5 + .locals init (bool V_0) + IL_0000: ldc.i4.1 + IL_0001: ldstr """" + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.0 + IL_0008: ldloca.s V_0 + IL_000a: newobj ""CustomHandler..ctor(int, int, out bool)"" + IL_000f: call ""void C.M(int, string, CustomHandler)"" + IL_0014: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_EmptyWithoutMatchingConstructor(string extraConstructorArg) + { + var code = @" +using System.Runtime.CompilerServices; +public class C +{ + public static void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute()] CustomHandler c) { } +} +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i" + extraConstructorArg + @") + { +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }); + // https://github.com/dotnet/roslyn/issues/53981 tracks warning here in the future, with user feedback. + CompileAndVerify(comp, symbolValidator: validate, sourceSymbolValidator: validate).VerifyDiagnostics(); + + CreateCompilation(@"C.M(1, """", $"""");", new[] { comp.EmitToImageReference() }).VerifyDiagnostics( + (extraConstructorArg == "") + ? new[] + { + // (1,12): error CS7036: There is no argument given that corresponds to the required formal parameter 'i' of 'CustomHandler.CustomHandler(int, int, int)' + // C.M(1, "", $""); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("i", "CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 12), + // (1,12): error CS1615: Argument 3 may not be passed with the 'out' keyword + // C.M(1, "", $""); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""""").WithArguments("3", "out").WithLocation(1, 12) + } + : new[] + { + // (1,12): error CS7036: There is no argument given that corresponds to the required formal parameter 'i' of 'CustomHandler.CustomHandler(int, int, int, out bool)' + // C.M(1, "", $""); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("i", "CustomHandler.CustomHandler(int, int, int, out bool)").WithLocation(1, 12), + // (1,12): error CS7036: There is no argument given that corresponds to the required formal parameter 'success' of 'CustomHandler.CustomHandler(int, int, int, out bool)' + // C.M(1, "", $""); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, @"$""""").WithArguments("success", "CustomHandler.CustomHandler(int, int, int, out bool)").WithLocation(1, 12) + } + ); + + static void validate(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Empty(cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_OnIndexerRvalue(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +var c = new C(); +Console.WriteLine(c[10, ""str"", $""literal""]); + +public class C +{ + public string this[int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"")] CustomHandler c] { get => c.ToString(); } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i1, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i1:"" + i1.ToString()); + _builder.AppendLine(""s:"" + s); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +i1:10 +s:str +literal:literal +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 52 (0x34) + .maxstack 8 + .locals init (int V_0, + string V_1, + CustomHandler V_2) + IL_0000: newobj ""C..ctor()"" + IL_0005: ldc.i4.s 10 + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldstr ""str"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloca.s V_2 + IL_0012: ldc.i4.7 + IL_0013: ldc.i4.0 + IL_0014: ldloc.0 + IL_0015: ldloc.1 + IL_0016: call ""CustomHandler..ctor(int, int, int, string)"" + IL_001b: ldloca.s V_2 + IL_001d: ldstr ""literal"" + IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: pop + IL_0028: ldloc.2 + IL_0029: callvirt ""string C.this[int, string, CustomHandler].get"" + IL_002e: call ""void System.Console.WriteLine(string)"" + IL_0033: ret +} +" + : @" +{ + // Code size 59 (0x3b) + .maxstack 8 + .locals init (int V_0, + string V_1, + CustomHandler V_2, + bool V_3) + IL_0000: newobj ""C..ctor()"" + IL_0005: ldc.i4.s 10 + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldstr ""str"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldc.i4.7 + IL_0011: ldc.i4.0 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_3 + IL_0016: newobj ""CustomHandler..ctor(int, int, int, string, out bool)"" + IL_001b: stloc.2 + IL_001c: ldloc.3 + IL_001d: brfalse.s IL_002d + IL_001f: ldloca.s V_2 + IL_0021: ldstr ""literal"" + IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002b: br.s IL_002e + IL_002d: ldc.i4.0 + IL_002e: pop + IL_002f: ldloc.2 + IL_0030: callvirt ""string C.this[int, string, CustomHandler].get"" + IL_0035: call ""void System.Console.WriteLine(string)"" + IL_003a: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetIndexer("Item").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_OnIndexerLvalue(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +var c = new C(); +c[10, ""str"", $""literal""] = """"; + +public class C +{ + public string this[int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", ""s"")] CustomHandler c] { set => Console.WriteLine(c.ToString()); } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i1, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i1:"" + i1.ToString()); + _builder.AppendLine(""s:"" + s); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +i1:10 +s:str +literal:literal +"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 52 (0x34) + .maxstack 8 + .locals init (int V_0, + string V_1, + CustomHandler V_2) + IL_0000: newobj ""C..ctor()"" + IL_0005: ldc.i4.s 10 + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldstr ""str"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldloca.s V_2 + IL_0012: ldc.i4.7 + IL_0013: ldc.i4.0 + IL_0014: ldloc.0 + IL_0015: ldloc.1 + IL_0016: call ""CustomHandler..ctor(int, int, int, string)"" + IL_001b: ldloca.s V_2 + IL_001d: ldstr ""literal"" + IL_0022: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0027: pop + IL_0028: ldloc.2 + IL_0029: ldstr """" + IL_002e: callvirt ""void C.this[int, string, CustomHandler].set"" + IL_0033: ret +} +" + : @" +{ + // Code size 59 (0x3b) + .maxstack 8 + .locals init (int V_0, + string V_1, + CustomHandler V_2, + bool V_3) + IL_0000: newobj ""C..ctor()"" + IL_0005: ldc.i4.s 10 + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldstr ""str"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldc.i4.7 + IL_0011: ldc.i4.0 + IL_0012: ldloc.0 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_3 + IL_0016: newobj ""CustomHandler..ctor(int, int, int, string, out bool)"" + IL_001b: stloc.2 + IL_001c: ldloc.3 + IL_001d: brfalse.s IL_002d + IL_001f: ldloca.s V_2 + IL_0021: ldstr ""literal"" + IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002b: br.s IL_002e + IL_002d: ldc.i4.0 + IL_002e: pop + IL_002f: ldloc.2 + IL_0030: ldstr """" + IL_0035: callvirt ""void C.this[int, string, CustomHandler].set"" + IL_003a: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetIndexer("Item").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void InterpolatedStringHandlerArgumentAttribute_ThisParameter(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +(new C(5)).M((int)10, ""str"", $""literal""); + +public class C +{ + public int Prop { get; } + public C(int i) => Prop = i; + public void M(int i, string s, [InterpolatedStringHandlerArgumentAttribute(""i"", """", ""s"")] CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i1, C c, string s" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i1:"" + i1.ToString()); + _builder.AppendLine(""c.Prop:"" + c.Prop.ToString()); + _builder.AppendLine(""s:"" + s); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +i1:10 +c.Prop:5 +s:str +literal:literal +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 51 (0x33) + .maxstack 9 + .locals init (int V_0, + C V_1, + string V_2, + CustomHandler V_3) + IL_0000: ldc.i4.5 + IL_0001: newobj ""C..ctor(int)"" + IL_0006: stloc.1 + IL_0007: ldloc.1 + IL_0008: ldc.i4.s 10 + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: ldstr ""str"" + IL_0011: stloc.2 + IL_0012: ldloc.2 + IL_0013: ldloca.s V_3 + IL_0015: ldc.i4.7 + IL_0016: ldc.i4.0 + IL_0017: ldloc.0 + IL_0018: ldloc.1 + IL_0019: ldloc.2 + IL_001a: call ""CustomHandler..ctor(int, int, int, C, string)"" + IL_001f: ldloca.s V_3 + IL_0021: ldstr ""literal"" + IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002b: pop + IL_002c: ldloc.3 + IL_002d: callvirt ""void C.M(int, string, CustomHandler)"" + IL_0032: ret +} +" + : @" +{ + // Code size 59 (0x3b) + .maxstack 9 + .locals init (int V_0, + C V_1, + string V_2, + CustomHandler V_3, + bool V_4) + IL_0000: ldc.i4.5 + IL_0001: newobj ""C..ctor(int)"" + IL_0006: stloc.1 + IL_0007: ldloc.1 + IL_0008: ldc.i4.s 10 + IL_000a: stloc.0 + IL_000b: ldloc.0 + IL_000c: ldstr ""str"" + IL_0011: stloc.2 + IL_0012: ldloc.2 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.0 + IL_0015: ldloc.0 + IL_0016: ldloc.1 + IL_0017: ldloc.2 + IL_0018: ldloca.s V_4 + IL_001a: newobj ""CustomHandler..ctor(int, int, int, C, string, out bool)"" + IL_001f: stloc.3 + IL_0020: ldloc.s V_4 + IL_0022: brfalse.s IL_0032 + IL_0024: ldloca.s V_3 + IL_0026: ldstr ""literal"" + IL_002b: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0030: br.s IL_0033 + IL_0032: ldc.i4.0 + IL_0033: pop + IL_0034: ldloc.3 + IL_0035: callvirt ""void C.M(int, string, CustomHandler)"" + IL_003a: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(2).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0, -1, 1 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Fact] + public void InterpolatedStringHandlerArgumentAttribute_OnConstructor() + { + + var code = @" +using System; +using System.Runtime.CompilerServices; + +_ = new C(5, $""literal""); + +public class C +{ + public int Prop { get; } + public C(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")]CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int i) : this(literalLength, formattedCount) + { + _builder.AppendLine(""i:"" + i.ToString()); + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +i:5 +literal:literal +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 34 (0x22) + .maxstack 5 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: ldc.i4.5 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldloca.s V_1 + IL_0005: ldc.i4.7 + IL_0006: ldc.i4.0 + IL_0007: ldloc.0 + IL_0008: call ""CustomHandler..ctor(int, int, int)"" + IL_000d: ldloca.s V_1 + IL_000f: ldstr ""literal"" + IL_0014: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0019: pop + IL_001a: ldloc.1 + IL_001b: newobj ""C..ctor(int, CustomHandler)"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void RefReturningMethodAsReceiver_Success(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C c = new C(1); +GetC(ref c).M($""literal""); +Console.WriteLine(c.I); + +ref C GetC(ref C c) +{ + Console.WriteLine(""GetC""); + return ref c; +} + +public class C +{ + public int I; + public C(int i) + { + I = i; + } + + public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, ref C c" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + c = new C(2); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, verify: ExecutionConditionUtil.IsWindowsDesktop ? Verification.Skipped : Verification.Passes, expectedOutput: @" +GetC +literal:literal + +2 +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 57 (0x39) + .maxstack 4 + .locals init (C V_0, //c + C& V_1, + CustomHandler V_2) + IL_0000: ldc.i4.1 + IL_0001: newobj ""C..ctor(int)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""ref C $.<
$>g__GetC|0_0(ref C)"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldind.ref + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.0 + IL_0013: ldloc.1 + IL_0014: newobj ""CustomHandler..ctor(int, int, ref C)"" + IL_0019: stloc.2 + IL_001a: ldloca.s V_2 + IL_001c: ldstr ""literal"" + IL_0021: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0026: pop + IL_0027: ldloc.2 + IL_0028: callvirt ""void C.M(CustomHandler)"" + IL_002d: ldloc.0 + IL_002e: ldfld ""int C.I"" + IL_0033: call ""void System.Console.WriteLine(int)"" + IL_0038: ret +} +" + : @" +{ + // Code size 65 (0x41) + .maxstack 5 + .locals init (C V_0, //c + C& V_1, + CustomHandler V_2, + bool V_3) + IL_0000: ldc.i4.1 + IL_0001: newobj ""C..ctor(int)"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_0 + IL_0009: call ""ref C $.<
$>g__GetC|0_0(ref C)"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: ldind.ref + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.0 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_3 + IL_0016: newobj ""CustomHandler..ctor(int, int, ref C, out bool)"" + IL_001b: stloc.2 + IL_001c: ldloc.3 + IL_001d: brfalse.s IL_002d + IL_001f: ldloca.s V_2 + IL_0021: ldstr ""literal"" + IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002b: br.s IL_002e + IL_002d: ldc.i4.0 + IL_002e: pop + IL_002f: ldloc.2 + IL_0030: callvirt ""void C.M(CustomHandler)"" + IL_0035: ldloc.0 + IL_0036: ldfld ""int C.I"" + IL_003b: call ""void System.Console.WriteLine(int)"" + IL_0040: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { -1 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("")] + [InlineData(", out bool success")] + public void RefReturningMethodAsReceiver_Success_StructReceiver(string extraConstructorArg) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C c = new C(1); +GetC(ref c).M($""literal""); +Console.WriteLine(c.I); + +ref C GetC(ref C c) +{ + Console.WriteLine(""GetC""); + return ref c; +} + +public struct C +{ + public int I; + public C(int i) + { + I = i; + } + + public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, ref C c" + extraConstructorArg + @") : this(literalLength, formattedCount) + { + c = new C(2); +" + (extraConstructorArg != "" ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, verify: ExecutionConditionUtil.IsWindowsDesktop ? Verification.Skipped : Verification.Passes, expectedOutput: @" +GetC +literal:literal + +2 +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", (extraConstructorArg == "") + ? @" +{ + // Code size 57 (0x39) + .maxstack 4 + .locals init (C V_0, //c + C& V_1, + CustomHandler V_2) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: call ""C..ctor(int)"" + IL_0008: ldloca.s V_0 + IL_000a: call ""ref C $.<
$>g__GetC|0_0(ref C)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.0 + IL_0013: ldloc.1 + IL_0014: newobj ""CustomHandler..ctor(int, int, ref C)"" + IL_0019: stloc.2 + IL_001a: ldloca.s V_2 + IL_001c: ldstr ""literal"" + IL_0021: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0026: pop + IL_0027: ldloc.2 + IL_0028: call ""void C.M(CustomHandler)"" + IL_002d: ldloc.0 + IL_002e: ldfld ""int C.I"" + IL_0033: call ""void System.Console.WriteLine(int)"" + IL_0038: ret +} +" + : @" +{ + // Code size 65 (0x41) + .maxstack 5 + .locals init (C V_0, //c + C& V_1, + CustomHandler V_2, + bool V_3) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.1 + IL_0003: call ""C..ctor(int)"" + IL_0008: ldloca.s V_0 + IL_000a: call ""ref C $.<
$>g__GetC|0_0(ref C)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.0 + IL_0013: ldloc.1 + IL_0014: ldloca.s V_3 + IL_0016: newobj ""CustomHandler..ctor(int, int, ref C, out bool)"" + IL_001b: stloc.2 + IL_001c: ldloc.3 + IL_001d: brfalse.s IL_002d + IL_001f: ldloca.s V_2 + IL_0021: ldstr ""literal"" + IL_0026: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002b: br.s IL_002e + IL_002d: ldc.i4.0 + IL_002e: pop + IL_002f: ldloc.2 + IL_0030: call ""void C.M(CustomHandler)"" + IL_0035: ldloc.0 + IL_0036: ldfld ""int C.I"" + IL_003b: call ""void System.Console.WriteLine(int)"" + IL_0040: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { -1 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [InlineData("ref readonly")] + [InlineData("")] + public void RefReturningMethodAsReceiver_MismatchedRefness_01(string refness) + { + var code = @" +using System.Runtime.CompilerServices; + +C c = new C(1); +GetC().M($""literal""); + +" + refness + @" C GetC() => throw null; + +public class C +{ + public C(int i) { } + public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) { } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, ref C c) : this(literalLength, formattedCount) { } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (5,10): error CS1620: Argument 3 must be passed with the 'ref' keyword + // GetC().M($"literal"); + Diagnostic(ErrorCode.ERR_BadArgRef, @"$""literal""").WithArguments("3", "ref").WithLocation(5, 10) + ); + } + + [Theory] + [InlineData("in")] + [InlineData("")] + public void RefReturningMethodAsReceiver_MismatchedRefness_02(string refness) + { + var code = @" +using System.Runtime.CompilerServices; + +C c = new C(1); +GetC(ref c).M($""literal""); + +ref C GetC(ref C c) => ref c; + +public class C +{ + public C(int i) { } + public void M([InterpolatedStringHandlerArgument("""")]CustomHandler c) { } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount," + refness + @" C c) : this(literalLength, formattedCount) { } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (5,15): error CS1615: Argument 3 may not be passed with the 'ref' keyword + // GetC(ref c).M($"literal"); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, @"$""literal""").WithArguments("3", "ref").WithLocation(5, 15) + ); + } + + [Fact] + public void StructReceiver_Rvalue() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +S s1 = new S { I = 1 }; +S s2 = new S { I = 2 }; + +s1.M(s2, $""""); + +public struct S +{ + public int I; + + public void M(S s2, [InterpolatedStringHandlerArgument("""", ""s2"")]CustomHandler handler) + { + Console.WriteLine(""s1.I:"" + this.I.ToString()); + Console.WriteLine(""s2.I:"" + s2.I.ToString()); + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, S s1, S s2) : this(literalLength, formattedCount) + { + s1.I = 3; + s2.I = 4; + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +s1.I:1 +s2.I:2"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 56 (0x38) + .maxstack 6 + .locals init (S V_0, //s2 + S V_1, + S V_2) + IL_0000: ldloca.s V_1 + IL_0002: initobj ""S"" + IL_0008: ldloca.s V_1 + IL_000a: ldc.i4.1 + IL_000b: stfld ""int S.I"" + IL_0010: ldloc.1 + IL_0011: ldloca.s V_1 + IL_0013: initobj ""S"" + IL_0019: ldloca.s V_1 + IL_001b: ldc.i4.2 + IL_001c: stfld ""int S.I"" + IL_0021: ldloc.1 + IL_0022: stloc.0 + IL_0023: stloc.1 + IL_0024: ldloca.s V_1 + IL_0026: ldloc.0 + IL_0027: stloc.2 + IL_0028: ldloc.2 + IL_0029: ldc.i4.0 + IL_002a: ldc.i4.0 + IL_002b: ldloc.1 + IL_002c: ldloc.2 + IL_002d: newobj ""CustomHandler..ctor(int, int, S, S)"" + IL_0032: call ""void S.M(S, CustomHandler)"" + IL_0037: ret +} +"); + } + + [Fact] + public void StructReceiver_Lvalue() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +S s1 = new S { I = 1 }; +S s2 = new S { I = 2 }; + +s1.M(ref s2, $""""); + +public struct S +{ + public int I; + + public void M(ref S s2, [InterpolatedStringHandlerArgument("""", ""s2"")]CustomHandler handler) + { + Console.WriteLine(""s1.I:"" + this.I.ToString()); + Console.WriteLine(""s2.I:"" + s2.I.ToString()); + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, ref S s1, ref S s2) : this(literalLength, formattedCount) + { + s1.I = 3; + s2.I = 4; + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + comp.VerifyDiagnostics( + // (8,14): error CS1620: Argument 3 must be passed with the 'ref' keyword + // s1.M(ref s2, $""); + Diagnostic(ErrorCode.ERR_BadArgRef, @"$""""").WithArguments("3", "ref").WithLocation(8, 14) + ); + } + + [Fact] + public void StructParameter_ByVal() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +S s = new S { I = 1 }; + +S.M(s, $""""); + +public struct S +{ + public int I; + + public static void M(S s, [InterpolatedStringHandlerArgument(""s"")]CustomHandler handler) + { + Console.WriteLine(""s.I:"" + s.I.ToString()); + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, S s) : this(literalLength, formattedCount) + { + s.I = 2; + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + + var verifier = CompileAndVerify(comp, expectedOutput: @"s.I:1"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 33 (0x21) + .maxstack 4 + .locals init (S V_0) + IL_0000: ldloca.s V_0 + IL_0002: initobj ""S"" + IL_0008: ldloca.s V_0 + IL_000a: ldc.i4.1 + IL_000b: stfld ""int S.I"" + IL_0010: ldloc.0 + IL_0011: stloc.0 + IL_0012: ldloc.0 + IL_0013: ldc.i4.0 + IL_0014: ldc.i4.0 + IL_0015: ldloc.0 + IL_0016: newobj ""CustomHandler..ctor(int, int, S)"" + IL_001b: call ""void S.M(S, CustomHandler)"" + IL_0020: ret +} +"); + } + + [Fact] + public void StructParameter_ByRef() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +S s = new S { I = 1 }; + +S.M(ref s, $""""); + +public struct S +{ + public int I; + + public static void M(ref S s, [InterpolatedStringHandlerArgument(""s"")]CustomHandler handler) + { + Console.WriteLine(""s.I:"" + s.I.ToString()); + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, ref S s) : this(literalLength, formattedCount) + { + s.I = 2; + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + + var verifier = CompileAndVerify(comp, expectedOutput: @"s.I:2"); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 36 (0x24) + .maxstack 4 + .locals init (S V_0, //s + S V_1, + S& V_2) + IL_0000: ldloca.s V_1 + IL_0002: initobj ""S"" + IL_0008: ldloca.s V_1 + IL_000a: ldc.i4.1 + IL_000b: stfld ""int S.I"" + IL_0010: ldloc.1 + IL_0011: stloc.0 + IL_0012: ldloca.s V_0 + IL_0014: stloc.2 + IL_0015: ldloc.2 + IL_0016: ldc.i4.0 + IL_0017: ldc.i4.0 + IL_0018: ldloc.2 + IL_0019: newobj ""CustomHandler..ctor(int, int, ref S)"" + IL_001e: call ""void S.M(ref S, CustomHandler)"" + IL_0023: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void SideEffects(bool useBoolReturns, bool validityParameter) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +GetReceiver().M( + GetArg(""Unrelated parameter 1""), + GetArg(""Second value""), + GetArg(""Unrelated parameter 2""), + GetArg(""First value""), + $""literal"", + GetArg(""Unrelated parameter 4"")); + +C GetReceiver() +{ + Console.WriteLine(""GetReceiver""); + return new C() { Prop = ""Prop"" }; +} + +string GetArg(string s) +{ + Console.WriteLine(s); + return s; +} + +public class C +{ + public string Prop { get; set; } + public void M(string param1, string param2, string param3, string param4, [InterpolatedStringHandlerArgument(""param4"", """", ""param2"")] CustomHandler c, string param6) + => Console.WriteLine(c.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, string s1, C c, string s2" + (validityParameter ? ", out bool success" : "") + @") + : this(literalLength, formattedCount) + { + Console.WriteLine(""Handler constructor""); + _builder.AppendLine(""s1:"" + s1); + _builder.AppendLine(""c.Prop:"" + c.Prop); + _builder.AppendLine(""s2:"" + s2); + " + (validityParameter ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns); + + var verifier = CompileAndVerify(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }, expectedOutput: @" +GetReceiver +Unrelated parameter 1 +Second value +Unrelated parameter 2 +First value +Handler constructor +Unrelated parameter 4 +s1:First value +c.Prop:Prop +s2:Second value +literal:literal +"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void InterpolatedStringHandlerArgumentsAttribute_ConversionFromArgumentType() + { + var code = @" +using System; +using System.Globalization; +using System.Runtime.CompilerServices; + +int i = 1; +C.M(i, $""literal""); + +public class C +{ + public static implicit operator C(int i) => throw null; + public static implicit operator C(double d) + { + Console.WriteLine(d.ToString(""G"", CultureInfo.InvariantCulture)); + return new C(); + } + public override string ToString() => ""C""; + + public static void M(double d, [InterpolatedStringHandlerArgument(""d"")] CustomHandler handler) => Console.WriteLine(handler.ToString()); +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) { } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, sourceSymbolValidator: validator, symbolValidator: validator, expectedOutput: @" +1 +literal:literal +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 39 (0x27) + .maxstack 5 + .locals init (double V_0, + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: conv.r8 + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: ldloca.s V_1 + IL_0006: ldc.i4.7 + IL_0007: ldc.i4.0 + IL_0008: ldloc.0 + IL_0009: call ""C C.op_Implicit(double)"" + IL_000e: call ""CustomHandler..ctor(int, int, C)"" + IL_0013: ldloca.s V_1 + IL_0015: ldstr ""literal"" + IL_001a: call ""bool CustomHandler.AppendLiteral(string)"" + IL_001f: pop + IL_0020: ldloc.1 + IL_0021: call ""void C.M(double, CustomHandler)"" + IL_0026: ret +} +"); + + static void validator(ModuleSymbol module) + { + var cParam = module.GlobalNamespace.GetTypeMember("C").GetMethod("M").Parameters.Skip(1).Single(); + AssertEx.Equal("System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute", + cParam.GetAttributes().Single().AttributeClass.ToTestDisplayString()); + Assert.Equal(new[] { 0 }, cParam.InterpolatedStringHandlerArgumentIndexes); + } + } + + [Theory] + [CombinatorialData] + public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_01(bool useBoolReturns, bool validityParameter) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +int i = 3; +GetC()[GetInt(1), $""literal{i}""] += GetInt(2); + +static C GetC() +{ + Console.WriteLine(""GetC""); + return new C() { Prop = 2 }; +} + +static int GetInt(int i) +{ + Console.WriteLine(""GetInt"" + i.ToString()); + return 1; +} + +public class C +{ + public int Prop { get; set; } + public int this[int arg1, [InterpolatedStringHandlerArgument(""arg1"", """")] CustomHandler c] + { + get + { + Console.WriteLine(""Indexer getter""); + return 0; + } + set + { + Console.WriteLine(""Indexer setter""); + Console.WriteLine(c.ToString()); + } + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int arg1, C c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + Console.WriteLine(""Handler constructor""); + _builder.AppendLine(""arg1:"" + arg1); + _builder.AppendLine(""C.Prop:"" + c.Prop.ToString()); + " + (validityParameter ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: useBoolReturns); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +GetC +GetInt1 +Handler constructor +Indexer getter +GetInt2 +Indexer setter +arg1:1 +C.Prop:2 +literal:literal +value:3 +alignment:0 +format: +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", getIl()); + + string getIl() => (useBoolReturns, validityParameter) switch + { + (useBoolReturns: false, validityParameter: false) => @" +{ + // Code size 85 (0x55) + .maxstack 6 + .locals init (int V_0, //i + int V_1, + C V_2, + int V_3, + CustomHandler V_4, + CustomHandler V_5) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldc.i4.1 + IL_0009: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: stloc.3 + IL_0011: ldloca.s V_5 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.1 + IL_0015: ldloc.1 + IL_0016: ldloc.2 + IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" + IL_001c: ldloca.s V_5 + IL_001e: ldstr ""literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloca.s V_5 + IL_002a: ldloc.0 + IL_002b: box ""int"" + IL_0030: ldc.i4.0 + IL_0031: ldnull + IL_0032: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0037: ldloc.s V_5 + IL_0039: stloc.s V_4 + IL_003b: ldloc.2 + IL_003c: ldloc.3 + IL_003d: ldloc.s V_4 + IL_003f: ldloc.2 + IL_0040: ldloc.3 + IL_0041: ldloc.s V_4 + IL_0043: callvirt ""int C.this[int, CustomHandler].get"" + IL_0048: ldc.i4.2 + IL_0049: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_004e: add + IL_004f: callvirt ""void C.this[int, CustomHandler].set"" + IL_0054: ret +} +", + (useBoolReturns: false, validityParameter: true) => @" +{ + // Code size 96 (0x60) + .maxstack 6 + .locals init (int V_0, //i + int V_1, + C V_2, + int V_3, + CustomHandler V_4, + CustomHandler V_5, + bool V_6) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldc.i4.1 + IL_0009: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: stloc.3 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.1 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloca.s V_6 + IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" + IL_001c: stloc.s V_5 + IL_001e: ldloc.s V_6 + IL_0020: brfalse.s IL_0040 + IL_0022: ldloca.s V_5 + IL_0024: ldstr ""literal"" + IL_0029: call ""void CustomHandler.AppendLiteral(string)"" + IL_002e: ldloca.s V_5 + IL_0030: ldloc.0 + IL_0031: box ""int"" + IL_0036: ldc.i4.0 + IL_0037: ldnull + IL_0038: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_003d: ldc.i4.1 + IL_003e: br.s IL_0041 + IL_0040: ldc.i4.0 + IL_0041: pop + IL_0042: ldloc.s V_5 + IL_0044: stloc.s V_4 + IL_0046: ldloc.2 + IL_0047: ldloc.3 + IL_0048: ldloc.s V_4 + IL_004a: ldloc.2 + IL_004b: ldloc.3 + IL_004c: ldloc.s V_4 + IL_004e: callvirt ""int C.this[int, CustomHandler].get"" + IL_0053: ldc.i4.2 + IL_0054: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_0059: add + IL_005a: callvirt ""void C.this[int, CustomHandler].set"" + IL_005f: ret +} +", + (useBoolReturns: true, validityParameter: false) => @" +{ + // Code size 91 (0x5b) + .maxstack 6 + .locals init (int V_0, //i + int V_1, + C V_2, + int V_3, + CustomHandler V_4, + CustomHandler V_5) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldc.i4.1 + IL_0009: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: stloc.3 + IL_0011: ldloca.s V_5 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.1 + IL_0015: ldloc.1 + IL_0016: ldloc.2 + IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" + IL_001c: ldloca.s V_5 + IL_001e: ldstr ""literal"" + IL_0023: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0028: brfalse.s IL_003b + IL_002a: ldloca.s V_5 + IL_002c: ldloc.0 + IL_002d: box ""int"" + IL_0032: ldc.i4.0 + IL_0033: ldnull + IL_0034: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_0039: br.s IL_003c + IL_003b: ldc.i4.0 + IL_003c: pop + IL_003d: ldloc.s V_5 + IL_003f: stloc.s V_4 + IL_0041: ldloc.2 + IL_0042: ldloc.3 + IL_0043: ldloc.s V_4 + IL_0045: ldloc.2 + IL_0046: ldloc.3 + IL_0047: ldloc.s V_4 + IL_0049: callvirt ""int C.this[int, CustomHandler].get"" + IL_004e: ldc.i4.2 + IL_004f: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_0054: add + IL_0055: callvirt ""void C.this[int, CustomHandler].set"" + IL_005a: ret +} +", + (useBoolReturns: true, validityParameter: true) => @" +{ + // Code size 97 (0x61) + .maxstack 6 + .locals init (int V_0, //i + int V_1, + C V_2, + int V_3, + CustomHandler V_4, + CustomHandler V_5, + bool V_6) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldc.i4.1 + IL_0009: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000e: stloc.1 + IL_000f: ldloc.1 + IL_0010: stloc.3 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.1 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloca.s V_6 + IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" + IL_001c: stloc.s V_5 + IL_001e: ldloc.s V_6 + IL_0020: brfalse.s IL_0041 + IL_0022: ldloca.s V_5 + IL_0024: ldstr ""literal"" + IL_0029: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002e: brfalse.s IL_0041 + IL_0030: ldloca.s V_5 + IL_0032: ldloc.0 + IL_0033: box ""int"" + IL_0038: ldc.i4.0 + IL_0039: ldnull + IL_003a: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_003f: br.s IL_0042 + IL_0041: ldc.i4.0 + IL_0042: pop + IL_0043: ldloc.s V_5 + IL_0045: stloc.s V_4 + IL_0047: ldloc.2 + IL_0048: ldloc.3 + IL_0049: ldloc.s V_4 + IL_004b: ldloc.2 + IL_004c: ldloc.3 + IL_004d: ldloc.s V_4 + IL_004f: callvirt ""int C.this[int, CustomHandler].get"" + IL_0054: ldc.i4.2 + IL_0055: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_005a: add + IL_005b: callvirt ""void C.this[int, CustomHandler].set"" + IL_0060: ret +} +", + }; + } + + [Theory] + [CombinatorialData] + public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_02(bool useBoolReturns, bool validityParameter) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +int i = 3; +GetC()[GetInt(1), $""literal{i}""] += GetInt(2); + +static C GetC() +{ + Console.WriteLine(""GetC""); + return new C() { Prop = 2 }; +} + +static int GetInt(int i) +{ + Console.WriteLine(""GetInt"" + i.ToString()); + return 1; +} + +public class C +{ + private int field; + public int Prop { get; set; } + public ref int this[int arg1, [InterpolatedStringHandlerArgument(""arg1"", """")] CustomHandler c] + { + get + { + Console.WriteLine(""Indexer getter""); + Console.WriteLine(c.ToString()); + return ref field; + } + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int arg1, C c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + Console.WriteLine(""Handler constructor""); + _builder.AppendLine(""arg1:"" + arg1); + _builder.AppendLine(""C.Prop:"" + c.Prop.ToString()); + " + (validityParameter ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: useBoolReturns); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +GetC +GetInt1 +Handler constructor +Indexer getter +arg1:1 +C.Prop:2 +literal:literal +value:3 +alignment:0 +format: + +GetInt2 +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", getIl()); + + string getIl() => (useBoolReturns, validityParameter) switch + { + (useBoolReturns: false, validityParameter: false) => @" +{ + // Code size 72 (0x48) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldloca.s V_3 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.1 + IL_0015: ldloc.1 + IL_0016: ldloc.2 + IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" + IL_001c: ldloca.s V_3 + IL_001e: ldstr ""literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloca.s V_3 + IL_002a: ldloc.0 + IL_002b: box ""int"" + IL_0030: ldc.i4.0 + IL_0031: ldnull + IL_0032: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0037: ldloc.3 + IL_0038: callvirt ""ref int C.this[int, CustomHandler].get"" + IL_003d: dup + IL_003e: ldind.i4 + IL_003f: ldc.i4.2 + IL_0040: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_0045: add + IL_0046: stind.i4 + IL_0047: ret +} +", + (useBoolReturns: false, validityParameter: true) => @" +{ + // Code size 82 (0x52) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3, + bool V_4) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.1 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloca.s V_4 + IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" + IL_001c: stloc.3 + IL_001d: ldloc.s V_4 + IL_001f: brfalse.s IL_003f + IL_0021: ldloca.s V_3 + IL_0023: ldstr ""literal"" + IL_0028: call ""void CustomHandler.AppendLiteral(string)"" + IL_002d: ldloca.s V_3 + IL_002f: ldloc.0 + IL_0030: box ""int"" + IL_0035: ldc.i4.0 + IL_0036: ldnull + IL_0037: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_003c: ldc.i4.1 + IL_003d: br.s IL_0040 + IL_003f: ldc.i4.0 + IL_0040: pop + IL_0041: ldloc.3 + IL_0042: callvirt ""ref int C.this[int, CustomHandler].get"" + IL_0047: dup + IL_0048: ldind.i4 + IL_0049: ldc.i4.2 + IL_004a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_004f: add + IL_0050: stind.i4 + IL_0051: ret +} +", + (useBoolReturns: true, validityParameter: false) => @" +{ + // Code size 78 (0x4e) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldloca.s V_3 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.1 + IL_0015: ldloc.1 + IL_0016: ldloc.2 + IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" + IL_001c: ldloca.s V_3 + IL_001e: ldstr ""literal"" + IL_0023: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0028: brfalse.s IL_003b + IL_002a: ldloca.s V_3 + IL_002c: ldloc.0 + IL_002d: box ""int"" + IL_0032: ldc.i4.0 + IL_0033: ldnull + IL_0034: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_0039: br.s IL_003c + IL_003b: ldc.i4.0 + IL_003c: pop + IL_003d: ldloc.3 + IL_003e: callvirt ""ref int C.this[int, CustomHandler].get"" + IL_0043: dup + IL_0044: ldind.i4 + IL_0045: ldc.i4.2 + IL_0046: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_004b: add + IL_004c: stind.i4 + IL_004d: ret +} +", + (useBoolReturns: true, validityParameter: true) => @" +{ + // Code size 83 (0x53) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3, + bool V_4) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.1 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloca.s V_4 + IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" + IL_001c: stloc.3 + IL_001d: ldloc.s V_4 + IL_001f: brfalse.s IL_0040 + IL_0021: ldloca.s V_3 + IL_0023: ldstr ""literal"" + IL_0028: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002d: brfalse.s IL_0040 + IL_002f: ldloca.s V_3 + IL_0031: ldloc.0 + IL_0032: box ""int"" + IL_0037: ldc.i4.0 + IL_0038: ldnull + IL_0039: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_003e: br.s IL_0041 + IL_0040: ldc.i4.0 + IL_0041: pop + IL_0042: ldloc.3 + IL_0043: callvirt ""ref int C.this[int, CustomHandler].get"" + IL_0048: dup + IL_0049: ldind.i4 + IL_004a: ldc.i4.2 + IL_004b: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_0050: add + IL_0051: stind.i4 + IL_0052: ret +} +", + }; + } + + [Theory] + [CombinatorialData] + public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_RefReturningMethod(bool useBoolReturns, bool validityParameter) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +int i = 3; +GetC().M(GetInt(1), $""literal{i}"") += GetInt(2); + +static C GetC() +{ + Console.WriteLine(""GetC""); + return new C() { Prop = 2 }; +} + +static int GetInt(int i) +{ + Console.WriteLine(""GetInt"" + i.ToString()); + return 1; +} + +public class C +{ + private int field; + public int Prop { get; set; } + public ref int M(int arg1, [InterpolatedStringHandlerArgument(""arg1"", """")] CustomHandler c) + { + Console.WriteLine(""M""); + Console.WriteLine(c.ToString()); + return ref field; + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, int arg1, C c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + Console.WriteLine(""Handler constructor""); + _builder.AppendLine(""arg1:"" + arg1); + _builder.AppendLine(""C.Prop:"" + c.Prop.ToString()); + " + (validityParameter ? "success = true;" : "") + @" + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: useBoolReturns); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +GetC +GetInt1 +Handler constructor +M +arg1:1 +C.Prop:2 +literal:literal +value:3 +alignment:0 +format: + +GetInt2 +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", getIl()); + + string getIl() => (useBoolReturns, validityParameter) switch + { + (useBoolReturns: false, validityParameter: false) => @" +{ + // Code size 72 (0x48) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldloca.s V_3 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.1 + IL_0015: ldloc.1 + IL_0016: ldloc.2 + IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" + IL_001c: ldloca.s V_3 + IL_001e: ldstr ""literal"" + IL_0023: call ""void CustomHandler.AppendLiteral(string)"" + IL_0028: ldloca.s V_3 + IL_002a: ldloc.0 + IL_002b: box ""int"" + IL_0030: ldc.i4.0 + IL_0031: ldnull + IL_0032: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0037: ldloc.3 + IL_0038: callvirt ""ref int C.M(int, CustomHandler)"" + IL_003d: dup + IL_003e: ldind.i4 + IL_003f: ldc.i4.2 + IL_0040: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_0045: add + IL_0046: stind.i4 + IL_0047: ret +} +", + (useBoolReturns: false, validityParameter: true) => @" +{ + // Code size 82 (0x52) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3, + bool V_4) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.1 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloca.s V_4 + IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" + IL_001c: stloc.3 + IL_001d: ldloc.s V_4 + IL_001f: brfalse.s IL_003f + IL_0021: ldloca.s V_3 + IL_0023: ldstr ""literal"" + IL_0028: call ""void CustomHandler.AppendLiteral(string)"" + IL_002d: ldloca.s V_3 + IL_002f: ldloc.0 + IL_0030: box ""int"" + IL_0035: ldc.i4.0 + IL_0036: ldnull + IL_0037: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_003c: ldc.i4.1 + IL_003d: br.s IL_0040 + IL_003f: ldc.i4.0 + IL_0040: pop + IL_0041: ldloc.3 + IL_0042: callvirt ""ref int C.M(int, CustomHandler)"" + IL_0047: dup + IL_0048: ldind.i4 + IL_0049: ldc.i4.2 + IL_004a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_004f: add + IL_0050: stind.i4 + IL_0051: ret +} +", + (useBoolReturns: true, validityParameter: false) => @" +{ + // Code size 78 (0x4e) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldloca.s V_3 + IL_0013: ldc.i4.7 + IL_0014: ldc.i4.1 + IL_0015: ldloc.1 + IL_0016: ldloc.2 + IL_0017: call ""CustomHandler..ctor(int, int, int, C)"" + IL_001c: ldloca.s V_3 + IL_001e: ldstr ""literal"" + IL_0023: call ""bool CustomHandler.AppendLiteral(string)"" + IL_0028: brfalse.s IL_003b + IL_002a: ldloca.s V_3 + IL_002c: ldloc.0 + IL_002d: box ""int"" + IL_0032: ldc.i4.0 + IL_0033: ldnull + IL_0034: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_0039: br.s IL_003c + IL_003b: ldc.i4.0 + IL_003c: pop + IL_003d: ldloc.3 + IL_003e: callvirt ""ref int C.M(int, CustomHandler)"" + IL_0043: dup + IL_0044: ldind.i4 + IL_0045: ldc.i4.2 + IL_0046: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_004b: add + IL_004c: stind.i4 + IL_004d: ret +} +", + (useBoolReturns: true, validityParameter: true) => @" +{ + // Code size 83 (0x53) + .maxstack 7 + .locals init (int V_0, //i + int V_1, + C V_2, + CustomHandler V_3, + bool V_4) + IL_0000: ldc.i4.3 + IL_0001: stloc.0 + IL_0002: call ""C $.<
$>g__GetC|0_0()"" + IL_0007: stloc.2 + IL_0008: ldloc.2 + IL_0009: ldc.i4.1 + IL_000a: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_000f: stloc.1 + IL_0010: ldloc.1 + IL_0011: ldc.i4.7 + IL_0012: ldc.i4.1 + IL_0013: ldloc.1 + IL_0014: ldloc.2 + IL_0015: ldloca.s V_4 + IL_0017: newobj ""CustomHandler..ctor(int, int, int, C, out bool)"" + IL_001c: stloc.3 + IL_001d: ldloc.s V_4 + IL_001f: brfalse.s IL_0040 + IL_0021: ldloca.s V_3 + IL_0023: ldstr ""literal"" + IL_0028: call ""bool CustomHandler.AppendLiteral(string)"" + IL_002d: brfalse.s IL_0040 + IL_002f: ldloca.s V_3 + IL_0031: ldloc.0 + IL_0032: box ""int"" + IL_0037: ldc.i4.0 + IL_0038: ldnull + IL_0039: call ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_003e: br.s IL_0041 + IL_0040: ldc.i4.0 + IL_0041: pop + IL_0042: ldloc.3 + IL_0043: callvirt ""ref int C.M(int, CustomHandler)"" + IL_0048: dup + IL_0049: ldind.i4 + IL_004a: ldc.i4.2 + IL_004b: call ""int $.<
$>g__GetInt|0_1(int)"" + IL_0050: add + IL_0051: stind.i4 + IL_0052: ret +} +", + }; + } + + [Fact] + public void InterpolatedStringHandlerArgumentsAttribute_CollectionInitializerAdd() + { + var code = @" +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +_ = new C(1) { $""literal"" }; + +public class C : IEnumerable +{ + public int Field; + + public C(int i) + { + Field = i; + } + + public void Add([InterpolatedStringHandlerArgument("""")] CustomHandler c) + { + Console.WriteLine(c.ToString()); + } + + public IEnumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, C c) : this(literalLength, formattedCount) + { + _builder.AppendLine(""c.Field:"" + c.Field.ToString()); + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +c.Field:1 +literal:literal +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 37 (0x25) + .maxstack 5 + .locals init (C V_0, + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: newobj ""C..ctor(int)"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldloca.s V_1 + IL_000a: ldc.i4.7 + IL_000b: ldc.i4.0 + IL_000c: ldloc.0 + IL_000d: call ""CustomHandler..ctor(int, int, C)"" + IL_0012: ldloca.s V_1 + IL_0014: ldstr ""literal"" + IL_0019: call ""void CustomHandler.AppendLiteral(string)"" + IL_001e: ldloc.1 + IL_001f: callvirt ""void C.Add(CustomHandler)"" + IL_0024: ret +} +"); + } + + [Theory] + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_AttributeOnAppendFormatCall(bool useBoolReturns, bool validityParameter) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C.M(1, $""{$""Inner string""}{2}""); + +class C +{ + public static void M(int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler handler) + { + Console.WriteLine(handler.ToString()); + } +} + +public partial class CustomHandler +{ + private int I = 0; + + public CustomHandler(int literalLength, int formattedCount, int i" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + Console.WriteLine(""int constructor""); + I = i; + " + (validityParameter ? "success = true;" : "") + @" + } + + public CustomHandler(int literalLength, int formattedCount, CustomHandler c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + Console.WriteLine(""CustomHandler constructor""); + _builder.AppendLine(""c.I:"" + c.I.ToString()); + " + (validityParameter ? "success = true;" : "") + @" + } + + public " + (useBoolReturns ? "bool" : "void") + @" AppendFormatted([InterpolatedStringHandlerArgument("""")]CustomHandler c) + { + _builder.AppendLine(""CustomHandler AppendFormatted""); + _builder.Append(c.ToString()); + " + (useBoolReturns ? "return true;" : "") + @" + } +} +"; + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: useBoolReturns); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +int constructor +CustomHandler constructor +CustomHandler AppendFormatted +c.I:1 +literal:Inner string +value:2 +alignment:0 +format: +"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", getIl()); + + string getIl() => (useBoolReturns, validityParameter) switch + { + (useBoolReturns: false, validityParameter: false) => @" +{ + // Code size 59 (0x3b) + .maxstack 6 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.2 + IL_0005: ldloc.0 + IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" + IL_000b: dup + IL_000c: stloc.1 + IL_000d: ldloc.1 + IL_000e: ldc.i4.s 12 + IL_0010: ldc.i4.0 + IL_0011: ldloc.1 + IL_0012: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" + IL_0017: dup + IL_0018: ldstr ""Inner string"" + IL_001d: callvirt ""void CustomHandler.AppendLiteral(string)"" + IL_0022: callvirt ""void CustomHandler.AppendFormatted(CustomHandler)"" + IL_0027: dup + IL_0028: ldc.i4.2 + IL_0029: box ""int"" + IL_002e: ldc.i4.0 + IL_002f: ldnull + IL_0030: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0035: call ""void C.M(int, CustomHandler)"" + IL_003a: ret +} +", + (useBoolReturns: false, validityParameter: true) => @" +{ + // Code size 87 (0x57) + .maxstack 6 + .locals init (int V_0, + CustomHandler V_1, + bool V_2, + CustomHandler V_3, + CustomHandler V_4, + bool V_5) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.2 + IL_0005: ldloc.0 + IL_0006: ldloca.s V_2 + IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" + IL_000d: stloc.1 + IL_000e: ldloc.2 + IL_000f: brfalse.s IL_004e + IL_0011: ldloc.1 + IL_0012: stloc.3 + IL_0013: ldloc.3 + IL_0014: ldc.i4.s 12 + IL_0016: ldc.i4.0 + IL_0017: ldloc.3 + IL_0018: ldloca.s V_5 + IL_001a: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" + IL_001f: stloc.s V_4 + IL_0021: ldloc.s V_5 + IL_0023: brfalse.s IL_0034 + IL_0025: ldloc.s V_4 + IL_0027: ldstr ""Inner string"" + IL_002c: callvirt ""void CustomHandler.AppendLiteral(string)"" + IL_0031: ldc.i4.1 + IL_0032: br.s IL_0035 + IL_0034: ldc.i4.0 + IL_0035: pop + IL_0036: ldloc.s V_4 + IL_0038: callvirt ""void CustomHandler.AppendFormatted(CustomHandler)"" + IL_003d: ldloc.1 + IL_003e: ldc.i4.2 + IL_003f: box ""int"" + IL_0044: ldc.i4.0 + IL_0045: ldnull + IL_0046: callvirt ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_004b: ldc.i4.1 + IL_004c: br.s IL_004f + IL_004e: ldc.i4.0 + IL_004f: pop + IL_0050: ldloc.1 + IL_0051: call ""void C.M(int, CustomHandler)"" + IL_0056: ret +} +", + (useBoolReturns: true, validityParameter: false) => @" +{ + // Code size 68 (0x44) + .maxstack 5 + .locals init (int V_0, + CustomHandler V_1, + CustomHandler V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.2 + IL_0005: ldloc.0 + IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" + IL_000b: stloc.1 + IL_000c: ldloc.1 + IL_000d: stloc.2 + IL_000e: ldloc.2 + IL_000f: ldc.i4.s 12 + IL_0011: ldc.i4.0 + IL_0012: ldloc.2 + IL_0013: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" + IL_0018: dup + IL_0019: ldstr ""Inner string"" + IL_001e: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0023: pop + IL_0024: callvirt ""bool CustomHandler.AppendFormatted(CustomHandler)"" + IL_0029: brfalse.s IL_003b + IL_002b: ldloc.1 + IL_002c: ldc.i4.2 + IL_002d: box ""int"" + IL_0032: ldc.i4.0 + IL_0033: ldnull + IL_0034: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_0039: br.s IL_003c + IL_003b: ldc.i4.0 + IL_003c: pop + IL_003d: ldloc.1 + IL_003e: call ""void C.M(int, CustomHandler)"" + IL_0043: ret +} +", + (useBoolReturns: true, validityParameter: true) => @" +{ + // Code size 87 (0x57) + .maxstack 6 + .locals init (int V_0, + CustomHandler V_1, + bool V_2, + CustomHandler V_3, + CustomHandler V_4, + bool V_5) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.2 + IL_0005: ldloc.0 + IL_0006: ldloca.s V_2 + IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" + IL_000d: stloc.1 + IL_000e: ldloc.2 + IL_000f: brfalse.s IL_004e + IL_0011: ldloc.1 + IL_0012: stloc.3 + IL_0013: ldloc.3 + IL_0014: ldc.i4.s 12 + IL_0016: ldc.i4.0 + IL_0017: ldloc.3 + IL_0018: ldloca.s V_5 + IL_001a: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" + IL_001f: stloc.s V_4 + IL_0021: ldloc.s V_5 + IL_0023: brfalse.s IL_0033 + IL_0025: ldloc.s V_4 + IL_0027: ldstr ""Inner string"" + IL_002c: callvirt ""bool CustomHandler.AppendLiteral(string)"" + IL_0031: br.s IL_0034 + IL_0033: ldc.i4.0 + IL_0034: pop + IL_0035: ldloc.s V_4 + IL_0037: callvirt ""bool CustomHandler.AppendFormatted(CustomHandler)"" + IL_003c: brfalse.s IL_004e + IL_003e: ldloc.1 + IL_003f: ldc.i4.2 + IL_0040: box ""int"" + IL_0045: ldc.i4.0 + IL_0046: ldnull + IL_0047: callvirt ""bool CustomHandler.AppendFormatted(object, int, string)"" + IL_004c: br.s IL_004f + IL_004e: ldc.i4.0 + IL_004f: pop + IL_0050: ldloc.1 + IL_0051: call ""void C.M(int, CustomHandler)"" + IL_0056: ret +} +", + }; + } + + [Fact] + public void DiscardsUsedAsParameters() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +C.M(out _, $""literal""); + +public class C +{ + public static void M(out int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler c) + { + i = 0; + Console.WriteLine(c.ToString()); + } +} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, out int i) : this(literalLength, formattedCount) + { + i = 1; + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: @"literal:literal"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 31 (0x1f) + .maxstack 4 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.0 + IL_0004: ldloca.s V_0 + IL_0006: newobj ""CustomHandler..ctor(int, int, out int)"" + IL_000b: stloc.1 + IL_000c: ldloca.s V_1 + IL_000e: ldstr ""literal"" + IL_0013: call ""void CustomHandler.AppendLiteral(string)"" + IL_0018: ldloc.1 + IL_0019: call ""void C.M(out int, CustomHandler)"" + IL_001e: ret +} +"); + } + + [Fact] + public void DiscardsUsedAsParameters_DefinedInVB() + { + var vb = @" +Imports System +Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices +Public Class C + Public Shared Sub M( ByRef i As Integer, c As CustomHandler) + Console.WriteLine(i) + End Sub +End Class + + +Public Structure CustomHandler + Public Sub New(literalLength As Integer, formattedCount As Integer, ByRef i As Integer) + i = 1 + End Sub +End Structure +"; + + var vbComp = CreateVisualBasicCompilation(new[] { vb, InterpolatedStringHandlerAttributesVB }); + vbComp.VerifyDiagnostics(); + + var code = @"C.M(out _, $"""");"; + + var comp = CreateCompilation(code, new[] { vbComp.EmitToImageReference() }); + var verifier = CompileAndVerify(comp, expectedOutput: @"1"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 17 (0x11) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.0 + IL_0004: ldloca.s V_0 + IL_0006: newobj ""CustomHandler..ctor(int, int, out int)"" + IL_000b: call ""void C.M(out int, CustomHandler)"" + IL_0010: ret +} +"); + } + + [Fact] + public void DisallowedInExpressionTrees() + { + var code = @" +using System; +using System.Linq.Expressions; + +Expression> expr = () => $""""; +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); + + var comp = CreateCompilation(new[] { code, handler }); + comp.VerifyDiagnostics( + // (5,46): error CS8952: An expression tree may not contain an interpolated string handler conversion. + // Expression> expr = () => $""; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsInterpolatedStringHandlerConversion, @"$""""").WithLocation(5, 46) + ); + } + + [Theory] + [CombinatorialData] + public void CustomHandlerUsedAsArgumentToCustomHandler(bool useBoolReturns, bool validityParameter) + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +C.M(1, $"""", $""""); + +public class C +{ + public static void M(int i, [InterpolatedStringHandlerArgument(""i"")] CustomHandler c1, [InterpolatedStringHandlerArgument(""c1"")] CustomHandler c2) => Console.WriteLine(c2.ToString()); +} + +public partial class CustomHandler +{ + private int i; + public CustomHandler(int literalLength, int formattedCount, int i" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""i:"" + i.ToString()); + this.i = i; + " + (validityParameter ? "success = true;" : "") + @" + } + public CustomHandler(int literalLength, int formattedCount, CustomHandler c" + (validityParameter ? ", out bool success" : "") + @") : this(literalLength, formattedCount) + { + _builder.AppendLine(""c.i:"" + c.i.ToString()); + " + (validityParameter ? "success = true;" : "") + @" + } +}"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns); + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); + var verifier = CompileAndVerify(comp, expectedOutput: "c.i:1"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", getIl()); + + string getIl() => (useBoolReturns, validityParameter) switch + { + (useBoolReturns: false, validityParameter: false) => @" +{ + // Code size 27 (0x1b) + .maxstack 5 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" + IL_000b: stloc.1 + IL_000c: ldloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.1 + IL_0010: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" + IL_0015: call ""void C.M(int, CustomHandler, CustomHandler)"" + IL_001a: ret +} +", + (useBoolReturns: false, validityParameter: true) => @" +{ + // Code size 31 (0x1f) + .maxstack 6 + .locals init (int V_0, + CustomHandler V_1, + bool V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: ldloca.s V_2 + IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" + IL_000d: stloc.1 + IL_000e: ldloc.1 + IL_000f: ldc.i4.0 + IL_0010: ldc.i4.0 + IL_0011: ldloc.1 + IL_0012: ldloca.s V_2 + IL_0014: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" + IL_0019: call ""void C.M(int, CustomHandler, CustomHandler)"" + IL_001e: ret +} +", + (useBoolReturns: true, validityParameter: false) => @" +{ + // Code size 27 (0x1b) + .maxstack 5 + .locals init (int V_0, + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: newobj ""CustomHandler..ctor(int, int, int)"" + IL_000b: stloc.1 + IL_000c: ldloc.1 + IL_000d: ldc.i4.0 + IL_000e: ldc.i4.0 + IL_000f: ldloc.1 + IL_0010: newobj ""CustomHandler..ctor(int, int, CustomHandler)"" + IL_0015: call ""void C.M(int, CustomHandler, CustomHandler)"" + IL_001a: ret +} +", + (useBoolReturns: true, validityParameter: true) => @" +{ + // Code size 31 (0x1f) + .maxstack 6 + .locals init (int V_0, + CustomHandler V_1, + bool V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: ldloca.s V_2 + IL_0008: newobj ""CustomHandler..ctor(int, int, int, out bool)"" + IL_000d: stloc.1 + IL_000e: ldloc.1 + IL_000f: ldc.i4.0 + IL_0010: ldc.i4.0 + IL_0011: ldloc.1 + IL_0012: ldloca.s V_2 + IL_0014: newobj ""CustomHandler..ctor(int, int, CustomHandler, out bool)"" + IL_0019: call ""void C.M(int, CustomHandler, CustomHandler)"" + IL_001e: ret +} +", + }; + } + + [Theory] + [CombinatorialData] + public void DefiniteAssignment_01(bool useBoolReturns, bool trailingOutParameter) + { + var code = @" +int i; +string s; + +CustomHandler c = $""{i = 1}{M(out var o)}{s = o.ToString()}""; +_ = i.ToString(); +_ = o.ToString(); +_ = s.ToString(); + +string M(out object o) +{ + o = null; + return null; +} +"; + + var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns, includeTrailingOutConstructorParameter: trailingOutParameter); + var comp = CreateCompilation(new[] { code, customHandler }); + + if (trailingOutParameter) + { + comp.VerifyDiagnostics( + // (6,5): error CS0165: Use of unassigned local variable 'i' + // _ = i.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "i").WithArguments("i").WithLocation(6, 5), + // (7,5): error CS0165: Use of unassigned local variable 'o' + // _ = o.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "o").WithArguments("o").WithLocation(7, 5), + // (8,5): error CS0165: Use of unassigned local variable 's' + // _ = s.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(8, 5) + ); + } + else if (useBoolReturns) + { + comp.VerifyDiagnostics( + // (7,5): error CS0165: Use of unassigned local variable 'o' + // _ = o.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "o").WithArguments("o").WithLocation(7, 5), + // (8,5): error CS0165: Use of unassigned local variable 's' + // _ = s.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(8, 5) + ); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Theory] + [CombinatorialData] + public void DefiniteAssignment_02(bool useBoolReturns, bool trailingOutParameter) + { + var code = @" +int i; + +CustomHandler c = $""{i = 1}""; +_ = i.ToString(); +"; + + var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns, includeTrailingOutConstructorParameter: trailingOutParameter); + var comp = CreateCompilation(new[] { code, customHandler }); + + if (trailingOutParameter) + { + comp.VerifyDiagnostics( + // (5,5): error CS0165: Use of unassigned local variable 'i' + // _ = i.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "i").WithArguments("i").WithLocation(5, 5) + ); + } + else + { + comp.VerifyDiagnostics(); + } + } + + [Fact] + public void DynamicConstruction_01() + { + var code = @" +using System.Runtime.CompilerServices; +dynamic d = 1; +M(d, $""""); + +void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, dynamic d) : this() {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + + var comp = CreateCompilation(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute }); + comp.VerifyDiagnostics( + // (4,6): error CS8953: An interpolated string handler construction cannot use dynamic. Manually construct an instance of 'CustomHandler'. + // M(d, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, @"$""""").WithArguments("CustomHandler").WithLocation(4, 6) + ); + } + + [Fact] + public void DynamicConstruction_02() + { + var code = @" +using System.Runtime.CompilerServices; +int i = 1; +M(i, $""""); + +void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, dynamic d) : this() {} +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false); + + var comp = CreateCompilation(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute }); + comp.VerifyDiagnostics( + // (4,6): error CS8953: An interpolated string handler construction cannot use dynamic. Manually construct an instance of 'CustomHandler'. + // M(d, $""); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, @"$""""").WithArguments("CustomHandler").WithLocation(4, 6) + ); + } + + [Fact] + public void DynamicConstruction_03() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +int i = 1; +M(i, $""""); + +void M(int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler c) {} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, dynamic d) : this(literalLength, formattedCount) + { + Console.WriteLine(""d:"" + d.ToString()); + } +} +"; + + var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false, includeOneTimeHelpers: false); + + var comp = CreateCompilation(new[] { code, handler, InterpolatedStringHandlerArgumentAttribute, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: "d:1"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 22 (0x16) + .maxstack 4 + .locals init (int V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: ldc.i4.0 + IL_0004: ldc.i4.0 + IL_0005: ldloc.0 + IL_0006: box ""int"" + IL_000b: newobj ""CustomHandler..ctor(int, int, dynamic)"" + IL_0010: call ""void $.<
$>g__M|0_0(int, CustomHandler)"" + IL_0015: ret +} +"); + } + + [Fact] + public void DynamicConstruction_04() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +M($""""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(dynamic literalLength, int formattedCount) + { + Console.WriteLine(""ctor""); + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: "ctor"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldc.i4.0 + IL_0001: box ""int"" + IL_0006: ldc.i4.0 + IL_0007: newobj ""CustomHandler..ctor(dynamic, int)"" + IL_000c: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_0011: ret +} +"); + } + + [Fact] + public void DynamicConstruction_05() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +M($""""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) + { + Console.WriteLine(""ctor""); + } + + public CustomHandler(dynamic literalLength, int formattedCount) + { + throw null; + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: "ctor"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 13 (0xd) + .maxstack 2 + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.0 + IL_0002: newobj ""CustomHandler..ctor(int, int)"" + IL_0007: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_000c: ret +} +"); + } + + [Fact] + public void DynamicConstruction_06() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +M($""Literal""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) + { + } + + public void AppendLiteral(dynamic d) + { + Console.WriteLine(""AppendLiteral""); + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: "AppendLiteral"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 28 (0x1c) + .maxstack 3 + .locals init (CustomHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.7 + IL_0003: ldc.i4.0 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldstr ""Literal"" + IL_0010: call ""void CustomHandler.AppendLiteral(dynamic)"" + IL_0015: ldloc.0 + IL_0016: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_001b: ret +} +"); + } + + [Fact] + public void DynamicConstruction_07() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +M($""{1}""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) + { + } + + public void AppendFormatted(dynamic d) + { + Console.WriteLine(""AppendFormatted""); + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: "AppendFormatted"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 29 (0x1d) + .maxstack 3 + .locals init (CustomHandler V_0) + IL_0000: ldloca.s V_0 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_0 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: call ""void CustomHandler.AppendFormatted(dynamic)"" + IL_0016: ldloc.0 + IL_0017: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_001c: ret +} +"); + } + + [Fact] + public void DynamicConstruction_08() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +dynamic d = 1; +M($""literal{d}""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) + { + } + + public void AppendLiteral(dynamic d) + { + Console.WriteLine(""AppendLiteral""); + } + + public void AppendFormatted(dynamic d) + { + Console.WriteLine(""AppendFormatted""); + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +AppendLiteral +AppendFormatted"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 128 (0x80) + .maxstack 9 + .locals init (object V_0, //d + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: box ""int"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_1 + IL_0009: ldc.i4.7 + IL_000a: ldc.i4.1 + IL_000b: call ""CustomHandler..ctor(int, int)"" + IL_0010: ldloca.s V_1 + IL_0012: ldstr ""literal"" + IL_0017: call ""void CustomHandler.AppendLiteral(dynamic)"" + IL_001c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> $.<>o__0.<>p__0"" + IL_0021: brtrue.s IL_0062 + IL_0023: ldc.i4 0x100 + IL_0028: ldstr ""AppendFormatted"" + IL_002d: ldnull + IL_002e: ldtoken ""$"" + IL_0033: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0038: ldc.i4.2 + IL_0039: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"" + IL_003e: dup + IL_003f: ldc.i4.0 + IL_0040: ldc.i4.s 9 + IL_0042: ldnull + IL_0043: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" + IL_0048: stelem.ref + IL_0049: dup + IL_004a: ldc.i4.1 + IL_004b: ldc.i4.0 + IL_004c: ldnull + IL_004d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" + IL_0052: stelem.ref + IL_0053: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable, System.Type, System.Collections.Generic.IEnumerable)"" + IL_0058: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_005d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> $.<>o__0.<>p__0"" + IL_0062: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> $.<>o__0.<>p__0"" + IL_0067: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" + IL_006c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> $.<>o__0.<>p__0"" + IL_0071: ldloca.s V_1 + IL_0073: ldloc.0 + IL_0074: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref CustomHandler, dynamic)"" + IL_0079: ldloc.1 + IL_007a: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_007f: ret +} +"); + } + + [Fact] + public void DynamicConstruction_09() + { + var code = @" +using System; +using System.Runtime.CompilerServices; +dynamic d = 1; +M($""literal{d}""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount) + { + } + + public bool AppendLiteral(dynamic d) + { + Console.WriteLine(""AppendLiteral""); + return true; + } + + public bool AppendFormatted(dynamic d) + { + Console.WriteLine(""AppendFormatted""); + return true; + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.Mscorlib45AndCSharp); + var verifier = CompileAndVerify(comp, expectedOutput: @" +AppendLiteral +AppendFormatted"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", @" +{ + // Code size 196 (0xc4) + .maxstack 11 + .locals init (object V_0, //d + CustomHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: box ""int"" + IL_0006: stloc.0 + IL_0007: ldloca.s V_1 + IL_0009: ldc.i4.7 + IL_000a: ldc.i4.1 + IL_000b: call ""CustomHandler..ctor(int, int)"" + IL_0010: ldloca.s V_1 + IL_0012: ldstr ""literal"" + IL_0017: call ""bool CustomHandler.AppendLiteral(dynamic)"" + IL_001c: brfalse IL_00bb + IL_0021: ldsfld ""System.Runtime.CompilerServices.CallSite> $.<>o__0.<>p__1"" + IL_0026: brtrue.s IL_004c + IL_0028: ldc.i4.0 + IL_0029: ldtoken ""bool"" + IL_002e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0033: ldtoken ""$"" + IL_0038: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_003d: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)"" + IL_0042: call ""System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0047: stsfld ""System.Runtime.CompilerServices.CallSite> $.<>o__0.<>p__1"" + IL_004c: ldsfld ""System.Runtime.CompilerServices.CallSite> $.<>o__0.<>p__1"" + IL_0051: ldfld ""System.Func System.Runtime.CompilerServices.CallSite>.Target"" + IL_0056: ldsfld ""System.Runtime.CompilerServices.CallSite> $.<>o__0.<>p__1"" + IL_005b: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> $.<>o__0.<>p__0"" + IL_0060: brtrue.s IL_009d + IL_0062: ldc.i4.0 + IL_0063: ldstr ""AppendFormatted"" + IL_0068: ldnull + IL_0069: ldtoken ""$"" + IL_006e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0073: ldc.i4.2 + IL_0074: newarr ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo"" + IL_0079: dup + IL_007a: ldc.i4.0 + IL_007b: ldc.i4.s 9 + IL_007d: ldnull + IL_007e: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" + IL_0083: stelem.ref + IL_0084: dup + IL_0085: ldc.i4.1 + IL_0086: ldc.i4.0 + IL_0087: ldnull + IL_0088: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" + IL_008d: stelem.ref + IL_008e: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable, System.Type, System.Collections.Generic.IEnumerable)"" + IL_0093: call ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> System.Runtime.CompilerServices.CallSite<<>F{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0098: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> $.<>o__0.<>p__0"" + IL_009d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> $.<>o__0.<>p__0"" + IL_00a2: ldfld ""<>F{00000002} System.Runtime.CompilerServices.CallSite<<>F{00000002}>.Target"" + IL_00a7: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> $.<>o__0.<>p__0"" + IL_00ac: ldloca.s V_1 + IL_00ae: ldloc.0 + IL_00af: callvirt ""dynamic <>F{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref CustomHandler, dynamic)"" + IL_00b4: callvirt ""bool System.Func.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)"" + IL_00b9: br.s IL_00bc + IL_00bb: ldc.i4.0 + IL_00bc: pop + IL_00bd: ldloc.1 + IL_00be: call ""void $.<
$>g__M|0_0(CustomHandler)"" + IL_00c3: ret +} +"); + } + + [Fact] + public void RefEscape_01() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + Span s; + + public CustomHandler(int literalLength, int formattedCount) : this() {} + + public void AppendFormatted(Span s) => this.s = s; + + public static CustomHandler M() + { + Span s = stackalloc char[10]; + return $""{s}""; + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (17,19): error CS8352: Cannot use local 's' in this context because it may expose referenced variables outside of their declaration scope + // return $"{s}"; + Diagnostic(ErrorCode.ERR_EscapeLocal, "s").WithArguments("s").WithLocation(17, 19) + ); + } + + [Fact] + public void RefEscape_02() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + Span s; + + public CustomHandler(int literalLength, int formattedCount) : this() {} + + public void AppendFormatted(Span s) => this.s = s; + + public static ref CustomHandler M() + { + Span s = stackalloc char[10]; + return $""{s}""; + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (17,9): error CS8150: By-value returns may only be used in methods that return by value + // return $"{s}"; + Diagnostic(ErrorCode.ERR_MustHaveRefReturn, "return").WithLocation(17, 9) + ); + } + + [Fact] + public void RefEscape_03() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + Span s; + + public CustomHandler(int literalLength, int formattedCount) : this() {} + + public void AppendFormatted(Span s) => this.s = s; + + public static ref CustomHandler M() + { + Span s = stackalloc char[10]; + return ref $""{s}""; + } +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (17,20): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // return ref $"{s}"; + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, @"$""{s}""").WithLocation(17, 20) + ); + } + + [Fact] + public void RefEscape_04() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + S1 s1; + + public CustomHandler(int literalLength, int formattedCount, ref S1 s1) : this() { this.s1 = s1; } + + public void AppendFormatted(Span s) => this.s1.s = s; + + public static void M(ref S1 s1) + { + Span s = stackalloc char[10]; + M2(ref s1, $""{s}""); + } + + public static void M2(ref S1 s1, [InterpolatedStringHandlerArgument(""s1"")] ref CustomHandler handler) {} +} + +public ref struct S1 +{ + public Span s; +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (17,9): error CS8350: This combination of arguments to 'CustomHandler.M2(ref S1, ref CustomHandler)' is disallowed because it may expose variables referenced by parameter 'handler' outside of their declaration scope + // M2(ref s1, $"{s}"); + Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, $""{s}"")").WithArguments("CustomHandler.M2(ref S1, ref CustomHandler)", "handler").WithLocation(17, 9), + // (17,23): error CS8352: Cannot use local 's' in this context because it may expose referenced variables outside of their declaration scope + // M2(ref s1, $"{s}"); + Diagnostic(ErrorCode.ERR_EscapeLocal, "s").WithArguments("s").WithLocation(17, 23) + ); + } + + [Fact] + public void RefEscape_05() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + Span s; + + public CustomHandler(int literalLength, int formattedCount, ref Span s) : this() { this.s = s; } + + public void AppendFormatted(S1 s1) => s1.s = this.s; + + public static void M(ref S1 s1) + { + Span s = stackalloc char[10]; + M2(ref s, $""{s1}""); + } + + public static void M2(ref Span s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler handler) {} +} + +public ref struct S1 +{ + public Span s; +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics(); + } + + [Fact] + public void RefEscape_06() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +Span s = stackalloc char[5]; +Span s2 = stackalloc char[10]; +s.TryWrite($""{s2}""); + +public static class MemoryExtensions +{ + public static bool TryWrite(this Span span, [InterpolatedStringHandlerArgument(""span"")] CustomHandler builder) => true; +} + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, Span s) : this() { } + + public bool AppendFormatted(Span s) => true; +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics(); + } + + [Fact] + public void RefEscape_07() + { + var code = @" +using System; +using System.Runtime.CompilerServices; + +Span s = stackalloc char[5]; +Span s2 = stackalloc char[10]; +s.TryWrite($""{s2}""); + +public static class MemoryExtensions +{ + public static bool TryWrite(this Span span, [InterpolatedStringHandlerArgument(""span"")] ref CustomHandler builder) => true; +} + +[InterpolatedStringHandler] +public ref struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, Span s) : this() { } + + public bool AppendFormatted(Span s) => true; +} +"; + + var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerAttribute, InterpolatedStringHandlerArgumentAttribute }, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void BracesAreEscaped_01() + { + var code = @" +int i = 1; +System.Console.WriteLine($""{{ {i} }}"");"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +{ +value:1 + }"); + + verifier.VerifyIL("", @" +{ + // Code size 56 (0x38) + .maxstack 3 + .locals init (int V_0, //i + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_1) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.4 + IL_0005: ldc.i4.1 + IL_0006: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldstr ""{ "" + IL_0012: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_0017: ldloca.s V_1 + IL_0019: ldloc.0 + IL_001a: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_001f: ldloca.s V_1 + IL_0021: ldstr "" }"" + IL_0026: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)"" + IL_002b: ldloca.s V_1 + IL_002d: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0032: call ""void System.Console.WriteLine(string)"" + IL_0037: ret +} +"); + } + + [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + public void BracesAreEscaped_02() + { + var code = @" +int i = 1; +CustomHandler c = $""{{ {i} }}""; +System.Console.WriteLine(c.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +literal:{ +value:1 +alignment:0 +format: +literal: }"); + + verifier.VerifyIL("", @" +{ + // Code size 71 (0x47) + .maxstack 4 + .locals init (int V_0, //i + CustomHandler V_1, //c + CustomHandler V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_2 + IL_0004: ldc.i4.4 + IL_0005: ldc.i4.1 + IL_0006: call ""CustomHandler..ctor(int, int)"" + IL_000b: ldloca.s V_2 + IL_000d: ldstr ""{ "" + IL_0012: call ""void CustomHandler.AppendLiteral(string)"" + IL_0017: ldloca.s V_2 + IL_0019: ldloc.0 + IL_001a: box ""int"" + IL_001f: ldc.i4.0 + IL_0020: ldnull + IL_0021: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0026: ldloca.s V_2 + IL_0028: ldstr "" }"" + IL_002d: call ""void CustomHandler.AppendLiteral(string)"" + IL_0032: ldloc.2 + IL_0033: stloc.1 + IL_0034: ldloca.s V_1 + IL_0036: constrained. ""CustomHandler"" + IL_003c: callvirt ""string object.ToString()"" + IL_0041: call ""void System.Console.WriteLine(string)"" + IL_0046: ret +} +"); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs index 9daf64427a68e..135624fcdfa2d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LocalFunctionTests.cs @@ -1674,7 +1674,7 @@ void M() comp.DeclarationDiagnostics.Verify(); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/16652")] + [Fact] public void FetchLocalFunctionSymbolThroughLocal() { var tree = SyntaxFactory.ParseSyntaxTree(@" @@ -1692,30 +1692,28 @@ void Local<[A, B, CLSCompliant, D]T>() }"); var comp = CreateCompilation(tree); comp.DeclarationDiagnostics.Verify(); + comp.VerifyDiagnostics( - // (7,20): error CS8204: Attributes are not allowed on local function parameters or type parameters - // void Local<[A, B, CLSCompliant, D]T>() { } - Diagnostic(ErrorCode.ERR_AttributesInLocalFuncDecl, "[A, B, CLSCompliant, D]").WithLocation(7, 20), // (7,21): error CS0246: The type or namespace name 'AAttribute' could not be found (are you missing a using directive or an assembly reference?) - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "A").WithArguments("AAttribute").WithLocation(7, 21), // (7,21): error CS0246: The type or namespace name 'A' could not be found (are you missing a using directive or an assembly reference?) - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "A").WithArguments("A").WithLocation(7, 21), // (7,24): error CS0246: The type or namespace name 'BAttribute' could not be found (are you missing a using directive or an assembly reference?) - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "B").WithArguments("BAttribute").WithLocation(7, 24), // (7,24): error CS0246: The type or namespace name 'B' could not be found (are you missing a using directive or an assembly reference?) - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "B").WithArguments("B").WithLocation(7, 24), // (7,41): error CS0246: The type or namespace name 'DAttribute' could not be found (are you missing a using directive or an assembly reference?) - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "D").WithArguments("DAttribute").WithLocation(7, 41), // (7,41): error CS0246: The type or namespace name 'D' could not be found (are you missing a using directive or an assembly reference?) - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "D").WithArguments("D").WithLocation(7, 41), // (7,27): error CS7036: There is no argument given that corresponds to the required formal parameter 'isCompliant' of 'CLSCompliantAttribute.CLSCompliantAttribute(bool)' - // void Local<[A, B, CLSCompliant, D]T>() { } + // void Local<[A, B, CLSCompliant, D]T>() Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "CLSCompliant").WithArguments("isCompliant", "System.CLSCompliantAttribute.CLSCompliantAttribute(bool)").WithLocation(7, 27)); var model = comp.GetSemanticModel(tree); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index fa06937b150cd..d083ea601f15a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -63523,6 +63523,182 @@ void Test2(string x2, string? y2) ); } + [Theory] + [CombinatorialData] + public void StringInterpolation_02(bool useBoolReturns, bool validityParameter) + { + CSharpCompilation c = CreateCompilation(new[] { @" +#nullable enable + +string? s = null; +M($""{s = """"}{s.ToString()}"", s); + +void M(CustomHandler c, string s) {} +", GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns, includeTrailingOutConstructorParameter: validityParameter) }, parseOptions: TestOptions.RegularPreview); + + if (validityParameter) + { + c.VerifyDiagnostics( + // (5,30): warning CS8604: Possible null reference argument for parameter 's' in 'void M(CustomHandler c, string s)'. + // M($"{s = ""}{s.ToString()}", s); + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("s", "void M(CustomHandler c, string s)").WithLocation(5, 30) + ); + } + else + { + c.VerifyDiagnostics(); + } + } + + [Theory] + [CombinatorialData] + public void StringInterpolation_03(bool useBoolReturns, bool validityParameter) + { + CSharpCompilation c = CreateCompilation(new[] { @" +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +#nullable enable + +string? s = """"; +M( +#line 1000 + ref s, +#line 2000 + $"""", +#line 3000 + s.ToString()); + +void M(ref T t1, [InterpolatedStringHandlerArgument(""t1"")] CustomHandler c, T t2) {} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, [MaybeNull] ref T t " + (validityParameter ? ", out bool success" : "") + @") : this() + { + " + (validityParameter ? "success = true;" : "") + @" + } +} +", GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns, includeTrailingOutConstructorParameter: validityParameter), InterpolatedStringHandlerArgumentAttribute, MaybeNullAttributeDefinition }, + parseOptions: TestOptions.RegularPreview); + + // https://github.com/dotnet/roslyn/issues/54583 + // Should be a warning on `s.ToString()` + c.VerifyDiagnostics( + // (1000,9): warning CS8601: Possible null reference assignment. + // ref s, + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s").WithLocation(1000, 9) + ); + } + + [Theory] + [CombinatorialData] + public void StringInterpolation_04(bool useBoolReturns, bool validityParameter) + { + CSharpCompilation c = CreateCompilation(new[] { @" +using System.Runtime.CompilerServices; +#nullable enable + +string? s = null; +M(s, $""""); + +void M(string? s1, [InterpolatedStringHandlerArgument(""s1"")] CustomHandler c) {} + +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount, string s" + (validityParameter ? ", out bool success" : "") + @") : this() + { + " + (validityParameter ? "success = true;" : "") + @" + } +} +", GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns, includeTrailingOutConstructorParameter: validityParameter), InterpolatedStringHandlerArgumentAttribute }, + parseOptions: TestOptions.RegularPreview); + + // https://github.com/dotnet/roslyn/issues/54583 + // Should be a warning for the constructor parameter + c.VerifyDiagnostics( + ); + } + + [Theory] + [CombinatorialData] + public void StringInterpolation_05(bool useBoolReturns, bool validityParameter) + { + CSharpCompilation c = CreateCompilation(new[] { @" +using System.Runtime.CompilerServices; +#nullable enable + +string? s = null; +M($""{s}""); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount" + (validityParameter ? ", out bool success" : "") + @") : this() + { + " + (validityParameter ? "success = true;" : "") + @" + } + + public " + (useBoolReturns ? "bool" : "void") + @" AppendFormatted(object o) + { + return " + (useBoolReturns ? "true" : "") + @"; + } +} +", InterpolatedStringHandlerAttribute }, + parseOptions: TestOptions.RegularPreview); + + c.VerifyDiagnostics( + // (6,6): warning CS8604: Possible null reference argument for parameter 'o' in 'void CustomHandler.AppendFormatted(object o)'. + // M($"{s}"); + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("o", (useBoolReturns ? "bool" : "void") + " CustomHandler.AppendFormatted(object o)").WithLocation(6, 6) + ); + } + + [Theory] + [CombinatorialData] + public void StringInterpolation_06(bool useBoolReturns, bool validityParameter) + { + CSharpCompilation c = CreateCompilation(new[] { @" +using System.Runtime.CompilerServices; +#nullable enable + +string? s = """"; +M($""{s = null}{s = """"}""); +_ = s.ToString(); + +void M(CustomHandler c) {} + +[InterpolatedStringHandler] +public partial struct CustomHandler +{ + public CustomHandler(int literalLength, int formattedCount" + (validityParameter ? ", out bool success" : "") + @") : this() + { + " + (validityParameter ? "success = true;" : "") + @" + } + + public " + (useBoolReturns ? "bool" : "void") + @" AppendFormatted(object? o) + { + return " + (useBoolReturns ? "true" : "") + @"; + } +} +", InterpolatedStringHandlerAttribute }, + parseOptions: TestOptions.RegularPreview); + + if (useBoolReturns) + { + c.VerifyDiagnostics( + // (7,5): warning CS8602: Dereference of a possibly null reference. + // _ = s.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(7, 5) + ); + } + else + { + c.VerifyDiagnostics( + ); + } + } + [Fact] public void DelegateCreation_01() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs index 349ee803afc90..61a4ebe97e08e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RecordTests.cs @@ -11,8 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.FlowAnalysis; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; @@ -10824,12 +10822,18 @@ record C(object P) public object get_P() => null; public object set_Q() => null; }"; - var comp = CreateCompilation(source); + var comp = CreateCompilation(RuntimeUtilities.IsCoreClrRuntime ? source : new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.StandardLatest); comp.VerifyDiagnostics( // (9,17): error CS0082: Type 'C' already reserves a member called 'get_P' with the same parameter types // record C(object P) Diagnostic(ErrorCode.ERR_MemberReserved, "P").WithArguments("get_P", "C").WithLocation(9, 17)); + Assert.Equal(RuntimeUtilities.IsCoreClrRuntime, comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + + var expectedClone = comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses + ? "B B." + WellKnownMemberNames.CloneMethodName + "()" + : "A B." + WellKnownMemberNames.CloneMethodName + "()"; + var expectedMembers = new[] { "B..ctor(System.Object P, System.Object Q)", @@ -10851,7 +10855,7 @@ record C(object P) "System.Boolean B.Equals(System.Object? obj)", "System.Boolean B.Equals(A? other)", "System.Boolean B.Equals(B? other)", - "A B." + WellKnownMemberNames.CloneMethodName + "()", + expectedClone, "B..ctor(B original)", "void B.Deconstruct(out System.Object P, out System.Object Q)" }; @@ -12696,7 +12700,7 @@ public void CopyCtor(bool useCompilationReference) @"public record B(object N1, object N2) { }"; - var compA = CreateCompilation(sourceA); + var compA = CreateCompilation(RuntimeUtilities.IsCoreClrRuntime ? sourceA : new[] { sourceA, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.StandardLatest); var verifierA = CompileAndVerify(compA, verify: ExecutionConditionUtil.IsCoreClr ? Verification.Skipped : Verification.Fails).VerifyDiagnostics(); verifierA.VerifyIL("B..ctor(B)", @" @@ -12735,7 +12739,7 @@ static void Main() System.Console.Write((c3.P1, c3.P2, c3.N1, c3.N2)); } }"; - var compB = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe); + var compB = CreateCompilation(RuntimeUtilities.IsCoreClrRuntime ? sourceB : new[] { sourceB, IsExternalInitTypeDefinition }, references: new[] { refA }, parseOptions: TestOptions.Regular9, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.StandardLatest); var verifierB = CompileAndVerify(compB, expectedOutput: "(1, 2, 3, 4) (1, 2, 3, 4) (10, 2, 30, 4)", verify: ExecutionConditionUtil.IsCoreClr ? Verification.Skipped : Verification.Fails).VerifyDiagnostics(); // call base copy constructor B..ctor(B) @@ -12756,6 +12760,15 @@ .maxstack 2 IL_001a: stfld ""object C.k__BackingField"" IL_001f: ret }"); + + verifierA.VerifyIL($"B.{WellKnownMemberNames.CloneMethodName}()", @" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: newobj ""B..ctor(B)"" + IL_0006: ret +}"); } [Fact, WorkItem(44902, "https://github.com/dotnet/roslyn/issues/44902")] @@ -15901,7 +15914,7 @@ public void Overrides_02() record B(int X, int Y) : A { }"; - var comp = CreateCompilation(source); + var comp = CreateCompilation(RuntimeUtilities.IsCoreClrRuntime ? source : new[] { source, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.StandardLatest); comp.VerifyDiagnostics( // (3,35): error CS0111: Type 'A' already defines a member called 'Equals' with the same parameter types // public abstract override bool Equals(object other); @@ -15911,6 +15924,11 @@ record B(int X, int Y) : A Diagnostic(ErrorCode.ERR_UnimplementedAbstractMethod, "B").WithArguments("B", "A.Equals(object)").WithLocation(7, 8) ); + Assert.Equal(RuntimeUtilities.IsCoreClrRuntime, comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + string expectedClone = comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses + ? "B B." + WellKnownMemberNames.CloneMethodName + "()" + : "A B." + WellKnownMemberNames.CloneMethodName + "()"; + var actualMembers = comp.GetMember("B").GetMembers().ToTestDisplayStrings(); var expectedMembers = new[] { @@ -15933,7 +15951,7 @@ record B(int X, int Y) : A "System.Boolean B.Equals(System.Object? obj)", "System.Boolean B.Equals(A? other)", "System.Boolean B.Equals(B? other)", - "A B." + WellKnownMemberNames.CloneMethodName + "()", + expectedClone, "B..ctor(B original)", "void B.Deconstruct(out System.Int32 X, out System.Int32 Y)" }; @@ -21080,6 +21098,104 @@ .locals init (C V_0, //c }"); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void WithExprReference_WithCovariantReturns(bool emitRef) + { + var src = @" +public record C +{ + public int X { get; init; } +} +public record D(int Y) : C;"; + var comp = CreateCompilation(RuntimeUtilities.IsCoreClrRuntime ? src : new[] { src, IsExternalInitTypeDefinition }, targetFramework: TargetFramework.StandardLatest); + comp.VerifyDiagnostics(); + + var src2 = @" +using System; +class E +{ + public static void Main() + { + var c = new C() { X = 1 }; + var c2 = CHelper(c); + Console.WriteLine(c.X); + Console.WriteLine(c2.X); + + var d = new D(2) { X = 1 }; + var d2 = DHelper(d); + Console.WriteLine(d.X + "" "" + d.Y); + Console.WriteLine(d2.X + "" "" + d2.Y); + } + + private static C CHelper(C c) + { + return c with { X = 2 }; + } + + private static D DHelper(D d) + { + return d with { X = 2, Y = 3 }; + } +}"; + var verifier = CompileAndVerify(RuntimeUtilities.IsCoreClrRuntime ? src2 : new[] { src2, IsExternalInitTypeDefinition }, + references: new[] { emitRef ? comp.EmitToImageReference() : comp.ToMetadataReference() }, + expectedOutput: @" +1 +2 +1 2 +2 3", targetFramework: TargetFramework.StandardLatest).VerifyDiagnostics().VerifyIL("E.CHelper", @" +{ + // Code size 14 (0xe) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: callvirt ""C C.$()"" + IL_0006: dup + IL_0007: ldc.i4.2 + IL_0008: callvirt ""void C.X.init"" + IL_000d: ret +} +"); + if (RuntimeUtilities.IsCoreClrRuntime) + { + verifier.VerifyIL("E.DHelper", @" +{ + // Code size 21 (0x15) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: callvirt ""D D.$()"" + IL_0006: dup + IL_0007: ldc.i4.2 + IL_0008: callvirt ""void C.X.init"" + IL_000d: dup + IL_000e: ldc.i4.3 + IL_000f: callvirt ""void D.Y.init"" + IL_0014: ret +} + "); + } + else + { + verifier.VerifyIL("E.DHelper", @" +{ + // Code size 26 (0x1a) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: callvirt ""C C.$()"" + IL_0006: castclass ""D"" + IL_000b: dup + IL_000c: ldc.i4.2 + IL_000d: callvirt ""void C.X.init"" + IL_0012: dup + IL_0013: ldc.i4.3 + IL_0014: callvirt ""void D.Y.init"" + IL_0019: ret +} + "); + } + } + private static ImmutableArray GetProperties(CSharpCompilation comp, string typeName) { return comp.GetMember(typeName).GetMembers().WhereAsArray(m => m.Kind == SymbolKind.Property); @@ -23568,7 +23684,7 @@ record B : A } record C : B; "; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.StandardLatest); comp.VerifyDiagnostics( // (4,43): error CS8872: 'B.EqualityContract' must allow overriding because the containing record is not sealed. // protected sealed override System.Type EqualityContract => typeof(B); @@ -23577,6 +23693,12 @@ record C : B; // record C : B; Diagnostic(ErrorCode.ERR_CantOverrideSealed, "C").WithArguments("C.EqualityContract", "B.EqualityContract").WithLocation(6, 8)); + Assert.Equal(RuntimeUtilities.IsCoreClrRuntime, comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + + string expectedClone = comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses + ? "B B." + WellKnownMemberNames.CloneMethodName + "()" + : "A B." + WellKnownMemberNames.CloneMethodName + "()"; + var actualMembers = comp.GetMember("B").GetMembers().ToTestDisplayStrings(); var expectedMembers = new[] { @@ -23590,7 +23712,7 @@ record C : B; "System.Boolean B.Equals(System.Object? obj)", "System.Boolean B.Equals(A? other)", "System.Boolean B.Equals(B? other)", - "A B." + WellKnownMemberNames.CloneMethodName + "()", + expectedClone, "B..ctor(B original)", "B..ctor()", }; @@ -25649,19 +25771,33 @@ public record E { } } } } -"); - - c.VerifyDiagnostics( - // (8,12): error CS0060: Inconsistent accessibility: base type 'X' is less accessible than class 'B.C' - // record C : X - Diagnostic(ErrorCode.ERR_BadVisBaseClass, "C").WithArguments("B.C", "X").WithLocation(8, 12), - // (8,12): error CS0050: Inconsistent accessibility: return type 'X' is less accessible than method 'B.C.$()' - // record C : X - Diagnostic(ErrorCode.ERR_BadVisReturnType, "C").WithArguments("B.C.$()", "X").WithLocation(8, 12), - // (8,12): error CS0051: Inconsistent accessibility: parameter type 'X' is less accessible than method 'B.C.Equals(X?)' - // record C : X - Diagnostic(ErrorCode.ERR_BadVisParamType, "C").WithArguments("B.C.Equals(X?)", "X").WithLocation(8, 12) - ); +", targetFramework: TargetFramework.StandardLatest); + Assert.Equal(RuntimeUtilities.IsCoreClrRuntime, c.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + if (c.Assembly.RuntimeSupportsCovariantReturnsOfClasses) + { + c.VerifyDiagnostics( + // (8,12): error CS0060: Inconsistent accessibility: base type 'X' is less accessible than class 'B.C' + // record C : X + Diagnostic(ErrorCode.ERR_BadVisBaseClass, "C").WithArguments("B.C", "X").WithLocation(8, 12), + // (8,12): error CS0051: Inconsistent accessibility: parameter type 'X' is less accessible than method 'B.C.Equals(X?)' + // record C : X + Diagnostic(ErrorCode.ERR_BadVisParamType, "C").WithArguments("B.C.Equals(X?)", "X").WithLocation(8, 12) + ); + } + else + { + c.VerifyDiagnostics( + // (8,12): error CS0060: Inconsistent accessibility: base type 'X' is less accessible than class 'B.C' + // record C : X + Diagnostic(ErrorCode.ERR_BadVisBaseClass, "C").WithArguments("B.C", "X").WithLocation(8, 12), + // (8,12): error CS0050: Inconsistent accessibility: return type 'X' is less accessible than method 'B.C.$()' + // record C : X + Diagnostic(ErrorCode.ERR_BadVisReturnType, "C").WithArguments("B.C.$()", "X").WithLocation(8, 12), + // (8,12): error CS0051: Inconsistent accessibility: parameter type 'X' is less accessible than method 'B.C.Equals(X?)' + // record C : X + Diagnostic(ErrorCode.ERR_BadVisParamType, "C").WithArguments("B.C.Equals(X?)", "X").WithLocation(8, 12) + ); + } } [Fact] @@ -26004,21 +26140,39 @@ public partial record C1 { } "; - var comp = CreateCompilation(text); - comp.VerifyDiagnostics( - // (2,15): error CS0106: The modifier 'static' is not valid for this item - // static record NV - Diagnostic(ErrorCode.ERR_BadMemberFlag, "NV").WithArguments("static").WithLocation(2, 15), - // (6,23): error CS0050: Inconsistent accessibility: return type 'NV' is less accessible than method 'C1.$()' - // public partial record C1 - Diagnostic(ErrorCode.ERR_BadVisReturnType, "C1").WithArguments("C1.$()", "NV").WithLocation(6, 23), - // (6,23): error CS0051: Inconsistent accessibility: parameter type 'NV' is less accessible than method 'C1.Equals(NV?)' - // public partial record C1 - Diagnostic(ErrorCode.ERR_BadVisParamType, "C1").WithArguments("C1.Equals(NV?)", "NV").WithLocation(6, 23), - // (10,16): error CS0060: Inconsistent accessibility: base class 'NV' is less accessible than class 'C1' - // partial record C1 : NV - Diagnostic(ErrorCode.ERR_BadVisBaseClass, "C1").WithArguments("C1", "NV").WithLocation(10, 16) - ); + var comp = CreateCompilation(text, targetFramework: TargetFramework.StandardLatest); + Assert.Equal(RuntimeUtilities.IsCoreClrRuntime, comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + if (comp.Assembly.RuntimeSupportsCovariantReturnsOfClasses) + { + comp.VerifyDiagnostics( + // (2,15): error CS0106: The modifier 'static' is not valid for this item + // static record NV + Diagnostic(ErrorCode.ERR_BadMemberFlag, "NV").WithArguments("static").WithLocation(2, 15), + // (6,23): error CS0051: Inconsistent accessibility: parameter type 'NV' is less accessible than method 'C1.Equals(NV?)' + // public partial record C1 + Diagnostic(ErrorCode.ERR_BadVisParamType, "C1").WithArguments("C1.Equals(NV?)", "NV").WithLocation(6, 23), + // (10,16): error CS0060: Inconsistent accessibility: base class 'NV' is less accessible than class 'C1' + // partial record C1 : NV + Diagnostic(ErrorCode.ERR_BadVisBaseClass, "C1").WithArguments("C1", "NV").WithLocation(10, 16) + ); + } + else + { + comp.VerifyDiagnostics( + // (2,15): error CS0106: The modifier 'static' is not valid for this item + // static record NV + Diagnostic(ErrorCode.ERR_BadMemberFlag, "NV").WithArguments("static").WithLocation(2, 15), + // (6,23): error CS0050: Inconsistent accessibility: return type 'NV' is less accessible than method 'C1.$()' + // public partial record C1 + Diagnostic(ErrorCode.ERR_BadVisReturnType, "C1").WithArguments("C1.$()", "NV").WithLocation(6, 23), + // (6,23): error CS0051: Inconsistent accessibility: parameter type 'NV' is less accessible than method 'C1.Equals(NV?)' + // public partial record C1 + Diagnostic(ErrorCode.ERR_BadVisParamType, "C1").WithArguments("C1.Equals(NV?)", "NV").WithLocation(6, 23), + // (10,16): error CS0060: Inconsistent accessibility: base class 'NV' is less accessible than class 'C1' + // partial record C1 : NV + Diagnostic(ErrorCode.ERR_BadVisBaseClass, "C1").WithArguments("C1", "NV").WithLocation(10, 16) + ); + } } [Fact] @@ -29918,5 +30072,70 @@ class C2 : I(0) Diagnostic(ErrorCode.ERR_UnexpectedArgumentList, "(0)").WithLocation(10, 13) ); } + + [Theory, WorkItem(44902, "https://github.com/dotnet/roslyn/issues/44902")] + [CombinatorialData] + public void CrossAssemblySupportingAndNotSupportingCovariantReturns(bool useCompilationReference) + { + var sourceA = +@"public record B(int I) +{ +} + +public record C(int I) : B(I);"; + + var compA = CreateEmptyCompilation(new[] { sourceA, IsExternalInitTypeDefinition }, references: TargetFrameworkUtil.GetReferences(TargetFramework.NetStandard20)); + compA.VerifyDiagnostics(); + Assert.False(compA.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + var actualMembers = compA.GetMember("C").GetMembers().ToTestDisplayStrings(); + var expectedMembers = new[] + { + "C..ctor(System.Int32 I)", + "System.Type C.EqualityContract.get", + "System.Type C.EqualityContract { get; }", + "System.String C.ToString()", + "System.Boolean C." + WellKnownMemberNames.PrintMembersMethodName + "(System.Text.StringBuilder builder)", + "System.Boolean C.op_Inequality(C? left, C? right)", + "System.Boolean C.op_Equality(C? left, C? right)", + "System.Int32 C.GetHashCode()", + "System.Boolean C.Equals(System.Object? obj)", + "System.Boolean C.Equals(B? other)", + "System.Boolean C.Equals(C? other)", + "B C." + WellKnownMemberNames.CloneMethodName + "()", + "C..ctor(C original)", + "void C.Deconstruct(out System.Int32 I)", + }; + AssertEx.Equal(expectedMembers, actualMembers); + + var refA = useCompilationReference ? compA.ToMetadataReference() : compA.EmitToImageReference(); + + var sourceB = "record D(int I) : C(I);"; + + // CS1701: Assuming assembly reference '{0}' used by '{1}' matches identity '{2}' of '{3}', you may need to supply runtime policy + var compB = CreateCompilation(sourceB, references: new[] { refA }, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions("CS1701", ReportDiagnostic.Suppress), parseOptions: TestOptions.Regular9, targetFramework: TargetFramework.NetCoreApp); + compB.VerifyDiagnostics(); + Assert.True(compB.Assembly.RuntimeSupportsCovariantReturnsOfClasses); + + actualMembers = compB.GetMember("D").GetMembers().ToTestDisplayStrings(); + expectedMembers = new[] + { + "D..ctor(System.Int32 I)", + "System.Type D.EqualityContract.get", + "System.Type D.EqualityContract { get; }", + "System.String D.ToString()", + "System.Boolean D." + WellKnownMemberNames.PrintMembersMethodName + "(System.Text.StringBuilder builder)", + "System.Boolean D.op_Inequality(D? left, D? right)", + "System.Boolean D.op_Equality(D? left, D? right)", + "System.Int32 D.GetHashCode()", + "System.Boolean D.Equals(System.Object? obj)", + "System.Boolean D.Equals(C? other)", + "System.Boolean D.Equals(D? other)", + "D D." + WellKnownMemberNames.CloneMethodName + "()", + "D..ctor(D original)", + "void D.Deconstruct(out System.Int32 I)" + }; + AssertEx.Equal(expectedMembers, actualMembers); + + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index 7ddb76fe3375a..19e7541e49585 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -390,8 +390,16 @@ public void NamespaceWithoutName() var tree = Parse(text); var comp = CreateCompilation(tree); var model = comp.GetSemanticModel(tree); - var errors = comp.GetDiagnostics().ToArray(); - Assert.Equal(3, errors.Length); + comp.VerifyDiagnostics( + // (1,10): error CS1001: Identifier expected + // namespace + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 10), + // (1,10): error CS1514: { expected + // namespace + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 10), + // (1,10): error CS1513: } expected + // namespace + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 10)); var nsArray = tree.GetCompilationUnitRoot().DescendantNodes().Where(node => node.IsKind(SyntaxKind.NamespaceDeclaration)).ToArray(); Assert.Equal(1, nsArray.Length); @@ -401,6 +409,27 @@ public void NamespaceWithoutName() Assert.Equal(string.Empty, symbol.Name); } + [WorkItem(539740, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539740")] + [Fact] + public void FileScopedNamespaceWithoutName() + { + var text = "namespace;"; + var tree = Parse(text); + var comp = CreateCompilation(tree); + var model = comp.GetSemanticModel(tree); + comp.VerifyDiagnostics( + // (1,10): error CS1001: Identifier expected + // namespace; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 10)); + + var nsArray = tree.GetCompilationUnitRoot().DescendantNodes().Where(node => node.IsKind(SyntaxKind.FileScopedNamespaceDeclaration)).ToArray(); + Assert.Equal(1, nsArray.Length); + + var nsSyntax = nsArray[0] as FileScopedNamespaceDeclarationSyntax; + var symbol = model.GetDeclaredSymbol(nsSyntax); + Assert.Equal(string.Empty, symbol.Name); + } + [Fact] public void LazyBoundUsings1() { @@ -3815,6 +3844,28 @@ static void Main() Assert.Equal("lib", target.ContainingAssembly.Name); } + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void LookupInNamespace(string ob, string cb) + { + var source = @" +namespace NS +" + ob + @" + class C + { + void M() { } + void M0() { } + } +" + cb + @" +"; + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var methodDecl = tree.GetRoot().DescendantNodes().OfType().First(); + var endPosition = methodDecl.Body.OpenBraceToken.EndPosition; + var symbol = model.LookupSymbols(endPosition, name: "M0").Single(); + Assert.Equal("void NS.C.M0()", symbol.ToTestDisplayString()); + } + [WorkItem(1019366, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1019366")] [WorkItem(273, "CodePlex")] [ClrOnlyFact] diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs index cc831c33af8c5..91b92032bb181 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs @@ -66,17 +66,17 @@ void M(int x) Assert.Equal(RefKind.RefReadOnly, rxLocal.RefKind); } - [Fact] - public void TestGetDeclaredSymbolFromNamespace() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void TestGetDeclaredSymbolFromNamespace(string ob, string cb) { - var compilation = CreateCompilation(@" + var compilation = CreateCompilation($@" namespace A.B -{ -} +{ob} +{cb} "); var tree = compilation.SyntaxTrees[0]; var root = tree.GetCompilationUnitRoot(); - var decl = (NamespaceDeclarationSyntax)root.Members[0]; + var decl = (BaseNamespaceDeclarationSyntax)root.Members[0]; var model = compilation.GetSemanticModel(tree); var symbol = model.GetDeclaredSymbol(decl); Assert.NotNull(symbol); @@ -108,21 +108,21 @@ public void NamespaceAndClassWithNoNames() Assert.Equal("", symbol.Name); } - [Fact] - public void TestGetDeclaredSymbolFromNestedNamespace() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void TestGetDeclaredSymbolFromNestedNamespace(string ob, string cb) { - var compilation = CreateCompilation(@" + var compilation = CreateCompilation($@" namespace A.B -{ +{ob} namespace C.D - { - } -} + {ob} + {cb} +{cb} "); var tree = compilation.SyntaxTrees[0]; var root = tree.GetCompilationUnitRoot(); - var abns = (NamespaceDeclarationSyntax)root.Members[0]; - var cdns = (NamespaceDeclarationSyntax)abns.Members[0]; + var abns = (BaseNamespaceDeclarationSyntax)root.Members[0]; + var cdns = (BaseNamespaceDeclarationSyntax)abns.Members[0]; var model = compilation.GetSemanticModel(tree); var symbol = model.GetDeclaredSymbol(cdns); Assert.NotNull(symbol); @@ -195,20 +195,20 @@ enum E { } Assert.Equal("E", symbol.ToTestDisplayString()); } - [Fact] - public void GenericNameInNamespaceName() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void GenericNameInNamespaceName(string ob, string cb) { var compilation = CreateCompilation(@" -namespace C.B -{ +namespace C.B +" + ob + @" class Y { } -} +" + cb + @" "); var tree = compilation.SyntaxTrees[0]; var root = tree.GetCompilationUnitRoot(); var classY = ((root. - Members[0] as NamespaceDeclarationSyntax). + Members[0] as BaseNamespaceDeclarationSyntax). Members[0] as TypeDeclarationSyntax); var model = compilation.GetSemanticModel(tree); @@ -218,20 +218,20 @@ class Y { } Assert.Equal("C.B.Y", symbol.ToTestDisplayString()); } - [Fact] - public void AliasedNameInNamespaceName() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void AliasedNameInNamespaceName(string ob, string cb) { var compilation = CreateCompilation(@" -namespace alias::C.B -{ +namespace alias::C.B +" + ob + @" class Y { } -} +" + cb + @" "); var tree = compilation.SyntaxTrees[0]; var root = tree.GetCompilationUnitRoot(); var classY = ((root. - Members[0] as NamespaceDeclarationSyntax). + Members[0] as BaseNamespaceDeclarationSyntax). Members[0] as TypeDeclarationSyntax); var model = compilation.GetSemanticModel(tree); @@ -1013,18 +1013,19 @@ class C1 { } } [WorkItem(537230, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537230")] - [Fact] - public void TestLookupUnresolvableNamespaceUsing() + [Theory] + [MemberData(nameof(FileScopedOrBracedNamespace))] + public void TestLookupUnresolvableNamespaceUsing(string ob, string cb) { var compilation = CreateCompilation(@" namespace A - { + " + ob + @" using B.C; public class B : C { } - } + " + cb + @" "); var tree = compilation.SyntaxTrees.Single(); var usingDirective = (UsingDirectiveSyntax)tree.FindNodeOrTokenByKind(SyntaxKind.UsingDirective).AsNode(); @@ -1034,21 +1035,22 @@ public class B : C // should validate type here } - [Fact] - public void TestLookupSourceSymbolHidesMetadataSymbol() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void TestLookupSourceSymbolHidesMetadataSymbol(string ob, string cb) { var compilation = CreateCompilation(@" namespace System -{ +" + ob + @" public class DateTime { string TheDateAndTime; } -}"); +" + cb + @" +"); var tree = compilation.SyntaxTrees.Single(); - var namespaceDecl = (NamespaceDeclarationSyntax)tree.GetCompilationUnitRoot().Members[0]; + var namespaceDecl = (BaseNamespaceDeclarationSyntax)tree.GetCompilationUnitRoot().Members[0]; var classDecl = (ClassDeclarationSyntax)namespaceDecl.Members[0]; var memberDecl = (FieldDeclarationSyntax)classDecl.Members[0]; @@ -2999,14 +3001,15 @@ void M(out byte p) {} } [WorkItem(537953, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537953")] - [Fact] - public void GetDeclaredSymbolNoTypeSymbolWithErr() + [Theory] + [MemberData(nameof(FileScopedOrBracedNamespace))] + public void GetDeclaredSymbolNoTypeSymbolWithErr(string ob, string cb) { var compilation = (Compilation)CreateCompilation(@" namespace NS -{ +" + ob + @" protected class A { } -} +" + cb + @" "); var tree = compilation.SyntaxTrees.First(); var root = tree.GetCompilationUnitRoot(); @@ -3016,7 +3019,7 @@ protected class A { } var ns1 = globalNS.GetMembers("NS").Single() as INamespaceSymbol; var srcSym = ns1.GetMembers("A").Single() as INamedTypeSymbol; - var nsSyntax = (root.Members[0] as NamespaceDeclarationSyntax); + var nsSyntax = (root.Members[0] as BaseNamespaceDeclarationSyntax); var declSym = model.GetDeclaredSymbol(nsSyntax.Members[0] as TypeDeclarationSyntax); Assert.Equal(srcSym, declSym); diff --git a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/IncludeTests.cs b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/IncludeTests.cs index 6a16e41bc8b41..4f210ff9018fb 100644 --- a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/IncludeTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/IncludeTests.cs @@ -74,6 +74,68 @@ class NamedType { } $@" +", symbol.GetDocumentationCommentXml(expandIncludes: false)); + } + + [Theory] + [InlineData("Field", "F:Acme.Widget.Field")] + [InlineData(WellKnownMemberNames.StaticConstructorName, "M:Acme.Widget.#cctor")] + [InlineData("Event", "E:Acme.Widget.Event")] + [InlineData("Property", "P:Acme.Widget.Property")] + [InlineData("Method", "M:Acme.Widget.Method")] + [InlineData("NamedType", "T:Acme.Widget.NamedType")] + public void TestDocumentationCachingFileScopedNamespace(string symbolName, string documentationId) + { + using var _ = new EnsureEnglishUICulture(); + + var compilation = CreateCompilationWithMscorlib40AndDocumentationComments(@"namespace Acme; + +class Widget +{ + /// + int Field; + + /// + static Widget() { } + + /// + event EventHandler Event; + + /// + int Property { get; } + + /// + void Method() { } + + /// + class NamedType { } +} +"); + + var acmeNamespace = (NamespaceSymbol)compilation.GlobalNamespace.GetMembers("Acme").Single(); + var widgetClass = acmeNamespace.GetTypeMembers("Widget").Single(); + + var symbol = widgetClass.GetMembers(symbolName).Single(); + Assert.Equal(documentationId, symbol.GetDocumentationCommentId()); + Assert.Equal( +$@" + + +", symbol.GetDocumentationCommentXml(expandIncludes: true)); + Assert.Equal( +$@" + + +", symbol.GetDocumentationCommentXml(expandIncludes: false)); + Assert.Equal( +$@" + + +", symbol.GetDocumentationCommentXml(expandIncludes: true)); + Assert.Equal( +$@" + + ", symbol.GetDocumentationCommentXml(expandIncludes: false)); } } diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 7f142995debb0..4c77ab06fec8e 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -2495,16 +2495,16 @@ class C2 {} } } } SymbolDisplayPartKind.ClassName); } - [Fact] - public void TestAlias3() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void TestAlias3(string ob, string cb) { var text = @" using Goo = N1.C1; -namespace N1 { +namespace N1 " + ob + @" class Goo { } class C1 { } -} +" + cb + @" "; Func findSymbol = global => @@ -4374,17 +4374,18 @@ class C } [WorkItem(791756, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/791756")] - [Fact] - public void KindOptions() + [Theory] + [MemberData(nameof(FileScopedOrBracedNamespace))] + public void KindOptions(string ob, string cb) { var source = @" namespace N -{ +" + ob + @" class C { event System.Action E; } -} +" + cb + @" "; var memberFormat = new SymbolDisplayFormat( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, @@ -7071,23 +7072,23 @@ event System.Action E { add {} remove {} } SymbolDisplayPartKind.Keyword); } - [Fact] - public void TestReadOnlyStruct_Nested() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void TestReadOnlyStruct_Nested(string ob, string cb) { var source = @" namespace Nested -{ +" + ob + @" struct X { readonly void M() { } } -} +" + cb + @" "; var format = SymbolDisplayFormat.TestFormat .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers) .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - var comp = CreateCompilation(source).VerifyDiagnostics(); + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics(); var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); var declaration = (BaseTypeDeclarationSyntax)semanticModel.SyntaxTree.GetRoot().DescendantNodes().Single(n => n.Kind() == SyntaxKind.StructDeclaration); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs index 260082a4d5353..92d3cd30abaad 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/AssemblyAndNamespaceTests.cs @@ -46,13 +46,13 @@ class A {} Assert.Null(sym.ContainingSymbol); } - [Fact, WorkItem(1979, "DevDiv_Projects/Roslyn"), WorkItem(2026, "DevDiv_Projects/Roslyn"), WorkItem(544009, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544009")] - public void SourceModule() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace)), WorkItem(1979, "DevDiv_Projects/Roslyn"), WorkItem(2026, "DevDiv_Projects/Roslyn"), WorkItem(544009, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544009")] + public void SourceModule(string ob, string cb) { var text = @"namespace NS.NS1.NS2 -{ +" + ob + @" class A {} -} +" + cb + @" "; var comp = CreateCompilation(text, assemblyName: "Test"); @@ -181,23 +181,23 @@ class C : NN.B { } Assert.Equal("NS.NS1.B", type2.BaseType().ToTestDisplayString()); } - [Fact] - public void MultiModulesNamespace() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void MultiModulesNamespace(string ob, string cb) { var text1 = @"namespace N1 -{ +" + ob + @" class A {} -} +" + cb + @" "; var text2 = @"namespace N1 -{ +" + ob + @" interface IGoo {} -} +" + cb + @" "; var text3 = @"namespace N1 -{ +" + ob + @" struct SGoo {} -} +" + cb + @" "; var comp1 = CreateCompilation(text1, assemblyName: "Compilation1"); var comp2 = CreateCompilation(text2, assemblyName: "Compilation2"); @@ -271,11 +271,11 @@ struct SGoo {} } /// Container with nested types and non-type members with the same name - [Fact] - public void ClassWithNestedTypesAndMembersWithSameName() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void ClassWithNestedTypesAndMembersWithSameName(string ob, string cb) { var text1 = @"namespace N1 -{ +" + ob + @" class A { class b @@ -292,7 +292,7 @@ int b() {} int b(string s){} } -} +" + cb + @" "; var comp = CSharpCompilation.Create( @@ -338,19 +338,19 @@ class C : ns.A {} } [WorkItem(540785, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540785")] - [Fact] - public void GenericNamespace() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void GenericNamespace(string ob, string cb) { var compilation = CreateEmptyCompilation(@" namespace Goo -{ +" + ob + @" class Program { static void Main() { } } -} +" + cb + @" "); var global = compilation.GlobalNamespace; @@ -440,12 +440,12 @@ async void AM() { } } [WorkItem(863435, "DevDiv/Personal")] - [Fact] - public void CS1671ERR_BadModifiersOnNamespace01() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void CS1671ERR_BadModifiersOnNamespace01(string ob, string cb) { var test = @" public namespace NS // CS1671 -{ +" + ob + @" class Test { public static int Main() @@ -453,9 +453,9 @@ public static int Main() return 1; } } -} +" + cb + @" "; - CreateCompilationWithMscorlib45(test).VerifyDiagnostics( + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( // (2,1): error CS1671: A namespace declaration cannot have modifiers or attributes Diagnostic(ErrorCode.ERR_BadModifiersOnNamespace, "public").WithLocation(2, 1)); } @@ -471,5 +471,210 @@ namespace N { } // (2,1): error CS1671: A namespace declaration cannot have modifiers or attributes Diagnostic(ErrorCode.ERR_BadModifiersOnNamespace, "[System.Obsolete]").WithLocation(1, 1)); } + + [Fact] + public void NamespaceWithSemicolon1() + { + var test = +@"namespace A;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics(); + } + + [Fact] + public void NamespaceWithSemicolon3() + { + var test = +@"namespace A.B;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics(); + } + + [Fact] + public void MultipleFileScopedNamespaces() + { + var test = +@"namespace A; +namespace B;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (2,11): error CS8907: Source file can only contain one file-scoped namespace declaration. + // namespace B; + Diagnostic(ErrorCode.ERR_MultipleFileScopedNamespace, "B").WithLocation(2, 11)); + } + + [Fact] + public void FileScopedNamespaceNestedInNormalNamespace() + { + var test = +@"namespace A +{ + namespace B; +}"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (3,15): error CS8908: Source file can not contain both file-scoped and normal namespace declarations. + // namespace B; + Diagnostic(ErrorCode.ERR_FileScopedAndNormalNamespace, "B").WithLocation(3, 15)); + } + + [Fact] + public void NormalAndFileScopedNamespace1() + { + var test = +@"namespace A; +namespace B +{ +}"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (2,11): error CS8908: error CS8908: Source file can not contain both file-scoped and normal namespace declarations. + // namespace B + Diagnostic(ErrorCode.ERR_FileScopedAndNormalNamespace, "B").WithLocation(2, 11)); + } + + [Fact] + public void NormalAndFileScopedNamespace2() + { + var test = +@"namespace A +{ +} +namespace B;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (4,11): error CS8909: File-scoped namespace must precede all other members in a file. + // namespace B; + Diagnostic(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, "B").WithLocation(4, 11)); + } + + [Fact] + public void NamespaceWithPrecedingUsing() + { + var test = +@"using System; +namespace A;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (1,1): hidden CS8019: Unnecessary using directive. + // using System; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(1, 1)); + } + + [Fact] + public void NamespaceWithFollowingUsing() + { + var test = +@"namespace X; +using System;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using System; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System;").WithLocation(2, 1)); + } + + [Fact] + public void NamespaceWithPrecedingType() + { + var test = +@"class X { } +namespace System;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (2,11): error CS8909: File-scoped namespace must precede all other members in a file. + // namespace System; + Diagnostic(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, "System").WithLocation(2, 11)); + } + + [Fact] + public void NamespaceWithFollowingType() + { + var test = +@"namespace System; +class X { }"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics(); + } + + [Fact] + public void FileScopedNamespaceWithPrecedingStatement() + { + var test = +@" +System.Console.WriteLine(); +namespace B;"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (3,11): error CS8914: File-scoped namespace must precede all other members in a file. + // namespace B; + Diagnostic(ErrorCode.ERR_FileScopedNamespaceNotBeforeAllMembers, "B").WithLocation(3, 11)); + } + + [Fact] + public void FileScopedNamespaceWithFollowingStatement() + { + var test = +@" +namespace B; +System.Console.WriteLine();"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (3,16): error CS0116: A namespace cannot directly contain members such as fields or methods + // System.Console.WriteLine(); + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "WriteLine").WithLocation(3, 16), + // (3,26): error CS8124: Tuple must contain at least two elements. + // System.Console.WriteLine(); + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(3, 26), + // (3,27): error CS1022: Type or namespace definition, or end-of-file expected + // System.Console.WriteLine(); + Diagnostic(ErrorCode.ERR_EOFExpected, ";").WithLocation(3, 27)); + } + + [Fact] + public void FileScopedNamespaceUsingsBeforeAndAfter() + { + var source1 = @" +namespace A +{ + class C1 { } +} + +namespace B +{ + class C2 { } +} +"; + var source2 = @" +using A; +namespace X; +using B; + +class C +{ + void M() + { + new C1(); + new C2(); + } +} +"; + + CreateCompilationWithMscorlib45(new[] { source1, source2 }, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics(); + } + + [Fact] + public void FileScopedNamespaceFollowedByVariable() + { + var test = @" +namespace B; +int x; // 1 +"; + + CreateCompilationWithMscorlib45(test, parseOptions: TestOptions.RegularWithFileScopedNamespaces).VerifyDiagnostics( + // (3,5): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // int x; // 1 + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "x").WithLocation(3, 5)); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index ae8c7882079db..5a2b786ec2ec3 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -608,6 +608,7 @@ public void AllWellKnownTypes() case WellKnownType.System_Runtime_CompilerServices_SwitchExpressionException: case WellKnownType.System_Runtime_CompilerServices_NativeIntegerAttribute: case WellKnownType.System_Runtime_CompilerServices_IsExternalInit: + case WellKnownType.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler: // Not yet in the platform. continue; case WellKnownType.Microsoft_CodeAnalysis_Runtime_Instrumentation: @@ -961,6 +962,7 @@ public void AllWellKnownTypeMembers() case WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctorObject: case WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctor: case WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags: + case WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear: // Not yet in the platform. continue; case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile: diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs index 4a1bba33a6d84..bdb7812c8f2d7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs @@ -327,6 +327,7 @@ internal override AttributeUsageInfo GetAttributeUsageInfo() internal override bool IsRecord => false; internal override bool IsRecordStruct => false; internal override bool HasPossibleWellKnownCloneMethod() => false; + internal override bool IsInterpolatedStringHandlerType => false; internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs index e37e1eaad7c06..8ed5810bc2360 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/BaseClassTests.cs @@ -1238,13 +1238,13 @@ public class ClassC : ClassB {} Assert.IsAssignableFrom(B2.BaseType()); } - [Fact] - public void NestedNames1() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void NestedNames1(string ob, string cb) { var text = @" namespace N -{ +" + ob + @" static class C { class A @@ -1253,7 +1253,7 @@ class B : A>.D { } private class D { } } } -} +" + cb + @" "; var comp = CreateEmptyCompilation(text); var global = comp.GlobalNamespace; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs index 1d996cb9da9d4..5f95470fd45f0 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs @@ -167,12 +167,12 @@ private void CheckLambdaDeclaringSyntax(CSharpCompilation comp, SyntaxTre } } - [Fact] - public void SourceNamedTypeDeclaringSyntax() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void SourceNamedTypeDeclaringSyntax(string ob, string cb) { var text = @" -namespace N1 { +namespace N1 " + ob + @" class C1 { class Nested {} delegate int NestedDel(string s); @@ -183,7 +183,7 @@ public struct S1 { internal interface I1 {} enum E1 { Red } delegate void D1(int i); -} +" + cb + @" "; var comp = (Compilation)CreateCompilation(text); var global = comp.GlobalNamespace; @@ -420,15 +420,15 @@ namespace System {} CheckDeclaringSyntaxNodesWithoutGetDeclaredSymbol(comp, global, 1, SyntaxKind.CompilationUnit); } - [Fact] - public void TypeParameterDeclaringSyntax() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void TypeParameterDeclaringSyntax(string ob, string cb) { var text = @" using System; using System.Collections.Generic; -namespace N1 { +namespace N1 " + ob + @" class C1 { class C2 { public C1.C2 f1; @@ -442,7 +442,7 @@ class C3 { class M { } -} +" + cb + @" "; var comp = (Compilation)CreateCompilation(text); var global = comp.GlobalNamespace; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs index 056af7e3cf087..7ac6c9897c222 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/MethodTests.cs @@ -450,15 +450,15 @@ public void MethodWithRefOutArray(ref int[] ary1, out string[] ary2) Assert.Equal(RefKind.Out, p2.RefKind); } - [Fact] - public void InterfaceImplementsCrossTrees() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void InterfaceImplementsCrossTrees(string ob, string cb) { var text1 = @"using System; using System.Collections.Generic; namespace NS -{ +" + ob + @" public class Abc {} public interface IGoo @@ -477,14 +477,14 @@ public interface I2 : I1 void M21(); Abc M22(ref Abc p); } -}"; +" + cb; var text2 = @"using System; using System.Collections.Generic; namespace NS.NS1 -{ +" + ob + @" public class Impl : I2, IGoo, I1 { void IGoo.M(ref string p) { } @@ -498,7 +498,7 @@ struct S: IGoo { void IGoo.M(ref T t) {} } -}"; +" + cb; var comp = CreateCompilation(new[] { text1, text2 }); Assert.Equal(0, comp.GetDeclarationDiagnostics().Count()); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/UsingAliasTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/UsingAliasTests.cs index cdb2cfbd0aaeb..98fb11ce1befd 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/UsingAliasTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/UsingAliasTests.cs @@ -369,5 +369,48 @@ public object Method1() Assert.Equal(0, symbolInfo.CandidateSymbols.Length); Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); } + + [ClrOnlyFact, WorkItem(2805, "https://github.com/dotnet/roslyn/issues/2805")] + public void AliasWithAnErrorFileScopedNamespace() + { + var text = +@" +namespace NS; +using Short = LongNamespace; +class Test +{ + public object Method1() + { + return (new Short.MyClass()).Prop; + } +} +"; + + var compilation = CreateCompilation(text, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); + + compilation.VerifyDiagnostics( + // (3,15): error CS0246: The type or namespace name 'LongNamespace' could not be found (are you missing a using directive or an assembly reference?) + // using Short = LongNamespace; + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "LongNamespace").WithArguments("LongNamespace").WithLocation(3, 15)); + + var tree = compilation.SyntaxTrees.Single(); + + var node = tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "Short").Skip(1).Single(); + + Assert.Equal("Short.MyClass", node.Parent.ToString()); + + var model = compilation.GetSemanticModel(tree); + + var alias = model.GetAliasInfo(node); + Assert.Equal("Short=LongNamespace", alias.ToTestDisplayString()); + Assert.Equal(SymbolKind.ErrorType, alias.Target.Kind); + Assert.Equal("LongNamespace", alias.Target.ToTestDisplayString()); + + var symbolInfo = model.GetSymbolInfo(node); + + Assert.Null(symbolInfo.Symbol); + Assert.Equal(0, symbolInfo.CandidateSymbols.Length); + Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/TypeTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/TypeTests.cs index 75418afed0f12..1c06c20659910 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/TypeTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/TypeTests.cs @@ -91,19 +91,19 @@ interface B { Assert.Equal(Accessibility.Internal, s.DeclaredAccessibility); } - [Fact] - public void InheritedTypesCrossTrees() + [Theory, MemberData(nameof(FileScopedOrBracedNamespace))] + public void InheritedTypesCrossTrees(string ob, string cb) { - var text = @"namespace MT { + var text = @"namespace MT " + ob + @" public interface IGoo { void Goo(); } public interface IGoo { R Goo(T t); } -} +" + cb + @" "; - var text1 = @"namespace MT { + var text1 = @"namespace MT " + ob + @" public interface IBar : IGoo { void Bar(T t); } -} +" + cb + @" "; - var text2 = @"namespace NS { + var text2 = @"namespace NS " + ob + @" using System; using MT; public class A : IGoo, IBar { @@ -113,11 +113,11 @@ void IBar.Bar(string s) { } } public class B : A {} -} +" + cb + @" "; - var text3 = @"namespace NS { + var text3 = @"namespace NS " + ob + @" public class C : B {} -} +" + cb + @" "; var comp = CreateCompilation(new[] { text, text1, text2, text3 }); diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 2ff7f2625b677..c106df571798d 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -41,7 +41,7 @@ public void Resources() continue; } - Assert.False(string.IsNullOrEmpty(ErrorFacts.GetMessage(code, CultureInfo.InvariantCulture))); + Assert.False(string.IsNullOrEmpty(ErrorFacts.GetMessage(code, CultureInfo.InvariantCulture)), $"Message for error {code} is null or empty."); } } @@ -263,6 +263,7 @@ public void WarningLevel_2() case ErrorCode.WRN_ReturnNotNullIfNotNull: case ErrorCode.WRN_UnreadRecordParameter: case ErrorCode.WRN_DoNotCompareFunctionPointers: + case ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; case ErrorCode.WRN_MainIgnored: @@ -424,6 +425,7 @@ public void NullableWarnings() ErrorCode.WRN_UnreadRecordParameter, ErrorCode.WRN_DoNotCompareFunctionPointers, ErrorCode.WRN_PartialMethodTypeDifference, + ErrorCode.WRN_ParameterOccursAfterInterpolatedStringHandlerParameter, }; Assert.Contains(error, nullableUnrelatedWarnings); 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 b480da073976c..ba699b4a520bc 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 @@ -451,6 +451,9 @@ private static Syntax.InternalSyntax.UsingDirectiveSyntax GenerateUsingDirective private static Syntax.InternalSyntax.NamespaceDeclarationSyntax GenerateNamespaceDeclaration() => InternalSyntaxFactory.NamespaceDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.NamespaceKeyword), GenerateIdentifierName(), InternalSyntaxFactory.Token(SyntaxKind.OpenBraceToken), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.CloseBraceToken), null); + private static Syntax.InternalSyntax.FileScopedNamespaceDeclarationSyntax GenerateFileScopedNamespaceDeclaration() + => InternalSyntaxFactory.FileScopedNamespaceDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.NamespaceKeyword), GenerateIdentifierName(), InternalSyntaxFactory.Token(SyntaxKind.SemicolonToken), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList()); + private static Syntax.InternalSyntax.AttributeListSyntax GenerateAttributeList() => InternalSyntaxFactory.AttributeList(InternalSyntaxFactory.Token(SyntaxKind.OpenBracketToken), null, new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.CloseBracketToken)); @@ -2537,6 +2540,23 @@ public void TestNamespaceDeclarationFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestFileScopedNamespaceDeclarationFactoryAndProperties() + { + var node = GenerateFileScopedNamespaceDeclaration(); + + Assert.Equal(default, node.AttributeLists); + Assert.Equal(default, node.Modifiers); + Assert.Equal(SyntaxKind.NamespaceKeyword, node.NamespaceKeyword.Kind); + Assert.NotNull(node.Name); + Assert.Equal(SyntaxKind.SemicolonToken, node.SemicolonToken.Kind); + Assert.Equal(default, node.Externs); + Assert.Equal(default, node.Usings); + Assert.Equal(default, node.Members); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestAttributeListFactoryAndProperties() { @@ -7543,6 +7563,32 @@ public void TestNamespaceDeclarationIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestFileScopedNamespaceDeclarationTokenDeleteRewriter() + { + var oldNode = GenerateFileScopedNamespaceDeclaration(); + 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 TestFileScopedNamespaceDeclarationIdentityRewriter() + { + var oldNode = GenerateFileScopedNamespaceDeclaration(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestAttributeListTokenDeleteRewriter() { @@ -10251,6 +10297,9 @@ private static UsingDirectiveSyntax GenerateUsingDirective() private static NamespaceDeclarationSyntax GenerateNamespaceDeclaration() => SyntaxFactory.NamespaceDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Token(SyntaxKind.NamespaceKeyword), GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.OpenBraceToken), new SyntaxList(), new SyntaxList(), new SyntaxList(), SyntaxFactory.Token(SyntaxKind.CloseBraceToken), default(SyntaxToken)); + private static FileScopedNamespaceDeclarationSyntax GenerateFileScopedNamespaceDeclaration() + => SyntaxFactory.FileScopedNamespaceDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Token(SyntaxKind.NamespaceKeyword), GenerateIdentifierName(), SyntaxFactory.Token(SyntaxKind.SemicolonToken), new SyntaxList(), new SyntaxList(), new SyntaxList()); + private static AttributeListSyntax GenerateAttributeList() => SyntaxFactory.AttributeList(SyntaxFactory.Token(SyntaxKind.OpenBracketToken), default(AttributeTargetSpecifierSyntax), new SeparatedSyntaxList(), SyntaxFactory.Token(SyntaxKind.CloseBracketToken)); @@ -12337,6 +12386,23 @@ public void TestNamespaceDeclarationFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestFileScopedNamespaceDeclarationFactoryAndProperties() + { + var node = GenerateFileScopedNamespaceDeclaration(); + + Assert.Equal(default, node.AttributeLists); + Assert.Equal(default, node.Modifiers); + Assert.Equal(SyntaxKind.NamespaceKeyword, node.NamespaceKeyword.Kind()); + Assert.NotNull(node.Name); + Assert.Equal(SyntaxKind.SemicolonToken, node.SemicolonToken.Kind()); + Assert.Equal(default, node.Externs); + Assert.Equal(default, node.Usings); + Assert.Equal(default, node.Members); + var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithNamespaceKeyword(node.NamespaceKeyword).WithName(node.Name).WithSemicolonToken(node.SemicolonToken).WithExterns(node.Externs).WithUsings(node.Usings).WithMembers(node.Members); + Assert.Equal(node, newNode); + } + [Fact] public void TestAttributeListFactoryAndProperties() { @@ -17343,6 +17409,32 @@ public void TestNamespaceDeclarationIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestFileScopedNamespaceDeclarationTokenDeleteRewriter() + { + var oldNode = GenerateFileScopedNamespaceDeclaration(); + 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 TestFileScopedNamespaceDeclarationIdentityRewriter() + { + var oldNode = GenerateFileScopedNamespaceDeclaration(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestAttributeListTokenDeleteRewriter() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs index a43d90daffd80..8961ea40e00a2 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs @@ -578,6 +578,27 @@ public void TestNamespace() Assert.NotEqual(default, ns.CloseBraceToken); } + [Fact] + public void TestFileScopedNamespace() + { + var text = "namespace a;"; + var file = this.ParseFile(text, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); + + Assert.NotNull(file); + Assert.Equal(1, file.Members.Count); + Assert.Equal(text, file.ToString()); + Assert.Equal(0, file.Errors().Length); + + Assert.Equal(SyntaxKind.FileScopedNamespaceDeclaration, file.Members[0].Kind()); + var ns = (FileScopedNamespaceDeclarationSyntax)file.Members[0]; + Assert.NotEqual(default, ns.NamespaceKeyword); + Assert.NotNull(ns.Name); + Assert.Equal("a", ns.Name.ToString()); + Assert.NotEqual(default, ns.SemicolonToken); + Assert.Equal(0, ns.Usings.Count); + Assert.Equal(0, ns.Members.Count); + } + [Fact] public void TestNamespaceWithDottedName() { @@ -623,6 +644,28 @@ public void TestNamespaceWithUsing() Assert.NotEqual(default, ns.CloseBraceToken); } + [Fact] + public void TestFileScopedNamespaceWithUsing() + { + var text = "namespace a; using b.c;"; + var file = this.ParseFile(text, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); + + Assert.NotNull(file); + Assert.Equal(1, file.Members.Count); + Assert.Equal(text, file.ToString()); + Assert.Equal(0, file.Errors().Length); + + Assert.Equal(SyntaxKind.FileScopedNamespaceDeclaration, file.Members[0].Kind()); + var ns = (FileScopedNamespaceDeclarationSyntax)file.Members[0]; + Assert.NotEqual(default, ns.NamespaceKeyword); + Assert.NotNull(ns.Name); + Assert.Equal("a", ns.Name.ToString()); + Assert.NotEqual(default, ns.SemicolonToken); + Assert.Equal(1, ns.Usings.Count); + Assert.Equal("using b.c;", ns.Usings[0].ToString()); + Assert.Equal(0, ns.Members.Count); + } + [Fact] public void TestNamespaceWithExternAlias() { @@ -646,6 +689,28 @@ public void TestNamespaceWithExternAlias() Assert.NotEqual(default, ns.CloseBraceToken); } + [Fact] + public void TestFileScopedNamespaceWithExternAlias() + { + var text = "namespace a; extern alias b;"; + var file = this.ParseFile(text, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); + + Assert.NotNull(file); + Assert.Equal(1, file.Members.Count); + Assert.Equal(text, file.ToString()); + Assert.Equal(0, file.Errors().Length); + + Assert.Equal(SyntaxKind.FileScopedNamespaceDeclaration, file.Members[0].Kind()); + var ns = (FileScopedNamespaceDeclarationSyntax)file.Members[0]; + Assert.NotEqual(default, ns.NamespaceKeyword); + Assert.NotNull(ns.Name); + Assert.Equal("a", ns.Name.ToString()); + Assert.NotEqual(default, ns.SemicolonToken); + Assert.Equal(1, ns.Externs.Count); + Assert.Equal("extern alias b;", ns.Externs[0].ToString()); + Assert.Equal(0, ns.Members.Count); + } + [Fact] public void TestNamespaceWithExternAliasFollowingUsingBad() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs index 9710e38fb52e1..084d38531cf9f 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParserErrorMessageTests.cs @@ -239,12 +239,12 @@ public static int Main() // (2,7): error CS1041: Identifier expected; 'namespace' is a keyword // using namespace System; Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "namespace").WithArguments("", "namespace").WithLocation(2, 7), - // (2,23): error CS1514: { expected - // using namespace System; - Diagnostic(ErrorCode.ERR_LbraceExpected, ";").WithLocation(2, 23), // (4,42): error CS0150: A constant value is expected // public enum e1 {one=1, two=2, three= }; - Diagnostic(ErrorCode.ERR_ConstantExpected, "}").WithLocation(4, 42)); + Diagnostic(ErrorCode.ERR_ConstantExpected, "}").WithLocation(4, 42), + // (10,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(10, 1)); } [WorkItem(862028, "DevDiv/Personal")] diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs index eeee72d89f53c..cbb7db8bd5ad0 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingErrorRecoveryTests.cs @@ -6251,6 +6251,30 @@ public void TestNamespaceDeclarationInUsingDirective() Assert.False(((NamespaceDeclarationSyntax)namespaceDeclaration).Name.IsMissing); } + [Fact] + public void TestFileScopedNamespaceDeclarationInUsingDirective() + { + var text = @"using namespace Goo;"; + var file = this.ParseTree(text); + + Assert.Equal(text, file.ToFullString()); + file.GetDiagnostics().Verify( + // (1,7): error CS1041: Identifier expected; 'namespace' is a keyword + // using namespace Goo; + Diagnostic(ErrorCode.ERR_IdentifierExpectedKW, "namespace").WithArguments("", "namespace").WithLocation(1, 7)); + + var usings = file.Usings; + Assert.Equal(1, usings.Count); + Assert.True(usings[0].Name.IsMissing); + + var members = file.Members; + Assert.Equal(1, members.Count); + + var namespaceDeclaration = members[0]; + Assert.Equal(SyntaxKind.FileScopedNamespaceDeclaration, namespaceDeclaration.Kind()); + Assert.False(((FileScopedNamespaceDeclarationSyntax)namespaceDeclaration).Name.IsMissing); + } + [Fact] public void TestContextualKeywordAsFromVariable() { @@ -7056,6 +7080,27 @@ class c Assert.False(ns.CloseBraceToken.IsMissing); } + + [WorkItem(947819, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/947819")] + [Fact] + public void MissingOpenBraceForClassFileScopedNamespace() + { + var source = @"namespace n; + +class c +"; + var root = SyntaxFactory.ParseSyntaxTree(source).GetRoot(); + + Assert.Equal(source, root.ToFullString()); + // Verify incomplete class decls don't eat tokens of surrounding nodes + var classDecl = root.DescendantNodes().OfType().Single(); + Assert.False(classDecl.Identifier.IsMissing); + Assert.True(classDecl.OpenBraceToken.IsMissing); + Assert.True(classDecl.CloseBraceToken.IsMissing); + var ns = root.DescendantNodes().OfType().Single(); + Assert.False(ns.SemicolonToken.IsMissing); + } + [WorkItem(947819, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/947819")] [Fact] public void MissingOpenBraceForStruct() diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/SingleLineDeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/SingleLineDeclarationParsingTests.cs new file mode 100644 index 0000000000000..3a9798d44b384 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SingleLineDeclarationParsingTests.cs @@ -0,0 +1,855 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class SingleLineDeclarationParsingTests : ParsingTests + { + public SingleLineDeclarationParsingTests(ITestOutputHelper output) : base(output) { } + + protected override SyntaxTree ParseTree(string text, CSharpParseOptions options) + { + return SyntaxFactory.ParseSyntaxTree(text, options); + } + + [Fact] + public void NamespaceWithNoNameSemicolonOrBraces() + { + UsingNode( +@"namespace", TestOptions.RegularPreview, + // (1,10): error CS1001: Identifier expected + // namespace + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 10), + // (1,10): error CS1514: { expected + // namespace + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 10), + // (1,10): error CS1513: } expected + // namespace + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 10)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithNoSemicolonOrBraces1() + { + UsingNode( +@"namespace A", TestOptions.RegularPreview, + // (1,12): error CS1514: { expected + // namespace A + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 12), + // (1,12): error CS1513: } expected + // namespace A + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 12)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithNoSemicolonOrBraces2() + { + UsingNode( +@"namespace A.", TestOptions.RegularPreview, + // (1,13): error CS1001: Identifier expected + // namespace A. + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(1, 13), + // (1,13): error CS1514: { expected + // namespace A. + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 13), + // (1,13): error CS1513: } expected + // namespace A. + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 13)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithNoSemicolonOrBraces3() + { + UsingNode( +@"namespace A.B", TestOptions.RegularPreview, + // (1,14): error CS1514: { expected + // namespace A.B + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 14), + // (1,14): error CS1513: } expected + // namespace A.B + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 14)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolon1() + { + UsingNode( +@"namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolon_CSharp9() + { + UsingNode( +@"namespace A;", TestOptions.Regular9, + // (1,1): error CS8773: Feature 'file-scoped namespace' is not available in C# 9.0. Please use language version 10.0 or greater. + // namespace A; + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "namespace").WithArguments("file-scoped namespace", "10.0").WithLocation(1, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolon2() + { + UsingNode( +@"namespace A.;", TestOptions.RegularPreview, + // (1,13): error CS1001: Identifier expected + // namespace A.; + Diagnostic(ErrorCode.ERR_IdentifierExpected, ";").WithLocation(1, 13)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolon3() + { + UsingNode( +@"namespace A.B;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolonAndOpenBrace() + { + UsingNode( +@"namespace A; {", TestOptions.RegularPreview, + // (1,14): error CS1022: Type or namespace definition, or end-of-file expected + // namespace A; { + Diagnostic(ErrorCode.ERR_EOFExpected, "{").WithLocation(1, 14)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolonAndBraces() + { + UsingNode( +@"namespace A; { }", TestOptions.RegularPreview, + // (1,14): error CS1022: Type or namespace definition, or end-of-file expected + // namespace A; { } + Diagnostic(ErrorCode.ERR_EOFExpected, "{").WithLocation(1, 14), + // (1,16): error CS1022: Type or namespace definition, or end-of-file expected + // namespace A; { } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(1, 16)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithSemicolonAndCloseBrace() + { + UsingNode( +@"namespace A; }", TestOptions.RegularPreview, + // (1,14): error CS1022: Type or namespace definition, or end-of-file expected + // namespace A; } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(1, 14)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MultipleFileScopedNamespaces() + { + UsingNode( +@"namespace A; +namespace B;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void FileScopedNamespaceNestedInNormalNamespace() + { + UsingNode( +@"namespace A +{ + namespace B; +}", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NormalAndFileScopedNamespace1() + { + UsingNode( +@"namespace A; +namespace B +{ +}", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NormalAndFileScopedNamespace2() + { + UsingNode( +@"namespace A +{ +} +namespace B;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.NamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithPrecedingUsing() + { + UsingNode( +@"using X; +namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.UsingDirective); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithFollowingUsing() + { + UsingNode( +@"namespace A; +using X;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.UsingDirective); + { + N(SyntaxKind.UsingKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithPrecedingType() + { + UsingNode( +@"class X { } +namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithFollowingType() + { + UsingNode( +@"namespace A; +class X { }", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithPrecedingExtern() + { + UsingNode( +@"extern alias X; +namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExternAliasDirective); + { + N(SyntaxKind.ExternKeyword); + N(SyntaxKind.AliasKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithFollowingExtern() + { + UsingNode( +@"namespace A; +extern alias X;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.ExternAliasDirective); + { + N(SyntaxKind.ExternKeyword); + N(SyntaxKind.AliasKeyword); + N(SyntaxKind.IdentifierToken, "X"); + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithExtraSemicolon() + { + UsingNode( +@"namespace A;;", TestOptions.RegularPreview, + // (1,13): error CS1022: Type or namespace definition, or end-of-file expected + // namespace A;; + Diagnostic(ErrorCode.ERR_EOFExpected, ";").WithLocation(1, 13)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithGenericName() + { + UsingNode( +@"namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "A"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithAlias() + { + UsingNode( +@"namespace A::B;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.AliasQualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.ColonColonToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "B"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithModifiers() + { + UsingNode( +@"public namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceWithAttributes() + { + UsingNode( +@"[X] namespace A;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "X"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NamespaceFollowedByVariable() + { + UsingNode( +@"namespace A; int x;", TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.FileScopedNamespaceDeclaration); + { + N(SyntaxKind.NamespaceKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "A"); + } + N(SyntaxKind.SemicolonToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs index 0e87076b9dafd..bda243253ce34 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs @@ -511,6 +511,12 @@ public void TestNormalizeDeclaration1() TestNormalizeDeclaration("class c{void M([a]int x,[b] [c,d]int y){}}", "class c\r\n{\r\n void M([a] int x, [b][c, d] int y)\r\n {\r\n }\r\n}"); } + [Fact] + public void TestFileScopedNamespace() + { + TestNormalizeDeclaration("namespace NS;class C{}", "namespace NS;\r\nclass C\r\n{\r\n}"); + } + [Fact] public void TestSpacingOnRecord() { diff --git a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs index 349d03d1618b2..b2f8948f09f10 100644 --- a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs @@ -481,6 +481,19 @@ public static async Task AnyAsync(this ImmutableArray array, F return default; } + public static TValue? FirstOrDefault(this ImmutableArray array, Func predicate, TArg arg) + { + foreach (var val in array) + { + if (predicate(val, arg)) + { + return val; + } + } + + return default; + } + /// /// Casts the immutable array of a Type to an immutable array of its base type. /// @@ -545,6 +558,16 @@ public static ImmutableArray NullToEmpty(this ImmutableArray array) return array.IsDefault ? ImmutableArray.Empty : array; } + /// + /// Returns an empty array if the input nullable value type is null or the underlying array is null (default) + /// + public static ImmutableArray NullToEmpty(this ImmutableArray? array) + => array switch + { + null or { IsDefault: true } => ImmutableArray.Empty, + { } underlying => underlying + }; + /// /// Returns an array of distinct elements, preserving the order in the original array. /// If the array has no duplicates, the original array is returned. The original array must not be null. @@ -654,6 +677,17 @@ internal static ImmutableArray Concat(this ImmutableArray first, Immuta return builder.ToImmutableAndFree(); } + internal static ImmutableArray Concat(this ImmutableArray first, ImmutableArray second, ImmutableArray third, ImmutableArray fourth, ImmutableArray fifth) + { + var builder = ArrayBuilder.GetInstance(first.Length + second.Length + third.Length + fourth.Length + fifth.Length); + builder.AddRange(first); + builder.AddRange(second); + builder.AddRange(third); + builder.AddRange(fourth); + builder.AddRange(fifth); + return builder.ToImmutableAndFree(); + } + internal static ImmutableArray Concat(this ImmutableArray first, T second) { return first.Add(second); diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs index 474b805df818d..46a672fe5d9c3 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs @@ -293,6 +293,8 @@ private static IEnumerable GetGeneratorSupportedLanguages(PEModule peMod } } + // https://github.com/dotnet/roslyn/issues/53994 tracks re-enabling nullable and fixing this method +#nullable disable private static IEnumerable ReadLanguagesFromAttribute(ref BlobReader argsReader) { if (argsReader.Length > 4) @@ -321,6 +323,8 @@ private static IEnumerable ReadLanguagesFromAttribute(ref BlobReader arg return SpecializedCollections.EmptyEnumerable(); } +#nullable enable + private static ISourceGenerator? CoerceGeneratorType(object? generator) { if (generator is IIncrementalGenerator incrementalGenerator) diff --git a/src/Compilers/Core/Portable/InternalUtilities/SpanUtilities.cs b/src/Compilers/Core/Portable/InternalUtilities/SpanUtilities.cs index 78b7a1d66c43d..0c5a1641d1090 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/SpanUtilities.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/SpanUtilities.cs @@ -20,5 +20,18 @@ public static bool All(this ReadOnlySpan span, TPara return true; } + + public static bool All(this ReadOnlySpan span, Func predicate) + { + foreach (var e in span) + { + if (!predicate(e)) + { + return false; + } + } + + return true; + } } } diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index b6c1f6ac999d0..b4252161e3dbe 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Metadata; @@ -83,8 +84,9 @@ internal sealed class PEModule : IDisposable // algorithm. private readonly CryptographicHashProvider _hashesOpt; +#nullable enable private delegate bool AttributeValueExtractor(out T value, ref BlobReader sigReader); - private static readonly AttributeValueExtractor s_attributeStringValueExtractor = CrackStringInAttributeValue; + private static readonly AttributeValueExtractor s_attributeStringValueExtractor = CrackStringInAttributeValue; private static readonly AttributeValueExtractor s_attributeStringAndIntValueExtractor = CrackStringAndIntInAttributeValue; private static readonly AttributeValueExtractor s_attributeBooleanValueExtractor = CrackBooleanInAttributeValue; private static readonly AttributeValueExtractor s_attributeByteValueExtractor = CrackByteInAttributeValue; @@ -95,34 +97,35 @@ internal sealed class PEModule : IDisposable private static readonly AttributeValueExtractor s_decimalValueInDecimalConstantAttributeExtractor = CrackDecimalInDecimalConstantAttribute; private static readonly AttributeValueExtractor> s_attributeBoolArrayValueExtractor = CrackBoolArrayInAttributeValue; private static readonly AttributeValueExtractor> s_attributeByteArrayValueExtractor = CrackByteArrayInAttributeValue; - private static readonly AttributeValueExtractor> s_attributeStringArrayValueExtractor = CrackStringArrayInAttributeValue; - private static readonly AttributeValueExtractor s_attributeDeprecatedDataExtractor = CrackDeprecatedAttributeData; + private static readonly AttributeValueExtractor> s_attributeStringArrayValueExtractor = CrackStringArrayInAttributeValue; + private static readonly AttributeValueExtractor s_attributeDeprecatedDataExtractor = CrackDeprecatedAttributeData; private static readonly AttributeValueExtractor s_attributeBoolAndStringArrayValueExtractor = CrackBoolAndStringArrayInAttributeValue; private static readonly AttributeValueExtractor s_attributeBoolAndStringValueExtractor = CrackBoolAndStringInAttributeValue; internal struct BoolAndStringArrayData { - public BoolAndStringArrayData(bool sense, ImmutableArray strings) + public BoolAndStringArrayData(bool sense, ImmutableArray strings) { Sense = sense; Strings = strings; } public readonly bool Sense; - public readonly ImmutableArray Strings; + public readonly ImmutableArray Strings; } internal struct BoolAndStringData { - public BoolAndStringData(bool sense, string @string) + public BoolAndStringData(bool sense, string? @string) { Sense = sense; String = @string; } public readonly bool Sense; - public readonly string String; + public readonly string? String; } +#nullable disable // 'ignoreAssemblyRefs' is used by the EE only, when debugging // .NET Native, where the corlib may have assembly references @@ -1002,6 +1005,11 @@ internal bool HasCodeAnalysisEmbeddedAttribute(EntityHandle token) return FindTargetAttribute(token, AttributeDescription.CodeAnalysisEmbeddedAttribute).HasValue; } + internal bool HasInterpolatedStringHandlerAttribute(EntityHandle token) + { + return FindTargetAttribute(token, AttributeDescription.InterpolatedStringHandlerAttribute).HasValue; + } + internal bool HasDefaultMemberAttribute(EntityHandle token, out string memberName) { return HasStringValuedAttribute(token, AttributeDescription.DefaultMemberAttribute, out memberName); @@ -1181,6 +1189,31 @@ internal ObsoleteAttributeData TryGetDeprecatedOrExperimentalOrObsoleteAttribute return UnmanagedCallersOnlyAttributeData.Create(unmanagedConventionTypes); } + + internal (ImmutableArray Names, bool FoundAttribute) GetInterpolatedStringHandlerArgumentAttributeValues(EntityHandle token) + { + var targetAttribute = FindTargetAttribute(token, AttributeDescription.InterpolatedStringHandlerArgumentAttribute); + if (!targetAttribute.HasValue) + { + return (default, false); + } + + Debug.Assert(AttributeDescription.InterpolatedStringHandlerArgumentAttribute.Signatures.Length == 2); + Debug.Assert(targetAttribute.SignatureIndex is 0 or 1); + if (targetAttribute.SignatureIndex == 0) + { + if (TryExtractStringValueFromAttribute(targetAttribute.Handle, out string? paramName)) + { + return (ImmutableArray.Create(paramName), true); + } + } + else if (TryExtractStringArrayValueFromAttribute(targetAttribute.Handle, out var paramNames)) + { + return (paramNames.NullToEmpty(), true); + } + + return (default, true); + } #nullable disable internal bool HasMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(EntityHandle token, AttributeDescription description, out bool when) @@ -1609,7 +1642,8 @@ private bool TryExtractTypeLibTypeFromAttribute(AttributeInfo info, out Cci.Type return false; } - internal bool TryExtractStringValueFromAttribute(CustomAttributeHandle handle, out string value) +#nullable enable + internal bool TryExtractStringValueFromAttribute(CustomAttributeHandle handle, out string? value) { return TryExtractValueFromAttribute(handle, out value, s_attributeStringValueExtractor); } @@ -1627,11 +1661,11 @@ private bool TryExtractDecimalValueFromDecimalConstantAttribute(CustomAttributeH private struct StringAndInt { - public string StringValue; + public string? StringValue; public int IntValue; } - private bool TryExtractStringAndIntValueFromAttribute(CustomAttributeHandle handle, out string stringValue, out int intValue) + private bool TryExtractStringAndIntValueFromAttribute(CustomAttributeHandle handle, out string? stringValue, out int intValue) { StringAndInt data; var result = TryExtractValueFromAttribute(handle, out data, s_attributeStringAndIntValueExtractor); @@ -1650,12 +1684,12 @@ private bool TryExtractByteArrayValueFromAttribute(CustomAttributeHandle handle, return TryExtractValueFromAttribute(handle, out value, s_attributeByteArrayValueExtractor); } - private bool TryExtractStringArrayValueFromAttribute(CustomAttributeHandle handle, out ImmutableArray value) + private bool TryExtractStringArrayValueFromAttribute(CustomAttributeHandle handle, out ImmutableArray value) { return TryExtractValueFromAttribute(handle, out value, s_attributeStringArrayValueExtractor); } - private bool TryExtractValueFromAttribute(CustomAttributeHandle handle, out T value, AttributeValueExtractor valueExtractor) + private bool TryExtractValueFromAttribute(CustomAttributeHandle handle, out T? value, AttributeValueExtractor valueExtractor) { Debug.Assert(!handle.IsNil); @@ -1685,6 +1719,7 @@ private bool TryExtractValueFromAttribute(CustomAttributeHandle handle, out T value = default(T); return false; } +#nullable disable internal bool HasStringValuedAttribute(EntityHandle token, AttributeDescription description, out string value) { @@ -1813,9 +1848,8 @@ private static (string? diagnosticId, string? urlFormat) CrackObsoleteProperties return (diagnosticId, urlFormat); } -#nullable disable - private static bool CrackDeprecatedAttributeData(out ObsoleteAttributeData value, ref BlobReader sig) + private static bool CrackDeprecatedAttributeData([NotNullWhen(true)] out ObsoleteAttributeData? value, ref BlobReader sig) { StringAndInt args; if (CrackStringAndIntInAttributeValue(out args, ref sig)) @@ -1836,7 +1870,7 @@ private static bool CrackStringAndIntInAttributeValue(out StringAndInt value, re CrackIntInAttributeValue(out value.IntValue, ref sig); } - internal static bool CrackStringInAttributeValue(out string value, ref BlobReader sig) + internal static bool CrackStringInAttributeValue(out string? value, ref BlobReader sig) { try { @@ -1864,12 +1898,12 @@ internal static bool CrackStringInAttributeValue(out string value, ref BlobReade } } - internal static bool CrackStringArrayInAttributeValue(out ImmutableArray value, ref BlobReader sig) + internal static bool CrackStringArrayInAttributeValue(out ImmutableArray value, ref BlobReader sig) { if (sig.RemainingBytes >= 4) { uint arrayLen = sig.ReadUInt32(); - var stringArray = new string[arrayLen]; + var stringArray = new string?[arrayLen]; for (int i = 0; i < arrayLen; i++) { if (!CrackStringInAttributeValue(out stringArray[i], ref sig)) @@ -1883,14 +1917,14 @@ internal static bool CrackStringArrayInAttributeValue(out ImmutableArray return true; } - value = default(ImmutableArray); + value = default; return false; } internal static bool CrackBoolAndStringArrayInAttributeValue(out BoolAndStringArrayData value, ref BlobReader sig) { if (CrackBooleanInAttributeValue(out bool sense, ref sig) && - CrackStringArrayInAttributeValue(out ImmutableArray strings, ref sig)) + CrackStringArrayInAttributeValue(out ImmutableArray strings, ref sig)) { value = new BoolAndStringArrayData(sense, strings); return true; @@ -1903,7 +1937,7 @@ internal static bool CrackBoolAndStringArrayInAttributeValue(out BoolAndStringAr internal static bool CrackBoolAndStringInAttributeValue(out BoolAndStringData value, ref BlobReader sig) { if (CrackBooleanInAttributeValue(out bool sense, ref sig) && - CrackStringInAttributeValue(out string @string, ref sig)) + CrackStringInAttributeValue(out string? @string, ref sig)) { value = new BoolAndStringData(sense, @string); return true; @@ -2039,6 +2073,7 @@ internal static bool CrackByteArrayInAttributeValue(out ImmutableArray val value = default(ImmutableArray); return false; } +#nullable disable internal struct AttributeInfo { diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 80be5b90c7f02..f61124b1be1b6 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6352,8 +6352,8 @@ public override IOperation VisitInterpolatedString(IInterpolatedStringOperation IOperation? rewrittenFormatString; if (interpolation.FormatString != null) { - Debug.Assert(interpolation.FormatString.Kind == OperationKind.Literal); - rewrittenFormatString = VisitLiteral((ILiteralOperation)interpolation.FormatString, captureIdForResult: null); + Debug.Assert(interpolation.FormatString is ILiteralOperation or IConversionOperation { Operand: ILiteralOperation }); + rewrittenFormatString = VisitRequired(interpolation.FormatString, argument: null); } else { @@ -6367,8 +6367,8 @@ public override IOperation VisitInterpolatedString(IInterpolatedStringOperation else { var interpolatedStringText = (IInterpolatedStringTextOperation)element; - Debug.Assert(interpolatedStringText.Text.Kind == OperationKind.Literal); - var rewrittenInterpolationText = VisitLiteral((ILiteralOperation)interpolatedStringText.Text, captureIdForResult: null); + Debug.Assert(interpolatedStringText.Text is ILiteralOperation or IConversionOperation { Operand: ILiteralOperation }); + var rewrittenInterpolationText = VisitRequired(interpolatedStringText.Text, argument: null); rewrittenElement = new InterpolatedStringTextOperation(rewrittenInterpolationText, semanticModel: null, element.Syntax, IsImplicit(element)); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/DriverStateTable.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/DriverStateTable.cs index 080e57b458756..5ee231f12bd13 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/DriverStateTable.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/DriverStateTable.cs @@ -7,17 +7,18 @@ using System.Diagnostics; using System.Linq; using System.Threading; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis { internal sealed class DriverStateTable { - private readonly ImmutableDictionary _tables; + private readonly ImmutableSegmentedDictionary _tables; - internal static DriverStateTable Empty { get; } = new DriverStateTable(ImmutableDictionary.Empty); + internal static DriverStateTable Empty { get; } = new DriverStateTable(ImmutableSegmentedDictionary.Empty); - private DriverStateTable(ImmutableDictionary tables) + private DriverStateTable(ImmutableSegmentedDictionary tables) { _tables = tables; } @@ -33,7 +34,7 @@ public NodeStateTable GetStateTableOrEmpty(object input) public sealed class Builder { - private readonly ImmutableDictionary.Builder _tableBuilder = ImmutableDictionary.CreateBuilder(); + private readonly ImmutableSegmentedDictionary.Builder _tableBuilder = ImmutableSegmentedDictionary.CreateBuilder(); private readonly ImmutableArray _syntaxInputNodes; private readonly ImmutableDictionary.Builder _syntaxExceptions = ImmutableDictionary.CreateBuilder(); private readonly DriverStateTable _previousTable; diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxInputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxInputNode.cs index bf63515a1216f..ad087d19911bb 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxInputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/ISyntaxInputNode.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading; +using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis { @@ -18,6 +18,6 @@ internal interface ISyntaxInputBuilder void VisitTree(SyntaxNode root, EntryState state, SemanticModel? model, CancellationToken cancellationToken); - void SaveStateAndFree(ImmutableDictionary.Builder tables); + void SaveStateAndFree(ImmutableSegmentedDictionary.Builder tables); } } diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxInputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxInputNode.cs index 914674882a436..650057e4c146c 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxInputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxInputNode.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Threading; +using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis { @@ -54,7 +55,7 @@ public Builder(SyntaxInputNode owner, DriverStateTable table) public ISyntaxInputNode SyntaxInputNode { get => _owner; } - public void SaveStateAndFree(ImmutableDictionary.Builder tables) + public void SaveStateAndFree(ImmutableSegmentedDictionary.Builder tables) { tables[_owner._filterKey] = _filterTable.ToImmutableAndFree(); tables[_owner] = _transformTable.ToImmutableAndFree(); diff --git a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverInputNode.cs b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverInputNode.cs index d6b213982fc9e..df96b79503fd8 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverInputNode.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/Nodes/SyntaxReceiverInputNode.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Threading; +using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -65,7 +65,7 @@ public Builder(SyntaxReceiverInputNode owner, DriverStateTable driverStateTable) public ISyntaxInputNode SyntaxInputNode { get => _owner; } - public void SaveStateAndFree(ImmutableDictionary.Builder tables) + public void SaveStateAndFree(ImmutableSegmentedDictionary.Builder tables) { _nodeStateTable.AddEntry(_receiver, EntryState.Modified); tables[_owner] = _nodeStateTable.ToImmutableAndFree(); diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs index a6d8f011f4849..fb9f994739dd6 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/AttributeDescription.cs @@ -340,6 +340,7 @@ static AttributeDescription() private static readonly byte[][] s_signaturesOfNullableAttribute = { s_signature_HasThis_Void_Byte, s_signature_HasThis_Void_SzArray_Byte }; private static readonly byte[][] s_signaturesOfNullableContextAttribute = { s_signature_HasThis_Void_Byte }; private static readonly byte[][] s_signaturesOfNativeIntegerAttribute = { s_signature_HasThis_Void, s_signature_HasThis_Void_SzArray_Boolean }; + private static readonly byte[][] s_signaturesOfInterpolatedStringArgumentAttribute = { s_signature_HasThis_Void_String, s_signature_HasThis_Void_SzArray_String }; // early decoded attributes: internal static readonly AttributeDescription OptionalAttribute = new AttributeDescription("System.Runtime.InteropServices", "OptionalAttribute", s_signatures_HasThis_Void_Only); @@ -474,5 +475,7 @@ static AttributeDescription() internal static readonly AttributeDescription NativeIntegerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "NativeIntegerAttribute", s_signaturesOfNativeIntegerAttribute); internal static readonly AttributeDescription ModuleInitializerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "ModuleInitializerAttribute", s_signatures_HasThis_Void_Only); internal static readonly AttributeDescription UnmanagedCallersOnlyAttribute = new AttributeDescription("System.Runtime.InteropServices", "UnmanagedCallersOnlyAttribute", s_signatures_HasThis_Void_Only); + internal static readonly AttributeDescription InterpolatedStringHandlerAttribute = new AttributeDescription("System.Runtime.CompilerServices", "InterpolatedStringHandlerAttribute", s_signatures_HasThis_Void_Only); + internal static readonly AttributeDescription InterpolatedStringHandlerArgumentAttribute = new AttributeDescription("System.Runtime.CompilerServices", "InterpolatedStringHandlerArgumentAttribute", s_signaturesOfInterpolatedStringArgumentAttribute); } } diff --git a/src/Compilers/Core/Portable/Symbols/Attributes/CommonTypeEarlyWellKnownAttributeData.cs b/src/Compilers/Core/Portable/Symbols/Attributes/CommonTypeEarlyWellKnownAttributeData.cs index 367c95805be02..392f5bdade81e 100644 --- a/src/Compilers/Core/Portable/Symbols/Attributes/CommonTypeEarlyWellKnownAttributeData.cs +++ b/src/Compilers/Core/Portable/Symbols/Attributes/CommonTypeEarlyWellKnownAttributeData.cs @@ -4,19 +4,15 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// /// Information decoded from early well-known custom attributes applied on a type. /// - internal class CommonTypeEarlyWellKnownAttributeData : EarlyWellKnownAttributeData + internal abstract class CommonTypeEarlyWellKnownAttributeData : EarlyWellKnownAttributeData { #region AttributeUsageAttribute private AttributeUsageInfo _attributeUsageInfo = AttributeUsageInfo.Null; diff --git a/src/Compilers/Core/Portable/SynthesizedLocalKind.cs b/src/Compilers/Core/Portable/SynthesizedLocalKind.cs index c3ec4661539c3..755fe4890c9ba 100644 --- a/src/Compilers/Core/Portable/SynthesizedLocalKind.cs +++ b/src/Compilers/Core/Portable/SynthesizedLocalKind.cs @@ -232,6 +232,12 @@ internal enum SynthesizedLocalKind /// TODO: Avoid using lambdas and display classes for implementation of relaxation stubs and remove this kind. /// DelegateRelaxationReceiver = 0x101, + + /// + /// The interpoled string builder, when the interpolated string is being lowered through the builder pattern + /// or the interpolated string expression is being converted directly to System.String. + /// + InterpolatedStringHandler = 0x102, } internal static class SynthesizedLocalKindExtensions diff --git a/src/Compilers/Core/Portable/WellKnownMember.cs b/src/Compilers/Core/Portable/WellKnownMember.cs index 739671df27728..eb0becb8652d7 100644 --- a/src/Compilers/Core/Portable/WellKnownMember.cs +++ b/src/Compilers/Core/Portable/WellKnownMember.cs @@ -511,6 +511,8 @@ internal enum WellKnownMember System_Text_StringBuilder__AppendObject, System_Text_StringBuilder__ctor, + System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear, + Count // Remember to update the AllWellKnownTypeMembers tests when making changes here diff --git a/src/Compilers/Core/Portable/WellKnownMembers.cs b/src/Compilers/Core/Portable/WellKnownMembers.cs index d99831ae007a4..e0e7cab92196a 100644 --- a/src/Compilers/Core/Portable/WellKnownMembers.cs +++ b/src/Compilers/Core/Portable/WellKnownMembers.cs @@ -3504,6 +3504,13 @@ static WellKnownMembers() 0, // Arity 0, // Method Signature (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void, // Return Type + + // System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear + (byte)MemberFlags.Method, // Flags + (byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler - WellKnownType.ExtSentinel), // DeclaringTypeId + 0, // Arity + 0, // Method Signature + (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_String, // Return Type }; string[] allNames = new string[(int)WellKnownMember.Count] @@ -3944,6 +3951,7 @@ static WellKnownMembers() "Append", // System_Text_StringBuilder__AppendString "Append", // System_Text_StringBuilder__AppendObject ".ctor", // System_Text_StringBuilder__ctor + "ToStringAndClear", // System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear }; s_descriptors = MemberDescriptor.InitializeFromStream(new System.IO.MemoryStream(initializationBytes, writable: false), allNames); diff --git a/src/Compilers/Core/Portable/WellKnownTypes.cs b/src/Compilers/Core/Portable/WellKnownTypes.cs index 3260d3bf8e243..40b5d73a187a7 100644 --- a/src/Compilers/Core/Portable/WellKnownTypes.cs +++ b/src/Compilers/Core/Portable/WellKnownTypes.cs @@ -313,6 +313,8 @@ internal enum WellKnownType System_Text_StringBuilder, + System_Runtime_CompilerServices_DefaultInterpolatedStringHandler, + NextAvailable, // Remember to update the AllWellKnownTypes tests when making changes here @@ -620,6 +622,7 @@ internal static class WellKnownTypes "System.Runtime.InteropServices.OutAttribute", "System.Text.StringBuilder", + "System.Runtime.CompilerServices.DefaultInterpolatedStringHandler", }; private static readonly Dictionary s_nameToTypeIdMap = new Dictionary((int)Count); diff --git a/src/Compilers/Test/Core/Assert/AssertEx.cs b/src/Compilers/Test/Core/Assert/AssertEx.cs index 833239de41661..110bbffc1b1c6 100644 --- a/src/Compilers/Test/Core/Assert/AssertEx.cs +++ b/src/Compilers/Test/Core/Assert/AssertEx.cs @@ -226,14 +226,7 @@ public static void Equal( return; } - string assertMessage = GetAssertMessage(expected, actual, comparer, itemInspector, itemSeparator, expectedValueSourcePath, expectedValueSourceLine); - - if (message != null) - { - assertMessage = message + "\r\n" + assertMessage; - } - - Assert.True(false, assertMessage); + Assert.True(false, GetAssertMessage(expected, actual, comparer, message, itemInspector, itemSeparator, expectedValueSourcePath, expectedValueSourceLine)); } /// @@ -434,14 +427,10 @@ public static void SetEqual(IEnumerable expected, IEnumerable actual, I var result = expected.Count() == actual.Count() && expectedSet.SetEquals(actual); if (!result) { - if (string.IsNullOrEmpty(message)) - { - message = GetAssertMessage( - ToString(expected, itemSeparator, itemInspector), - ToString(actual, itemSeparator, itemInspector)); - } - - Assert.True(result, message); + Assert.True(result, GetAssertMessage( + ToString(expected, itemSeparator, itemInspector), + ToString(actual, itemSeparator, itemInspector), + prefix: message)); } } @@ -523,6 +512,7 @@ public static void NotNull(T @object, string message = null) public static void AssertEqualToleratingWhitespaceDifferences( string expected, string actual, + string message = null, bool escapeQuotes = true, [CallerFilePath] string expectedValueSourcePath = null, [CallerLineNumber] int expectedValueSourceLine = 0) @@ -532,7 +522,7 @@ public static void AssertEqualToleratingWhitespaceDifferences( if (normalizedExpected != normalizedActual) { - Assert.True(false, GetAssertMessage(expected, actual, escapeQuotes, expectedValueSourcePath, expectedValueSourceLine)); + Assert.True(false, GetAssertMessage(expected, actual, message, escapeQuotes, expectedValueSourcePath, expectedValueSourceLine)); } } @@ -597,15 +587,13 @@ internal static string NormalizeWhitespace(string input) return output.ToString(); } - public static string GetAssertMessage(string expected, string actual, bool escapeQuotes = false, string expectedValueSourcePath = null, int expectedValueSourceLine = 0) - { - return GetAssertMessage(DiffUtil.Lines(expected), DiffUtil.Lines(actual), escapeQuotes, expectedValueSourcePath, expectedValueSourceLine); - } + public static string GetAssertMessage(string expected, string actual, string prefix = null, bool escapeQuotes = false, string expectedValueSourcePath = null, int expectedValueSourceLine = 0) + => GetAssertMessage(DiffUtil.Lines(expected), DiffUtil.Lines(actual), prefix, escapeQuotes, expectedValueSourcePath, expectedValueSourceLine); - public static string GetAssertMessage(IEnumerable expected, IEnumerable actual, bool escapeQuotes, string expectedValueSourcePath = null, int expectedValueSourceLine = 0) + public static string GetAssertMessage(IEnumerable expected, IEnumerable actual, string prefix = null, bool escapeQuotes = false, string expectedValueSourcePath = null, int expectedValueSourceLine = 0) { Func itemInspector = escapeQuotes ? new Func(t => t.ToString().Replace("\"", "\"\"")) : null; - return GetAssertMessage(expected, actual, itemInspector: itemInspector, itemSeparator: "\r\n", expectedValueSourcePath: expectedValueSourcePath, expectedValueSourceLine: expectedValueSourceLine); + return GetAssertMessage(expected, actual, prefix: prefix, itemInspector: itemInspector, itemSeparator: "\r\n", expectedValueSourcePath: expectedValueSourcePath, expectedValueSourceLine: expectedValueSourceLine); } private static readonly string s_diffToolPath = Environment.GetEnvironmentVariable("ROSLYN_DIFFTOOL"); @@ -614,6 +602,7 @@ public static string GetAssertMessage( IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null, + string prefix = null, Func itemInspector = null, string itemSeparator = null, string expectedValueSourcePath = null, @@ -647,13 +636,20 @@ public static string GetAssertMessage( var actualString = string.Join(itemSeparator, actual.Select(itemInspector)); var message = new StringBuilder(); - message.AppendLine(); + + if (!string.IsNullOrEmpty(prefix)) + { + message.AppendLine(prefix); + message.AppendLine(); + } + message.AppendLine("Expected:"); message.AppendLine(expectedString); if (expected.Count() > 10) { message.AppendLine("... truncated ..."); } + message.AppendLine("Actual:"); message.AppendLine(actualString); message.AppendLine("Differences:"); diff --git a/src/Compilers/Test/Core/CommonTestBase.cs b/src/Compilers/Test/Core/CommonTestBase.cs index 6a48efbf362af..e92f2e5628e72 100644 --- a/src/Compilers/Test/Core/CommonTestBase.cs +++ b/src/Compilers/Test/Core/CommonTestBase.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.PortableExecutable; @@ -338,6 +339,16 @@ protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( return CreateVisualBasicCompilation(assemblyName, code, parseOptions, compilationOptions, referencedAssemblies, referencedCompilations: null); } + protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( + string[] files, + VisualBasic.VisualBasicParseOptions parseOptions = null, + VisualBasic.VisualBasicCompilationOptions compilationOptions = null, + string assemblyName = null, + IEnumerable referencedAssemblies = null) + { + return CreateVisualBasicCompilation(assemblyName, files, parseOptions, compilationOptions, referencedAssemblies, referencedCompilations: null); + } + protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( string assemblyName, string code, @@ -347,7 +358,19 @@ protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( IEnumerable referencedCompilations = null, Encoding encoding = null, string sourceFileName = null) + => CreateVisualBasicCompilation(assemblyName, new[] { code }, parseOptions, compilationOptions, referencedAssemblies, referencedCompilations, encoding, new[] { sourceFileName }); + + protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( + string assemblyName, + string[] files, + VisualBasic.VisualBasicParseOptions parseOptions = null, + VisualBasic.VisualBasicCompilationOptions compilationOptions = null, + IEnumerable referencedAssemblies = null, + IEnumerable referencedCompilations = null, + Encoding encoding = null, + string[] sourceFileNames = null) { + Debug.Assert(sourceFileNames == null || sourceFileNames.Length == files.Length); if (assemblyName == null) { assemblyName = GetUniqueName(); @@ -381,9 +404,14 @@ protected VisualBasic.VisualBasicCompilation CreateVisualBasicCompilation( AddReferencedCompilations(referencedCompilations, references); - var tree = VisualBasic.VisualBasicSyntaxTree.ParseText(code, options: parseOptions, encoding: encoding, path: sourceFileName); + var trees = new SyntaxTree[files.Length]; + for (int i = 0; i < files.Length; i++) + { + trees[i] = VisualBasic.VisualBasicSyntaxTree.ParseText(files[i], options: parseOptions, encoding: encoding, path: sourceFileNames?[i]); + } + - return VisualBasic.VisualBasicCompilation.Create(assemblyName, new[] { tree }, references, compilationOptions); + return VisualBasic.VisualBasicCompilation.Create(assemblyName, trees, references, compilationOptions); } private void AddReferencedCompilations(IEnumerable referencedCompilations, List references) diff --git a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs index 437a89d6ca233..0c2ee0e26dfeb 100644 --- a/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/OperationTreeVerifier.cs @@ -57,10 +57,9 @@ public static void Verify(string expectedOperationTree, string actualOperationTr { char[] newLineChars = Environment.NewLine.ToCharArray(); string actual = actualOperationTree.Trim(newLineChars); - actual = actual.Replace("\"", "\"\"").Replace(" \n", "\n").Replace(" \r", "\r"); + actual = actual.Replace(" \n", "\n").Replace(" \r", "\r"); expectedOperationTree = expectedOperationTree.Trim(newLineChars); expectedOperationTree = expectedOperationTree.Replace("\r\n", "\n").Replace(" \n", "\n").Replace("\n", Environment.NewLine); - expectedOperationTree = expectedOperationTree.Replace("\"", "\"\""); AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedOperationTree, actual); } @@ -217,22 +216,19 @@ private void LogConstant(Optional constant, string header = "Constant") } } - private static string ConstantToString(object constant, bool quoteString = true) + private static string ConstantToString(object constant) { switch (constant) { case null: return "null"; case string s: - if (quoteString) - { - return @"""" + s + @""""; - } - return s; + s = s.Replace("\"", "\"\""); + return @"""" + s + @""""; case IFormattable formattable: - return formattable.ToString(null, CultureInfo.InvariantCulture); + return formattable.ToString(null, CultureInfo.InvariantCulture).Replace("\"", "\"\""); default: - return constant.ToString(); + return constant.ToString().Replace("\"", "\"\""); } } @@ -1774,7 +1770,10 @@ public override void VisitInterpolatedStringText(IInterpolatedStringTextOperatio LogString(nameof(IInterpolatedStringTextOperation)); LogCommonPropertiesAndNewLine(operation); - Assert.Equal(OperationKind.Literal, operation.Text.Kind); + if (operation.Text.Kind != OperationKind.Literal) + { + Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.Text).Operand.Kind); + } Visit(operation.Text, "Text"); } @@ -1787,9 +1786,9 @@ public override void VisitInterpolation(IInterpolationOperation operation) Visit(operation.Alignment, "Alignment"); Visit(operation.FormatString, "FormatString"); - if (operation.FormatString != null) + if (operation.FormatString != null && operation.FormatString.Kind != OperationKind.Literal) { - Assert.Equal(OperationKind.Literal, operation.FormatString.Kind); + Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.FormatString).Operand.Kind); } } diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index 108e47582030b..40aa23c5b2db6 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -1105,7 +1105,10 @@ public override void VisitInterpolatedString(IInterpolatedStringOperation operat public override void VisitInterpolatedStringText(IInterpolatedStringTextOperation operation) { Assert.Equal(OperationKind.InterpolatedStringText, operation.Kind); - Assert.Equal(OperationKind.Literal, operation.Text.Kind); + if (operation.Text.Kind != OperationKind.Literal) + { + Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.Text).Operand.Kind); + } Assert.Same(operation.Text, operation.Children.Single()); } @@ -1120,7 +1123,10 @@ public override void VisitInterpolation(IInterpolationOperation operation) if (operation.FormatString != null) { - Assert.Equal(OperationKind.Literal, operation.FormatString.Kind); + if (operation.FormatString.Kind != OperationKind.Literal) + { + Assert.Equal(OperationKind.Literal, ((IConversionOperation)operation.FormatString).Operand.Kind); + } children = children.Concat(new[] { operation.FormatString }); } diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index 16c909826ab46..d566b2f431498 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -293,7 +293,7 @@ private CompilationVerifier VerifyILImpl( string source = null) { string actualIL = VisualizeIL(qualifiedMethodName, realIL, sequencePoints, source); - AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedIL, actualIL, escapeQuotes, callerPath, callerLine); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedIL, actualIL, message: null, escapeQuotes, callerPath, callerLine); return this; } diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 00de9e50f7780..e394fc7ea219d 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -796,7 +796,7 @@ internal CompilationVerifier CompileAndVerify( TargetFramework targetFramework = TargetFramework.Standard, Verification verify = Verification.Passes) { - options = options ?? TestOptions.ReleaseDll.WithOutputKind((expectedOutput != null) ? OutputKind.ConsoleApplication : OutputKind.DynamicallyLinkedLibrary); + options = options ?? (expectedOutput != null ? TestOptions.ReleaseExe : CheckForTopLevelStatements(source.GetSyntaxTrees(parseOptions))); var compilation = CreateCompilation(source, references, options, parseOptions, targetFramework, assemblyName: GetUniqueName()); return CompileAndVerify( compilation, @@ -1106,12 +1106,7 @@ private static CSharpCompilation CreateCompilationCore( { var syntaxTrees = source.GetSyntaxTrees(parseOptions, sourceFileName); - if (options == null) - { - bool hasTopLevelStatements = syntaxTrees.Any(s => s.GetRoot().ChildNodes().OfType().Any()); - - options = hasTopLevelStatements ? TestOptions.ReleaseExe : TestOptions.ReleaseDll; - } + options ??= CheckForTopLevelStatements(syntaxTrees); // Using single-threaded build if debugger attached, to simplify debugging. if (Debugger.IsAttached) @@ -1142,6 +1137,14 @@ private static CSharpCompilation CreateCompilationCore( return compilation; } + private static CSharpCompilationOptions CheckForTopLevelStatements(SyntaxTree[] syntaxTrees) + { + bool hasTopLevelStatements = syntaxTrees.Any(s => s.GetRoot().ChildNodes().OfType().Any()); + + var options = hasTopLevelStatements ? TestOptions.ReleaseExe : TestOptions.ReleaseDll; + return options; + } + private static void ValidateCompilation(Func createCompilationLambda) { CompilationExtensions.ValidateIOperations(createCompilationLambda); @@ -1842,7 +1845,7 @@ protected static string GetOperationTreeForTest(CSharpCompilation compilation, I } protected static string GetOperationTreeForTest( - string testSrc, + CSharpTestSource testSrc, CSharpCompilationOptions compilationOptions = null, CSharpParseOptions parseOptions = null, bool useLatestFrameworkReferences = false) @@ -1893,7 +1896,7 @@ protected static void VerifyFlowGraph(CSharpCompilation compilation, SyntaxNode } protected static void VerifyOperationTreeForTest( - string testSrc, + CSharpTestSource testSrc, string expectedOperationTree, CSharpCompilationOptions compilationOptions = null, CSharpParseOptions parseOptions = null, @@ -1928,7 +1931,7 @@ protected static void VerifyFlowGraphAndDiagnosticsForTest( } protected static void VerifyOperationTreeAndDiagnosticsForTest( - string testSrc, + CSharpTestSource testSrc, string expectedOperationTree, DiagnosticDescription[] expectedDiagnostics, CSharpCompilationOptions compilationOptions = null, @@ -1948,7 +1951,7 @@ protected static void VerifyOperationTreeAndDiagnosticsForTest( additionalOperationTreeVerifier); protected static void VerifyOperationTreeAndDiagnosticsForTest( - string testSrc, + CSharpTestSource testSrc, string expectedOperationTree, TargetFramework targetFramework, DiagnosticDescription[] expectedDiagnostics, @@ -1959,8 +1962,9 @@ protected static void VerifyOperationTreeAndDiagnosticsForTest( where TSyntaxNode : SyntaxNode { var compilation = CreateCompilation( - new[] { Parse(testSrc, filename: "file.cs", options: parseOptions) }, + testSrc, references, + parseOptions: parseOptions, options: compilationOptions, targetFramework: targetFramework); VerifyOperationTreeAndDiagnosticsForTest(compilation, expectedOperationTree, expectedDiagnostics, additionalOperationTreeVerifier); @@ -1985,7 +1989,7 @@ protected static void VerifyOperationTreeAndDiagnosticsForTest( } protected static void VerifyFlowGraphAndDiagnosticsForTest( - string testSrc, + CSharpTestSource testSrc, string expectedFlowGraph, DiagnosticDescription[] expectedDiagnostics, CSharpCompilationOptions compilationOptions = null, @@ -2005,7 +2009,7 @@ protected static void VerifyFlowGraphAndDiagnosticsForTest( } protected static void VerifyFlowGraphAndDiagnosticsForTest( - string testSrc, + CSharpTestSource testSrc, string expectedFlowGraph, DiagnosticDescription[] expectedDiagnostics, TargetFramework targetFramework, @@ -2015,8 +2019,9 @@ protected static void VerifyFlowGraphAndDiagnosticsForTest( where TSyntaxNode : SyntaxNode { var compilation = CreateCompilation( - new[] { Parse(testSrc, filename: "file.cs", options: parseOptions) }, + testSrc, references, + parseOptions: parseOptions, options: compilationOptions, targetFramework: targetFramework); VerifyFlowGraphAndDiagnosticsForTest(compilation, expectedFlowGraph, expectedDiagnostics); @@ -2042,7 +2047,7 @@ protected static MetadataReference VerifyOperationTreeAndDiagnosticsForTestWithI #region Span - protected static CSharpCompilation CreateCompilationWithSpan(SyntaxTree tree, CSharpCompilationOptions options = null) + protected static CSharpCompilation CreateCompilationWithSpan(CSharpTestSource tree, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) { var reference = CreateCompilation( SpanSource, @@ -2053,14 +2058,12 @@ protected static CSharpCompilation CreateCompilationWithSpan(SyntaxTree tree, CS var comp = CreateCompilation( tree, references: new[] { reference.EmitToImageReference() }, - options: options); + options: options, + parseOptions: parseOptions); return comp; } - protected static CSharpCompilation CreateCompilationWithSpan(string s, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) - => CreateCompilationWithSpan(SyntaxFactory.ParseSyntaxTree(s, options: parseOptions), options); - protected static CSharpCompilation CreateCompilationWithMscorlibAndSpan(string text, CSharpCompilationOptions options = null, CSharpParseOptions parseOptions = null) { var reference = CreateEmptyCompilation( @@ -2331,6 +2334,199 @@ protected static CSharpCompilation CreateCompilationWithIndexAndRangeAndSpan(CSh } #endregion + #region Interpolated string handlers + + internal static string GetInterpolatedStringHandlerDefinition(bool includeSpanOverloads, bool useDefaultParameters, bool useBoolReturns, string returnExpression = null, bool constructorBoolArg = false, bool constructorSuccessResult = true) + { + Debug.Assert(returnExpression == null || useBoolReturns); + + var builder = new StringBuilder(); + builder.AppendLine(@" +namespace System.Runtime.CompilerServices +{ + using System.Text; + public ref partial struct DefaultInterpolatedStringHandler + { + private readonly StringBuilder _builder; + public DefaultInterpolatedStringHandler(int literalLength, int formattedCount" + (constructorBoolArg ? ", out bool success" : "") + @") + { + _builder = new StringBuilder(); + " + (constructorBoolArg ? $"success = {(constructorSuccessResult ? "true" : "false")};" : "") + @" + } + public string ToStringAndClear() => _builder.ToString();"); + + appendSignature("AppendLiteral(string s)"); + appendBody(includeValue: false, includeAlignment: false, includeFormat: false, isSpan: false); + + if (useDefaultParameters) + { + appendSignature("AppendFormatted(T value, int alignment = 0, string format = null)"); + appendBody(includeValue: true, includeAlignment: true, includeFormat: true, isSpan: false); + appendSignature("AppendFormatted(object value, int alignment = 0, string format = null)"); + appendBody(includeValue: true, includeAlignment: true, includeFormat: true, isSpan: false); + appendSignature("AppendFormatted(string value, int alignment = 0, string format = null)"); + appendBody(includeValue: true, includeAlignment: true, includeFormat: true, isSpan: false); + } + else + { + appendNonDefaultVariantsWithGenericAndType("T", ""); + appendNonDefaultVariantsWithGenericAndType("object", generic: null); + appendNonDefaultVariantsWithGenericAndType("string", generic: null); + } + + if (includeSpanOverloads) + { + if (useDefaultParameters) + { + appendSignature("AppendFormatted(ReadOnlySpan value, int alignment = 0, string format = null)"); + appendBody(includeValue: true, includeAlignment: true, includeFormat: true, isSpan: true); + } + else + { + appendNonDefaultVariantsWithGenericAndType("ReadOnlySpan", generic: null, isSpan: true); + } + } + + builder.Append(@" + } +}"); + return builder.ToString(); + + void appendBody(bool includeValue, bool includeAlignment, bool includeFormat, bool isSpan) + { + if (includeValue) + { + builder.Append($@" + {{ + _builder.Append(""value:""); + _builder.Append(value{(isSpan ? "" : "?")}.ToString());"); + } + else + { + builder.Append(@" + { + _builder.Append(s);"); + } + + if (includeAlignment) + { + builder.Append(@" + _builder.Append("",alignment:""); + _builder.Append(alignment);"); + } + + if (includeFormat) + { + builder.Append(@" + _builder.Append("":format:""); + _builder.Append(format);"); + } + + builder.Append(@" + _builder.AppendLine();"); + + if (useBoolReturns) + { + builder.Append($@" + return {returnExpression ?? "true"};"); + } + + builder.AppendLine(@" + }"); + } + + void appendSignature(string nameAndParams) + { + builder.Append(@$" + public {(useBoolReturns ? "bool" : "void")} {nameAndParams}"); + } + + void appendNonDefaultVariantsWithGenericAndType(string type, string generic, bool isSpan = false) + { + appendSignature($"AppendFormatted{generic}({type} value)"); + appendBody(includeValue: true, includeAlignment: false, includeFormat: false, isSpan); + appendSignature($"AppendFormatted{generic}({type} value, int alignment)"); + appendBody(includeValue: true, includeAlignment: true, includeFormat: false, isSpan); + appendSignature($"AppendFormatted{generic}({type} value, string format)"); + appendBody(includeValue: true, includeAlignment: false, includeFormat: true, isSpan); + appendSignature($"AppendFormatted{generic}({type} value, int alignment, string format)"); + appendBody(includeValue: true, includeAlignment: true, includeFormat: true, isSpan); + } + } + + internal const string InterpolatedStringHandlerAttribute = @" +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)] + public sealed class InterpolatedStringHandlerAttribute : Attribute + { + public InterpolatedStringHandlerAttribute() + { + } + } +} +"; + + internal static string GetInterpolatedStringCustomHandlerType(string name, string type, bool useBoolReturns, bool includeOneTimeHelpers = true, bool includeTrailingOutConstructorParameter = false) + { + var returnType = useBoolReturns ? "bool" : "void"; + var returnStatement = useBoolReturns ? "return true;" : "return;"; + + var cultureInfoHandler = @" +public class CultureInfoNormalizer +{ + public static void Normalize() + { + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + } +} +"; + + var nameWithGenericsTrimmed = name.IndexOf("<") is not -1 and var index ? name[..index] : name; + + return (includeOneTimeHelpers ? "using System.Globalization;\n" : "") + @" +using System.Text; +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public " + type + " " + name + @" +{ + private readonly StringBuilder _builder; + public " + nameWithGenericsTrimmed + @"(int literalLength, int formattedCount" + (includeTrailingOutConstructorParameter ? ", out bool success" : "") + @") + { + " + (includeTrailingOutConstructorParameter ? "success = true;" : "") + @" + _builder = new(); + } + public " + returnType + @" AppendLiteral(string literal) + { + _builder.AppendLine(""literal:"" + literal); + " + returnStatement + @" + } + public " + returnType + @" AppendFormatted(object o, int alignment = 0, string format = null) + { + _builder.AppendLine(""value:"" + o?.ToString()); + _builder.AppendLine(""alignment:"" + alignment.ToString()); + _builder.AppendLine(""format:"" + format); + " + returnStatement + @" + } + public override string ToString() => _builder.ToString(); +} +" + (includeOneTimeHelpers ? InterpolatedStringHandlerAttribute + cultureInfoHandler : ""); + } + + internal const string InterpolatedStringHandlerArgumentAttribute = @" +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + public sealed class InterpolatedStringHandlerArgumentAttribute : Attribute + { + public InterpolatedStringHandlerArgumentAttribute(string argument) => Arguments = new string[] { argument }; + public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) => Arguments = arguments; + public string[] Arguments { get; } + } +} +"; + + #endregion + #region Theory Helpers public static IEnumerable NonNullTypesTrueAndFalseDebugDll @@ -2356,6 +2552,18 @@ public static IEnumerable NonNullTypesTrueAndFalseReleaseDll }; } } + + public static IEnumerable FileScopedOrBracedNamespace + { + get + { + return new List() + { + new object[] { ";", "" }, + new object[] { "{", "}" } + }; + } + } #endregion protected static readonly string s_IAsyncEnumerable = @" diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs index 517457f1f7d67..bbd6b765e3393 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestSource.cs @@ -36,6 +36,12 @@ public SyntaxTree[] GetSyntaxTrees(CSharpParseOptions parseOptions, string sourc case string[] sources: Debug.Assert(string.IsNullOrEmpty(sourceFileName)); return CSharpTestBase.Parse(parseOptions, sources); + case (string source, string fileName): + Debug.Assert(string.IsNullOrEmpty(sourceFileName)); + return new[] { CSharpTestBase.Parse(source, fileName, parseOptions) }; + case (string Source, string FileName)[] sources: + Debug.Assert(string.IsNullOrEmpty(sourceFileName)); + return sources.Select(source => CSharpTestBase.Parse(source.Source, source.FileName, parseOptions)).ToArray(); case SyntaxTree tree: Debug.Assert(parseOptions == null); Debug.Assert(string.IsNullOrEmpty(sourceFileName)); @@ -55,6 +61,8 @@ public SyntaxTree[] GetSyntaxTrees(CSharpParseOptions parseOptions, string sourc public static implicit operator CSharpTestSource(string source) => new CSharpTestSource(source); public static implicit operator CSharpTestSource(string[] source) => new CSharpTestSource(source); + public static implicit operator CSharpTestSource((string Source, string FileName) source) => new CSharpTestSource(source); + public static implicit operator CSharpTestSource((string Source, string FileName)[] source) => new CSharpTestSource(source); public static implicit operator CSharpTestSource(SyntaxTree source) => new CSharpTestSource(source); public static implicit operator CSharpTestSource(SyntaxTree[] source) => new CSharpTestSource(source); public static implicit operator CSharpTestSource(List source) => new CSharpTestSource(source.ToArray()); diff --git a/src/Compilers/Test/Utilities/CSharp/Extensions.cs b/src/Compilers/Test/Utilities/CSharp/Extensions.cs index a1485cb2efc0f..ddf6c8d3a5648 100644 --- a/src/Compilers/Test/Utilities/CSharp/Extensions.cs +++ b/src/Compilers/Test/Utilities/CSharp/Extensions.cs @@ -611,7 +611,7 @@ declaration is ForEachStatementSyntax || declaration is JoinIntoClauseSyntax || declaration is LabeledStatementSyntax || declaration is MemberDeclarationSyntax || - declaration is NamespaceDeclarationSyntax || + declaration is BaseNamespaceDeclarationSyntax || declaration is ParameterSyntax || declaration is QueryClauseSyntax || declaration is QueryContinuationSyntax || diff --git a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs index 9dcee21970d7c..2628a3f0037eb 100644 --- a/src/Compilers/Test/Utilities/CSharp/TestOptions.cs +++ b/src/Compilers/Test/Utilities/CSharp/TestOptions.cs @@ -41,6 +41,7 @@ public static class TestOptions public static readonly CSharpParseOptions WithoutCovariantReturns = Regular.WithLanguageVersion(LanguageVersion.CSharp8); public static readonly CSharpParseOptions RegularWithExtendedPartialMethods = RegularPreview; + public static readonly CSharpParseOptions RegularWithFileScopedNamespaces = Regular.WithLanguageVersion(MessageID.IDS_FeatureFileScopedNamespace.RequiredVersion()); private static readonly SmallDictionary s_experimentalFeatures = new SmallDictionary { }; public static readonly CSharpParseOptions ExperimentalParseOptions = diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb index 17698ce2f4cb9..9c53a3fe62931 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/WellKnownTypeValidationTests.vb @@ -542,7 +542,8 @@ End Namespace WellKnownType.Microsoft_CodeAnalysis_EmbeddedAttribute, WellKnownType.System_Runtime_CompilerServices_SwitchExpressionException, WellKnownType.System_Runtime_CompilerServices_NativeIntegerAttribute, - WellKnownType.System_Runtime_CompilerServices_IsExternalInit + WellKnownType.System_Runtime_CompilerServices_IsExternalInit, + WellKnownType.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -607,7 +608,8 @@ End Namespace WellKnownType.Microsoft_CodeAnalysis_EmbeddedAttribute, WellKnownType.System_Runtime_CompilerServices_SwitchExpressionException, WellKnownType.System_Runtime_CompilerServices_NativeIntegerAttribute, - WellKnownType.System_Runtime_CompilerServices_IsExternalInit + WellKnownType.System_Runtime_CompilerServices_IsExternalInit, + WellKnownType.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler ' Not available on all platforms. Continue For Case WellKnownType.ExtSentinel @@ -692,7 +694,8 @@ End Namespace WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T, WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctor, - WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags + WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags, + WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear ' Not available yet, but will be in upcoming release. Continue For Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, @@ -833,7 +836,8 @@ End Namespace WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetSubArray_T, WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctor, - WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags + WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags, + WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear ' Not available yet, but will be in upcoming release. Continue For Case WellKnownMember.Microsoft_CodeAnalysis_Runtime_Instrumentation__CreatePayloadForMethodsSpanningSingleFile, diff --git a/src/Dependencies/PooledObjects/ArrayBuilder.cs b/src/Dependencies/PooledObjects/ArrayBuilder.cs index 5dc07827daab8..e1f19c559fe98 100644 --- a/src/Dependencies/PooledObjects/ArrayBuilder.cs +++ b/src/Dependencies/PooledObjects/ArrayBuilder.cs @@ -491,11 +491,29 @@ public void AddRange(ArrayBuilder items) _builder.AddRange(items._builder); } + public void AddRange(ArrayBuilder items, Func selector) + { + foreach (var item in items) + { + _builder.Add(selector(item)); + } + } + public void AddRange(ArrayBuilder items) where U : T { _builder.AddRange(items._builder); } + public void AddRange(ArrayBuilder items, int start, int length) where U : T + { + Debug.Assert(start >= 0 && length >= 0); + Debug.Assert(start + length <= items.Count); + for (int i = start, end = start + length; i < end; i++) + { + Add(items[i]); + } + } + public void AddRange(ImmutableArray items) { _builder.AddRange(items); diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.Cascading.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.Cascading.cs index a996a41288d50..72efbf595644d 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.Cascading.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.Cascading.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.ChangeSignature; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities.ChangeSignature; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ChangeSignature @@ -412,5 +411,26 @@ public override int M(string x, int newIntegerParameter, int y) }"; await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/53091"), Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task AddParameter_Cascade_Record() + { + var markup = @" +record $$BaseR(int A, int B); + +record DerivedR() : BaseR(0, 1);"; + var permutation = new AddedParameterOrExistingIndex[] + { + new(1), + new(new AddedParameter(null, "int", "C", CallSiteKind.Value, "3"), "int"), + new(0) + }; + var updatedCode = @" +record BaseR(int B, int C, int A); + +record DerivedR() : BaseR(1, 3, 0);"; + + await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); + } } } diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.cs index 136592635bd9a..11fecb5f73ea8 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/AddParameterTests.cs @@ -1261,5 +1261,39 @@ public void M() }"; await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); } + + [Fact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + [WorkItem(44558, "https://github.com/dotnet/roslyn/issues/44558")] + public async Task AddParameters_Record() + { + var markup = @" +/// +/// +/// +record $$R(int First, int Second, int Third) +{ + static R M() => new R(1, 2, 3); +} +"; + var updatedSignature = new AddedParameterOrExistingIndex[] + { + new(0), + new(2), + new(1), + new(new AddedParameter(null, "int", "Forth", CallSiteKind.Value, "12345"), "System.Int32") + }; + var updatedCode = @" +/// +/// +/// +/// +record R(int First, int Third, int Second, int Forth) +{ + static R M() => new R(1, 3, 2, 12345); +} +"; + + await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: updatedSignature, expectedUpdatedInvocationDocumentCode: updatedCode); + } } } diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs index 90bf56923b963..0fb298efc3e11 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs @@ -951,5 +951,29 @@ class D : C, I await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); } + + [Fact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task ReorderParamTagsInDocComments_Record() + { + var markup = @" +/// +/// +/// +record $$R(int A, int B, int C) +{ + public static R Instance = new(0, 1, 2); +}"; + var permutation = new[] { 2, 1, 0 }; + var updatedCode = @" +/// +/// +/// +record R(int C, int B, int A) +{ + public static R Instance = new(2, 1, 0); +}"; + + await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); + } } } diff --git a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs index a55f7f03b6f95..e003555af544b 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SemanticClassifierTests.cs @@ -4439,5 +4439,17 @@ await TestAsync( testHost, RecordStruct("R")); } + + [Theory] + [CombinatorialData] + public async Task BasicFileScopedNamespaceClassification(TestHost testHost) + { + await TestAsync( +@"namespace NS; + +class C { }", + testHost, + Namespace("NS")); + } } } diff --git a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs index 230bc23d1b54e..ff27fdbe06be6 100644 --- a/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/TotalClassifierTests.cs @@ -2151,5 +2151,23 @@ void Goo(object o) Punctuation.CloseCurly, Punctuation.CloseCurly); } + + [Theory] + [CombinatorialData] + public async Task BasicFileScopedNamespaceClassification(TestHost testHost) + { + await TestAsync( +@"namespace NS; + +class C { }", + testHost, + Keyword("namespace"), + Namespace("NS"), + Punctuation.Semicolon, + Keyword("class"), + Class("C"), + Punctuation.OpenCurly, + Punctuation.CloseCurly); + } } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/XmlDocumentationCommentCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/XmlDocumentationCommentCompletionProviderTests.cs index 8a0a6aa11c25a..52cbea5299e13 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/XmlDocumentationCommentCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/XmlDocumentationCommentCompletionProviderTests.cs @@ -1012,5 +1012,27 @@ static void Goo() await VerifyItemExistsAsync(text, "term"); await VerifyItemExistsAsync(text, "description"); } + + [WorkItem(52738, "https://github.com/dotnet/roslyn/issues/52738")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task RecordParam() + { + await VerifyItemsExistAsync(@" +/// $$ +public record Goo(string MyParameter); +", "param name=\"MyParameter\"", "typeparam name=\"T\""); + } + + [WorkItem(52738, "https://github.com/dotnet/roslyn/issues/52738")] + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + public async Task RecordParamRef() + { + await VerifyItemsExistAsync(@" +/// +/// $$ +/// +public record Goo(string MyParameter); +", "paramref name=\"MyParameter\"", "typeparamref name=\"T\""); + } } } diff --git a/src/EditorFeatures/CSharpTest/Debugging/LocationInfoGetterTests.cs b/src/EditorFeatures/CSharpTest/Debugging/LocationInfoGetterTests.cs index d83091ddd94c6..dbb181a8ebce3 100644 --- a/src/EditorFeatures/CSharpTest/Debugging/LocationInfoGetterTests.cs +++ b/src/EditorFeatures/CSharpTest/Debugging/LocationInfoGetterTests.cs @@ -68,6 +68,24 @@ void Method() }", "Namespace.Class.Method()", 2); } + [Fact, Trait(Traits.Feature, Traits.Features.DebuggingLocationName)] + [WorkItem(49000, "https://github.com/dotnet/roslyn/issues/49000")] + public async Task TestFileScopedNamespace() + { + // This test behavior is incorrect. This should be Namespace.Class.Method. + // See the associated WorkItem for details. + await TestAsync( +@"namespace Namespace; + +class Class +{ + void Method() + { + }$$ +} +", "Class.Method()", 2); + } + [Fact, Trait(Traits.Feature, Traits.Features.DebuggingLocationName)] [WorkItem(527668, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/527668")] public async Task TestDottedNamespace() diff --git a/src/EditorFeatures/CSharpTest/DocumentationComments/CodeFixes/AddDocCommentNodesCodeFixProviderTests.cs b/src/EditorFeatures/CSharpTest/DocumentationComments/CodeFixes/AddDocCommentNodesCodeFixProviderTests.cs index eebb1502ff7bc..9f9adf39a2826 100644 --- a/src/EditorFeatures/CSharpTest/DocumentationComments/CodeFixes/AddDocCommentNodesCodeFixProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/DocumentationComments/CodeFixes/AddDocCommentNodesCodeFixProviderTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; @@ -785,5 +786,29 @@ public void Fizz(int i, int j, int k) {} await TestAsync(initial, expected); } + + [WorkItem(52738, "https://github.com/dotnet/roslyn/issues/52738")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentNodes)] + public async Task AddsParamTag_Record() + { + var initial = @" +/// +/// +/// +/// +record R(int [|First|], int Second, int Third); +"; + + var expected = @" +/// +/// +/// +/// +/// +/// +record R(int First, int Second, int Third); +"; + await TestAsync(initial, expected); + } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs index 14c7ed46e2355..6ce641947edec 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs @@ -54,7 +54,7 @@ static void Main(string[] args) { DocumentResults( active, - diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo(int)")) }) + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo(int a)")) }) }); } @@ -661,7 +661,7 @@ public void Indexer_BlockBodyToExpressionBody2() var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int].set"))); + Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int a].set"))); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index a5306fe2a6398..eeb0066db42c8 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -1867,7 +1867,7 @@ public C() {} var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ModifiersUpdate, "a = 1", FeaturesResources.const_field)); + Diagnostic(RudeEditKind.ModifiersUpdate, "const int a = 1", FeaturesResources.const_field)); } [Fact] @@ -1917,7 +1917,8 @@ public C() {} var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ModifiersUpdate, "a = 1", FeaturesResources.const_field)); + Diagnostic(RudeEditKind.ModifiersUpdate, "const int a = 1, b = 2", FeaturesResources.const_field), + Diagnostic(RudeEditKind.ModifiersUpdate, "const int a = 1, b = 2", FeaturesResources.const_field)); } [Fact] @@ -10483,7 +10484,9 @@ public void InsertDeleteMethod_Inactive() }); } - [Fact, WorkItem(51177, "https://github.com/dotnet/roslyn/issues/51177")] + [Fact] + [WorkItem(51177, "https://github.com/dotnet/roslyn/issues/51177")] + [WorkItem(54758, "https://github.com/dotnet/roslyn/issues/54758")] public void InsertDeleteMethod_Active() { // Moving active method declaration in a file with active statements. @@ -10506,7 +10509,8 @@ public void InsertDeleteMethod_Active() }), DocumentResults( activeStatements: GetActiveStatements(srcB1, srcB2, path: "1"), - diagnostics: new[] { Diagnostic(RudeEditKind.DeleteActiveStatement, "partial class C", DeletedSymbolDisplay(FeaturesResources.method, "F()")) }) + // TODO: this is odd AS location https://github.com/dotnet/roslyn/issues/54758 + diagnostics: new[] { Diagnostic(RudeEditKind.DeleteActiveStatement, " partial c", DeletedSymbolDisplay(FeaturesResources.method, "F()")) }) }); } @@ -11035,5 +11039,84 @@ public static void H(int x) } #endregion + + #region Top Level Statements + + [Fact] + public void TopLevelStatements_UpdateAroundActiveStatement_LocalFunction() + { + var src1 = @" +using System; + +Console.WriteLine(1); +void M() { Console.WriteLine(2); } +"; + var src2 = @" +using System; + +Console.WriteLine(1); +void M() { Console.WriteLine(3); } +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + + [Fact] + public void TopLevelStatements_UpdateAroundActiveStatement_OutVar() + { + var src1 = @" +using System; + +Console.WriteLine(1); +M(); +"; + var src2 = @" +using System; + +Console.WriteLine(1); +M(out var x); +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + + [Fact] + public void TopLevelStatements_Inner() + { + var src1 = @" +using System; + +Goo(1); + +static void Goo(int a) +{ + Console.WriteLine(a); +} +"; + var src2 = @" +using System; + +while (true) +{ + Goo(2); +} + +static void Goo(int a) +{ + Console.WriteLine(a); +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2);")); + } + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs index d4f26f5ad2fdc..1c76d916543a3 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs @@ -5147,5 +5147,38 @@ void Goo() } }"); } + + #region Top Level Statements + + [Fact] + public void TopLevelStatements() + { + VerifyAllSpansInDeclaration(@" +$$[|int d = 5;|] +[|int a = 1|], [|b = 2|], [|c = 3|]; +for ([|int i = 0|], [|j = 1|], [|k = 2|]; [|i < 10|]; [|i++|], [|j++|], [|k--|]) + [|while (b > 0)|] + [|{|] + [|if (c < b)|] + try + [|{|] + [|System.Console.WriteLine(a);|] + [|}|] + [|catch (Exception e)|] + [|{|] + [|System.Console.WriteLine(e);|] + [|}|] + finally + [|{|] + [|}|] + else [|if (b < 10)|] + [|System.Console.WriteLine(b);|] + else + [|System.Console.WriteLine(c);|] + [|}|] +"); + } + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 710c9bb7e5085..cece2fab3af28 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -2090,7 +2090,7 @@ void F() "; var edits = GetTopEdits(src1, src2); - // TODO: allow creating a new leaf closure + // TODO: allow creating a new leaf closure: https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "F", "this")); } @@ -5254,7 +5254,7 @@ void F() "; var edits = GetTopEdits(src1, src2); - // TODO: allow creating a new leaf closure + // TODO: allow creating a new leaf closure: https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "F", "this")); } @@ -5516,7 +5516,6 @@ void F() int f0(int a) => x0; int f1(int a) => x1; - int f0(int a) => x0; int f2(int a) => x0 + x1; // error: connecting previously disconnected closures } } @@ -7060,7 +7059,8 @@ public void LocalFunction_ReadOnlyRef_ReturnType_Update() edits.VerifyEdits( "Update [void M() { int local() { throw null; } }]@13 -> [void M() { ref readonly int local() { throw null; } }]@13"); - edits.VerifyRudeDiagnostics(); + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingLambdaReturnType, "local", CSharpFeaturesResources.local_function)); } [WorkItem(37128, "https://github.com/dotnet/roslyn/issues/37128")] @@ -7351,7 +7351,7 @@ public void LocalFunction_Parameter_AddAttribute() "Update [int i]@9 -> [[A]int i]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int i", FeaturesResources.parameter)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.parameter)); } [Fact] @@ -7366,7 +7366,7 @@ public void LocalFunction_Parameter_RemoveAttribute() "Update [[A]int i]@9 -> [int i]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int i", FeaturesResources.parameter)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.parameter)); } [Fact] @@ -7394,7 +7394,7 @@ public void LocalFunction_TypeParameter_AddAttribute() "Update [T]@9 -> [[A] T]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.type_parameter)); } [Fact] @@ -7409,7 +7409,7 @@ public void LocalFunction_TypeParameter_RemoveAttribute() "Update [[A] T]@9 -> [T]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.type_parameter)); } [Fact] @@ -7437,7 +7437,7 @@ public void LocalFunctions_TypeParameter_Insert1() "Insert [A]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } [Fact] @@ -7452,7 +7452,7 @@ public void LocalFunctions_TypeParameter_Insert2() "Insert [B]@11"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } [Fact] @@ -7467,7 +7467,7 @@ public void LocalFunctions_TypeParameter_Delete1() "Delete [A]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } [Fact] @@ -7482,7 +7482,7 @@ public void LocalFunctions_TypeParameter_Delete2() "Delete [A]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } [Fact] @@ -7496,7 +7496,61 @@ public void LocalFunctions_TypeParameter_Update() "Update [A]@9 -> [B]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "B", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); + } + + [Theory] + [InlineData("Enum", "Delegate")] + [InlineData("IDisposable", "IDisposable, new()")] + public void LocalFunctions_TypeParameter_Constraint_Clause_Update(string oldConstraint, string newConstraint) + { + var src1 = "void L() where A : " + oldConstraint + " {}"; + var src2 = "void L() where A : " + newConstraint + " {}"; + + var edits = GetMethodEdits(src1, src2); + + edits.VerifyEdits( + "Update [where A : " + oldConstraint + "]@14 -> [where A : " + newConstraint + "]@14"); + + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); + } + + [Theory] + [InlineData("nonnull")] + [InlineData("struct")] + [InlineData("class")] + [InlineData("new()")] + [InlineData("unmanaged")] + [InlineData("System.IDisposable")] + [InlineData("System.Delegate")] + public void LocalFunctions_TypeParameter_Constraint_Clause_Delete(string oldConstraint) + { + var src1 = "void L() where A : " + oldConstraint + " {}"; + var src2 = "void L() {}"; + + var edits = GetMethodEdits(src1, src2); + + edits.VerifyEdits( + "Delete [where A : " + oldConstraint + "]@14"); + + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); + } + + [Fact] + public void LocalFunctions_TypeParameter_Constraint_Clause_Add() + { + var src1 = "void L() where A : new() {}"; + var src2 = "void L() where A : new() where B : System.IDisposable {}"; + + var edits = GetMethodEdits(src1, src2); + + edits.VerifyEdits( + "Insert [where B : System.IDisposable]@32"); + + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } [Fact] @@ -7510,7 +7564,8 @@ public void LocalFunctions_TypeParameter_Reorder() "Reorder [B]@11 -> @9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter), + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } [Fact] @@ -7526,7 +7581,7 @@ public void LocalFunctions_TypeParameter_ReorderAndUpdate() GetTopEdits(edits).VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter), - Diagnostic(RudeEditKind.Renamed, "C", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.ChangingTypeParameters, "L", FeaturesResources.local_function)); } #endregion @@ -10537,5 +10592,66 @@ public void WithExpression_PropertyValueReorder() } #endregion + + #region Top Level Statements + + [Fact] + public void TopLevelStatement_CaptureArgs() + { + var src1 = @" +using System; + +var x = new Func(() => ""Hello""); + +Console.WriteLine(x()); +"; + var src2 = @" +using System; + +var x = new Func(() => ""Hello"" + args[0]); + +Console.WriteLine(x()); +"; + var edits = GetTopEdits(src1, src2); + + // TODO: allow creating a new leaf closure: https://github.com/dotnet/roslyn/issues/54672 + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.CapturingVariable, "using System;\r\n\r\nvar x = new Func(() => \"Hello\" + args[0]);\r\n\r\nConsole.WriteLine(x());\r\n", "args")); + } + + [Fact, WorkItem(21499, "https://github.com/dotnet/roslyn/issues/21499")] + public void TopLevelStatement_InsertMultiScopeCapture() + { + var src1 = @" +using System; + +foreach (int x0 in new[] { 1 }) // Group #0 +{ // Group #1 + int x1 = 0; + + int f0(int a) => x0; + int f1(int a) => x1; +} +"; + var src2 = @" +using System; + +foreach (int x0 in new[] { 1 }) // Group #0 +{ // Group #1 + int x1 = 0; + + int f0(int a) => x0; + int f1(int a) => x1; + + int f2(int a) => x0 + x1; // error: connecting previously disconnected closures +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertLambdaWithMultiScopeCapture, "x1", CSharpFeaturesResources.local_function, "x0", "x1")); + } + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs index ca139c32c0195..87040bbf65213 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementMatchingTests.cs @@ -2118,5 +2118,51 @@ public void SwitchExpressionArms_NestedDissimilar() } #endregion + + #region Top Level Statements + + [Fact] + public void TopLevelStatements() + { + var src1 = @" +Console.WriteLine(1); +Console.WriteLine(2); + +var x = 0; +while (true) +{ + x++; +} + +Console.WriteLine(3); +"; + var src2 = @" +Console.WriteLine(4); +Console.WriteLine(5); + +var x = 1; +while (true) +{ + x--; +} + +Console.WriteLine(6); +"; + var match = GetTopEdits(src1, src2).Match; + var actual = ToMatchingPairs(match); + + var expected = new MatchingPairs + { + { "Console.WriteLine(1);", "Console.WriteLine(4);" }, + { "Console.WriteLine(2);", "Console.WriteLine(5);" }, + { "var x = 0;", "var x = 1;" }, + { "while (true) { x++; }", "while (true) { x--; }" }, + { "Console.WriteLine(3);", "Console.WriteLine(6);" } + }; + + expected.AssertEqual(actual); + } + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 3827ac1e2d9b2..27e7ed214b1a2 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -1109,7 +1109,21 @@ public void Struct_NoModifiers_IntoType_Insert() } [Fact] - public void BaseTypeUpdate1() + public void Type_BaseType_Add_Unchanged() + { + var src1 = "class C { }"; + var src2 = "class C : object { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [class C { }]@0 -> [class C : object { }]@0"); + + edits.VerifySemantics(); + } + + [Fact] + public void Type_BaseType_Add_Changed() { var src1 = "class C { }"; var src2 = "class C : D { }"; @@ -1123,23 +1137,51 @@ public void BaseTypeUpdate1() Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_)); } - [Fact] - public void BaseTypeUpdate2() + [Theory] + [InlineData("string", "string?")] + [InlineData("string[]", "string[]?")] + [InlineData("object", "dynamic")] + [InlineData("dynamic?", "dynamic")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void Type_BaseType_Update_RuntimeTypeUnchanged(string oldType, string newType) { - var src1 = "class C : D1 { }"; - var src2 = "class C : D2 { }"; + var src1 = "class C : System.Collections.Generic.List<" + oldType + "> {}"; + var src2 = "class C : System.Collections.Generic.List<" + newType + "> {}"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Update [class C : D1 { }]@0 -> [class C : D2 { }]@0"); + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void Type_BaseType_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C : System.Collections.Generic.List<" + oldType + "> {}"; + var src2 = "class C : System.Collections.Generic.List<" + newType + "> {}"; + + var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_)); + Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_)); + } + + [Fact] + public void Type_BaseType_Update_CompileTimeTypeUnchanged() + { + var src1 = "using A = System.Int32; using B = System.Int32; class C : System.Collections.Generic.List {}"; + var src2 = "using A = System.Int32; using B = System.Int32; class C : System.Collections.Generic.List {}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics(); } [Fact] - public void BaseInterfaceUpdate1() + public void Type_BaseInterface_Add() { var src1 = "class C { }"; var src2 = "class C : IDisposable { }"; @@ -1154,22 +1196,28 @@ public void BaseInterfaceUpdate1() } [Fact] - public void BaseInterfaceUpdate2() + public void Type_BaseInterface_Delete_Inherited() { - var src1 = "class C : IGoo, IBar { }"; - var src2 = "class C : IGoo { }"; + var src1 = @" +interface B {} +interface A : B {} - var edits = GetTopEdits(src1, src2); +class C : A, B {} +"; + var src2 = @" +interface B {} +interface A : B {} - edits.VerifyEdits( - "Update [class C : IGoo, IBar { }]@0 -> [class C : IGoo { }]@0"); +class C : A {} +"; - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_)); + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics(); } [Fact] - public void BaseInterfaceUpdate3() + public void Type_BaseInterface_Reorder() { var src1 = "class C : IGoo, IBar { }"; var src2 = "class C : IBar, IGoo { }"; @@ -1183,6 +1231,106 @@ public void BaseInterfaceUpdate3() Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_)); } + [Theory] + [InlineData("string", "string?")] + [InlineData("object", "dynamic")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void Type_BaseInterface_Update_RuntimeTypeUnchanged(string oldType, string newType) + { + var src1 = "class C : System.Collections.Generic.IEnumerable<" + oldType + "> {}"; + var src2 = "class C : System.Collections.Generic.IEnumerable<" + newType + "> {}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void Type_BaseInterface_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C : System.Collections.Generic.IEnumerable<" + oldType + "> {}"; + var src2 = "class C : System.Collections.Generic.IEnumerable<" + newType + "> {}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_)); + } + + [Fact] + public void Type_Base_Partial() + { + var srcA1 = "partial class C : B, I { }"; + var srcB1 = "partial class C : J { }"; + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C : B, I, J { }"; + + var srcC = @" +class B {} +interface I {} +interface J {}"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC, srcC) }, + new[] + { + DocumentResults(), + DocumentResults(), + DocumentResults() + }); + } + + [Fact] + public void Type_Base_Partial_InsertDeleteAndUpdate() + { + var srcA1 = "partial class C { }"; + var srcB1 = ""; + var srcC1 = "partial class C { }"; + + var srcA2 = ""; + var srcB2 = "partial class C : D { }"; + var srcC2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "partial class C", FeaturesResources.class_) }), + + DocumentResults(), + }); + } + + [Fact] + public void Type_Base_InsertDelete() + { + var srcA1 = ""; + var srcB1 = "class C : B, I { }"; + var srcA2 = "class C : B, I { }"; + var srcB2 = ""; + + var srcC = @" +class B {} +interface I {} +interface J {}"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC, srcC) }, + new[] + { + DocumentResults(), + DocumentResults(), + DocumentResults() + }); + } + [Fact] public void ClassInsert_AbstractVirtualOverride() { @@ -2243,7 +2391,7 @@ public void Record_AddProperty_Primary() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.AddRecordPositionalParameter, "int Y", FeaturesResources.parameter)); + Diagnostic(RudeEditKind.Insert, "int Y", FeaturesResources.parameter)); } [Fact] @@ -2515,7 +2663,7 @@ public void Record_DeleteProperty_Primary() var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.DeleteRecordPositionalParameter, "record C", FeaturesResources.parameter)); + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "record C", DeletedSymbolDisplay(FeaturesResources.parameter, "int Y"))); } [Fact] @@ -2986,7 +3134,7 @@ public void Enum_NoModifiers_IntoType_Insert() } [Fact] - public void EnumAttributeInsert() + public void Enum_Attribute_Insert() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -3003,7 +3151,7 @@ public void EnumAttributeInsert() } [Fact] - public void EnumMemberAttributeDelete() + public void Enum_Member_Attribute_Delete() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -3020,7 +3168,7 @@ public void EnumMemberAttributeDelete() } [Fact] - public void EnumMemberAttributeInsert() + public void Enum_Member_Attribute_Insert() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -3037,7 +3185,7 @@ public void EnumMemberAttributeInsert() } [Fact] - public void EnumMemberAttributeUpdate() + public void Enum_Member_Attribute_Update() { var attribute = "public class A1Attribute : System.Attribute { }\n\n" + "public class A2Attribute : System.Attribute { }\n\n"; @@ -3055,7 +3203,28 @@ public void EnumMemberAttributeUpdate() } [Fact] - public void EnumNameUpdate() + public void Enum_Member_Attribute_InsertDeleteAndUpdate() + { + var srcA1 = ""; + var srcB1 = "enum N { A = 1 }"; + var srcA2 = "enum N { [System.Obsolete]A = 1 }"; + var srcB2 = ""; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("N.A")) + }), + DocumentResults() + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void Enum_Rename() { var src1 = "enum Color { Red = 1, Blue = 2, }"; var src2 = "enum Colors { Red = 1, Blue = 2, }"; @@ -3070,7 +3239,7 @@ public void EnumNameUpdate() } [Fact] - public void EnumBaseTypeAdd() + public void Enum_BaseType_Add() { var src1 = "enum Color { Red = 1, Blue = 2, }"; var src2 = "enum Color : ushort { Red = 1, Blue = 2, }"; @@ -3084,7 +3253,20 @@ public void EnumBaseTypeAdd() } [Fact] - public void EnumBaseTypeUpdate() + public void Enum_BaseType_Add_Unchanged() + { + var src1 = "enum Color { Red = 1, Blue = 2, }"; + var src2 = "enum Color : int { Red = 1, Blue = 2, }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Update [enum Color { Red = 1, Blue = 2, }]@0 -> [enum Color : int { Red = 1, Blue = 2, }]@0"); + + edits.VerifySemantics(); + } + + [Fact] + public void Enum_BaseType_Update() { var src1 = "enum Color : ushort { Red = 1, Blue = 2, }"; var src2 = "enum Color : long { Red = 1, Blue = 2, }"; @@ -3098,7 +3280,20 @@ public void EnumBaseTypeUpdate() } [Fact] - public void EnumBaseTypeDelete() + public void Enum_BaseType_Delete_Unchanged() + { + var src1 = "enum Color : int { Red = 1, Blue = 2, }"; + var src2 = "enum Color { Red = 1, Blue = 2, }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Update [enum Color : int { Red = 1, Blue = 2, }]@0 -> [enum Color { Red = 1, Blue = 2, }]@0"); + + edits.VerifySemantics(); + } + + [Fact] + public void Enum_BaseType_Delete_Changed() { var src1 = "enum Color : ushort { Red = 1, Blue = 2, }"; var src2 = "enum Color { Red = 1, Blue = 2, }"; @@ -3479,7 +3674,7 @@ public void Delegates_Accessibility_Update() } [Fact] - public void Delegates_Update_ReturnType() + public void Delegates_ReturnType_Update() { var src1 = "public delegate int D();"; var src2 = "public delegate void D();"; @@ -3493,6 +3688,29 @@ public void Delegates_Update_ReturnType() Diagnostic(RudeEditKind.TypeUpdate, "public delegate void D()", FeaturesResources.delegate_)); } + [Fact] + public void Delegates_ReturnType_AddAttribute() + { + var attribute = "public class A : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "[return: A]public delegate int D(int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [public delegate int D(int a);]@39 -> [[return: A]public delegate int D(int a);]@39"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.BeginInvoke")) + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + [Fact] public void Delegates_Parameter_Insert() { @@ -3520,7 +3738,7 @@ public void Delegates_Parameter_Delete() "Delete [int a]@22"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public delegate int D()", FeaturesResources.parameter)); + Diagnostic(RudeEditKind.Delete, "public delegate int D()", DeletedSymbolDisplay(FeaturesResources.parameter, "int a"))); } [Fact] @@ -3554,37 +3772,7 @@ public void Delegates_Parameter_Update() } [Fact] - public void Delegates_ParameterOptionalParameter_Update() - { - var src1 = "public delegate int D(int a = 1);"; - var src2 = "public delegate int D(int a = 2);"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [int a = 1]@22 -> [int a = 2]@22"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InitializerUpdate, "int a = 2", FeaturesResources.parameter)); - } - - [Fact] - public void Delegates_Parameter_UpdateModifier() - { - var src1 = "public delegate int D(int[] a);"; - var src2 = "public delegate int D(params int[] a);"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [int[] a]@22 -> [params int[] a]@22"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "params int[] a", FeaturesResources.parameter)); - } - - [Fact] - public void Delegates_Parameter_AddAttribute() + public void Delegates_Parameter_AddAttribute_NotSupportedByRuntime() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -3601,9 +3789,9 @@ public void Delegates_Parameter_AddAttribute() } [Fact] - public void Delegates_Parameter_AddAttribute_SupportedByRuntime() + public void Delegates_Parameter_AddAttribute() { - var attribute = "public class AAttribute : System.Attribute { }\n\n"; + var attribute = "public class A : System.Attribute { }\n\n"; var src1 = attribute + "public delegate int D(int a);"; var src2 = attribute + "public delegate int D([A]int a);"; @@ -3611,12 +3799,16 @@ public void Delegates_Parameter_AddAttribute_SupportedByRuntime() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [int a]@70 -> [[A]int a]@70"); + "Update [int a]@61 -> [[A]int a]@61"); edits.VerifySemantics( ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.BeginInvoke")) + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -3632,7 +3824,7 @@ public void Delegates_TypeParameter_Insert() "Insert [T]@22"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.Insert, "T", FeaturesResources.type_parameter)); } [Fact] @@ -3648,7 +3840,7 @@ public void Delegates_TypeParameter_Delete() "Delete [T]@22"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public delegate int D()", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.Delete, "public delegate int D()", DeletedSymbolDisplay(FeaturesResources.type_parameter, "T"))); } [Fact] @@ -3724,12 +3916,17 @@ public void Delegates_TypeParameter_AddAttribute() edits.VerifyEdits( "Update [T]@70 -> [[A]T]@70"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")), + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] - public void Delegates_AddAttribute() + public void Delegates_Attribute_Add_NotSupportedByRuntime() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -3746,7 +3943,7 @@ public void Delegates_AddAttribute() } [Fact] - public void Delegates_AddAttribute_SupportedByRuntime() + public void Delegates_Attribute_Add() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -3761,43 +3958,31 @@ public void Delegates_AddAttribute_SupportedByRuntime() edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); - } - - [Fact] - public void Delegates_AddReturnAttribute() - { - var attribute = "public class AAttribute : System.Attribute { }\n\n"; - - var src1 = attribute + "public delegate int D(int a);"; - var src2 = attribute + "[return:A]public delegate int D(int a);"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [public delegate int D(int a);]@48 -> [[return:A]public delegate int D(int a);]@48"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public delegate int D(int a)", FeaturesResources.delegate_)); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] - public void Delegates_AddReturnAttribute_SupportedByRuntime() + public void Delegates_Attribute_Add_WithReturnTypeAttribute() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; var src1 = attribute + "public delegate int D(int a);"; - var src2 = attribute + "[return:A]public delegate int D(int a);"; + var src2 = attribute + "[return: A][A]public delegate int D(int a);"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [public delegate int D(int a);]@48 -> [[return:A]public delegate int D(int a);]@48"); + "Update [public delegate int D(int a);]@48 -> [[return: A][A]public delegate int D(int a);]@48"); edits.VerifySemantics( ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.BeginInvoke")) + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -4294,7 +4479,7 @@ public void NestedDelegateInPartialType_InsertDelete() } [Fact] - public void NestedDelegateInPartialType_InsertDeleteAndChangeSignature() + public void NestedDelegateInPartialType_InsertDeleteAndChangeParameters() { var srcA1 = "partial struct S { }"; var srcB1 = "partial struct S { delegate void D(); }"; @@ -4308,7 +4493,29 @@ public void NestedDelegateInPartialType_InsertDeleteAndChangeSignature() DocumentResults( diagnostics: new[] { - Diagnostic(RudeEditKind.Insert, "int x", FeaturesResources.parameter) + Diagnostic(RudeEditKind.ChangingParameterTypes, "delegate void D(int x)", FeaturesResources.delegate_) + }), + + DocumentResults() + }); + } + + [Fact] + public void NestedDelegateInPartialType_InsertDeleteAndChangeReturnType() + { + var srcA1 = "partial struct S { }"; + var srcB1 = "partial struct S { delegate ref int D(); }"; + var srcA2 = "partial struct S { delegate ref readonly int D(); }"; + var srcB2 = "partial struct S { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.TypeUpdate, "delegate ref readonly int D()", FeaturesResources.delegate_) }), DocumentResults() @@ -4355,7 +4562,7 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange() DocumentResults(), DocumentResults( - diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "partial struct S", DeletedSymbolDisplay(FeaturesResources.method, "F2(byte)")) }), + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "partial struct S", DeletedSymbolDisplay(FeaturesResources.method, "F2(byte x)")) }), DocumentResults( semanticEdits: new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("S").GetMember("C").GetMember("F2")) }) @@ -4363,14 +4570,14 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange() } [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteAndChange_BaseType() + public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Attribute() { var srcA1 = "partial class C { }"; var srcB1 = ""; var srcC1 = "partial class C { }"; var srcA2 = ""; - var srcB2 = "partial class C : D { }"; + var srcB2 = "[A]partial class C { }"; var srcC2 = "partial class C { }"; EditAndContinueValidation.VerifySemantics( @@ -4378,42 +4585,19 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_BaseType() new[] { DocumentResults(), - - DocumentResults( - diagnostics: new[] { Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "partial class C", FeaturesResources.class_) }), - + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")) + }), DocumentResults(), - }); + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Attribute() + public void NestedPartialTypeInPartialType_InsertDeleteAndChange_TypeParameterAttribute_NotSupportedByRuntime() { - var srcA1 = "partial class C { }"; - var srcB1 = ""; - var srcC1 = "partial class C { }"; - - var srcA2 = ""; - var srcB2 = "[A]partial class C { }"; - var srcC2 = "partial class C { }"; - - EditAndContinueValidation.VerifySemantics( - new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, - new[] - { - DocumentResults(), - - DocumentResults( - diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), - - DocumentResults(), - }); - } - - [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteAndChange_TypeParameter() - { - var srcA1 = "partial class C { }"; + var srcA1 = "partial class C { }"; var srcB1 = ""; var srcC1 = "partial class C { }"; @@ -4428,7 +4612,10 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_TypeParameter() DocumentResults(), DocumentResults( - diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C<[A]T>", FeaturesResources.class_) }), + diagnostics: new[] + { + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter) + }), DocumentResults(), }); @@ -4452,7 +4639,10 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Constraint() DocumentResults(), DocumentResults( - diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), + diagnostics: new[] + { + Diagnostic(RudeEditKind.ChangingConstraints, "where T : new()", FeaturesResources.type_parameter) + }), DocumentResults(), }); @@ -4474,8 +4664,10 @@ public void NestedPartialTypeInPartialType_InsertDeleteRefactor() var srcC2 = "[A]partial class C : I, J { void F() { } }"; var srcD2 = "[B]partial class C { void G() { } }"; + var srcE = "interface I {} interface J {}"; + EditAndContinueValidation.VerifySemantics( - new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2) }, + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2), GetTopEdits(srcE, srcE) }, new[] { DocumentResults(), @@ -4486,6 +4678,8 @@ public void NestedPartialTypeInPartialType_InsertDeleteRefactor() DocumentResults( semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("G")) }), + + DocumentResults(), }); } @@ -4512,8 +4706,14 @@ public void NestedPartialTypeInPartialType_InsertDeleteRefactor_AttributeListSpl { DocumentResults(), DocumentResults(), - DocumentResults(diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), - DocumentResults(diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")) + }), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G")) + }), }); } @@ -5135,6 +5335,84 @@ public void PartialMember_DeleteInsert_ConstructorWithInitializers() }); } + [Fact] + public void PartialMember_DeleteInsert_MethodAddParameter() + { + var srcA1 = "partial struct S { }"; + var srcB1 = "partial struct S { void F() {} }"; + var srcA2 = "partial struct S { void F(int x) {} }"; + var srcB2 = "partial struct S { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("S.F")) + }), + + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.Delete, "partial struct S", DeletedSymbolDisplay(FeaturesResources.method, "F()")) + }) + }); + } + + [Fact] + public void PartialMember_DeleteInsert_UpdateMethodParameterType() + { + var srcA1 = "partial struct S { }"; + var srcB1 = "partial struct S { void F(int x); }"; + var srcA2 = "partial struct S { void F(byte x); }"; + var srcB2 = "partial struct S { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("S.F")) + }), + + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.Delete, "partial struct S", DeletedSymbolDisplay(FeaturesResources.method, "F(int x)")) + }) + }); + } + + [Fact] + public void PartialMember_DeleteInsert_MethodAddTypeParameter() + { + var srcA1 = "partial struct S { }"; + var srcB1 = "partial struct S { void F(); }"; + var srcA2 = "partial struct S { void F(); }"; + var srcB2 = "partial struct S { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.InsertGenericMethod, "void F()", FeaturesResources.method) + }), + + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.Delete, "partial struct S", DeletedSymbolDisplay(FeaturesResources.method, "F()")) + }) + }); + } + #endregion #region Methods @@ -5333,6 +5611,36 @@ public async Task WaitAsync() Diagnostic(RudeEditKind.MakeMethodAsync, "public async Task WaitAsync()")); } + [Theory] + [InlineData("string", "string?")] + [InlineData("object", "dynamic")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void Method_ReturnType_Update_RuntimeTypeUnchanged(string oldType, string newType) + { + var src1 = "class C { " + oldType + " M() => default; }"; + var src2 = "class C { " + newType + " M() => default; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void Method_ReturnType_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C { " + oldType + " M() => default; }"; + var src2 = "class C { " + newType + " M() => default; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, newType + " M()", FeaturesResources.method)); + } + [Fact] public void Method_Update() { @@ -5609,7 +5917,7 @@ void goo(int a) { }]@18", "Delete [int a]@43"); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(int)"))); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(int a)"))); } [WorkItem(754853, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")] @@ -5643,7 +5951,7 @@ class C "Delete [string c]@135"); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "puts(string)"))); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "puts(string c)"))); } [Fact] @@ -6031,61 +6339,7 @@ static void Main(string[] b) } [Fact] - public void MethodUpdate_UpdateParameterToNullable() - { - var src1 = @" -class C -{ - static void M(string s) - { - } -}"; - var src2 = @" -class C -{ - static void M(string? s) - { - } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [string s]@32 -> [string? s]@32"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "string? s", FeaturesResources.parameter)); - } - - [Fact] - public void MethodUpdate_UpdateParameterToNonNullable() - { - var src1 = @" -class C -{ - static void M(string? s) - { - - } -}"; - var src2 = @" -class C -{ - static void M(string s) - { - - } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [string? s]@32 -> [string s]@32"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "string s", FeaturesResources.parameter)); - } - - [Fact] - public void MethodUpdate_RenameMethodName() + public void Method_Name_Update() { var src1 = @" class C @@ -6119,56 +6373,6 @@ static void EntryPoint(string[] args) Diagnostic(RudeEditKind.Renamed, "static void EntryPoint(string[] args)", FeaturesResources.method)); } - [Fact] - public void MethodUpdate_ReorderParameter() - { - var src1 = @" -class C -{ - static void Main(int a, char c) - { - - } -}"; - var src2 = @" -class C -{ - static void Main(char c, int a) - { - - } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Reorder [char c]@42 -> @35"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "char c", FeaturesResources.parameter)); - } - - [Fact] - public void MethodUpdate_DeleteParameter() - { - var src1 = @" -class C -{ - static void Main(string[] args) { } -}"; - var src2 = @" -class C -{ - static void Main() { } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Delete [string[] args]@35"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "static void Main()", FeaturesResources.parameter)); - } - [Fact] public void MethodUpdate_AsyncMethod0() { @@ -6246,162 +6450,66 @@ public async Task WaitAsync() } [Fact] - public void MethodUpdate_DeleteParameterModifierThis() + public void MethodUpdate_AddReturnTypeAttribute() { var src1 = @" -class C -{ - static void Main(string[] args) - { - var s = ""1""; - s.ToInt32(); - } -} -public static class Extensions +using System; + +class Test { - public static int ToInt32(this string s) + static void Main(string[] args) { - return Int32.Parse(s); + System.Console.Write(5); } }"; var src2 = @" -class C -{ - static void Main(string[] args) - { - var s = ""1""; - s.ToInt32(); - } -} -public static class Extensions +using System; + +class Test { - public static int ToInt32(string s) + [return: Obsolete] + static void Main(string[] args) { - return Int32.Parse(s); + System.Console.Write(5); } }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Update [this string s]@179 -> [string s]@179"); + edits.VerifyEdits(@"Update [static void Main(string[] args) + { + System.Console.Write(5); + }]@38 -> [[return: Obsolete] + static void Main(string[] args) + { + System.Console.Write(5); + }]@38"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "string s", FeaturesResources.parameter)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] - public void MethodUpdate_DeleteParameterModifiersRefAndOut() + public void MethodUpdate_AddAttribute() { var src1 = @" -class C +using System; + +class Test { - static void Main(string[] args) - { - int a; - C c = new C(); - c.Method(out a); - c.Method2(ref a); - } - void Method(out int a) - { - a = 1; - } - void Method2(ref int b) + static void Main(string[] args) { - b = 45; + System.Console.Write(5); } }"; var src2 = @" -class C +using System; + +class Test { - static void Main(string[] args) - { - int a; - C c = new C(); - c.Method(out a); - c.Method2(ref a); - } - void Method(int a) + [Obsolete] + static void Main(string[] args) { - a = 1; - } - void Method2(int b) - { - b = 45; - } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [out int a]@176 -> [int a]@176", - "Update [ref int b]@235 -> [int b]@231"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "int a", FeaturesResources.parameter), - Diagnostic(RudeEditKind.ModifiersUpdate, "int b", FeaturesResources.parameter)); - } - - [Fact] - public void MethodUpdate_AddReturnTypeAttribute() - { - var src1 = @" -using System; - -class Test -{ - static void Main(string[] args) - { - System.Console.Write(5); - } -}"; - var src2 = @" -using System; - -class Test -{ - [return: Obsolete] - static void Main(string[] args) - { - System.Console.Write(5); - } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits(@"Update [static void Main(string[] args) - { - System.Console.Write(5); - }]@38 -> [[return: Obsolete] - static void Main(string[] args) - { - System.Console.Write(5); - }]@38"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); - } - - [Fact] - public void MethodUpdate_AddAttribute() - { - var src1 = @" -using System; - -class Test -{ - static void Main(string[] args) - { - System.Console.Write(5); - } -}"; - var src2 = @" -using System; - -class Test -{ - [Obsolete] - static void Main(string[] args) - { - System.Console.Write(5); + System.Console.Write(5); } }"; var edits = GetTopEdits(src1, src2); @@ -6457,7 +6565,7 @@ static void Main(string[] args) edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Test.Main")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -6494,7 +6602,7 @@ void M() edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -7522,8 +7630,8 @@ class C var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(CSharpFeaturesResources.conversion_operator, "implicit operator bool(C)")), - Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.operator_, "operator +(C, C)"))); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(CSharpFeaturesResources.conversion_operator, "implicit operator bool(C c)")), + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.operator_, "operator +(C c, C d)"))); } [Fact] @@ -7950,12 +8058,13 @@ public void InstanceCtorDelete_Public() [InlineData("protected internal")] public void InstanceCtorDelete_NonPublic(string accessibility) { - var src1 = "class C { " + accessibility + " C() { } }"; + var src1 = "class C { [System.Obsolete] " + accessibility + " C() { } }"; var src2 = "class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()")), Diagnostic(RudeEditKind.ChangingAccessibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); } @@ -9313,12 +9422,13 @@ public void PropertyInitializerUpdate_StaticCtorUpdate1() [Fact] public void FieldInitializerUpdate_InstanceCtorUpdate_Private() { - var src1 = "class C { int a; C() { } }"; + var src1 = "class C { int a; [System.Obsolete]C() { } }"; var src2 = "class C { int a = 0; }"; var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()")), Diagnostic(RudeEditKind.ChangingAccessibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); } @@ -10689,7 +10799,7 @@ public void Field_Partial_DeleteInsert_InitializerUpdate() #region Fields [Fact] - public void FieldNameUpdate1() + public void Field_Rename() { var src1 = "class C { int a = 0; }"; var src2 = "class C { int b = 0; }"; @@ -10704,7 +10814,7 @@ public void FieldNameUpdate1() } [Fact] - public void FieldUpdate_FieldKind() + public void Field_Kind_Update() { var src1 = "class C { Action a; }"; var src2 = "class C { event Action a; }"; @@ -10715,7 +10825,7 @@ public void FieldUpdate_FieldKind() "Update [Action a;]@10 -> [event Action a;]@10"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.FieldKindUpdate, "event Action a", CSharpFeaturesResources.event_field)); + Diagnostic(RudeEditKind.FieldKindUpdate, "event Action a", FeaturesResources.event_)); } [Theory] @@ -10742,7 +10852,7 @@ public void Field_Modifiers_Update(string oldModifiers, string newModifiers = "" edits.VerifyEdits("Update [" + oldModifiers + "int F = 0;]@10 -> [" + newModifiers + "int F = 0;]@10"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "F = 0", FeaturesResources.field)); + Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "int F = 0", FeaturesResources.field)); } [Fact] @@ -10822,7 +10932,7 @@ public void Field_Const_Update() } [Fact] - public void EventFieldUpdate_VariableDeclarator() + public void Field_Event_VariableDeclarator_Update() { var src1 = "class C { event Action a; }"; var src2 = "class C { event Action a = () => { }; }"; @@ -10836,7 +10946,7 @@ public void EventFieldUpdate_VariableDeclarator() } [Fact] - public void FieldReorder() + public void Field_Reorder() { var src1 = "class C { int a = 0; int b = 1; int c = 2; }"; var src2 = "class C { int c = 2; int a = 0; int b = 1; }"; @@ -10851,7 +10961,7 @@ public void FieldReorder() } [Fact] - public void FieldInsert_Private() + public void Field_Insert() { var src1 = "class C { }"; var src2 = "class C { int a = 1; }"; @@ -10873,55 +10983,7 @@ public void FieldInsert_Private() } [Fact] - public void FieldInsert_PrivateReadonly() - { - var src1 = "class C { }"; - var src2 = "class C { private readonly int a = 1; }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Insert [private readonly int a = 1;]@10", - "Insert [int a = 1]@27", - "Insert [a = 1]@31"); - - edits.VerifyRudeDiagnostics(); - } - - [Fact] - public void FieldInsert_Public() - { - var src1 = "class C { }"; - var src2 = "class C { public int a = 1; }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Insert [public int a = 1;]@10", - "Insert [int a = 1]@17", - "Insert [a = 1]@21"); - - edits.VerifyRudeDiagnostics(); - } - - [Fact] - public void FieldInsert_Protected() - { - var src1 = "class C { }"; - var src2 = "class C { protected int a = 1; }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Insert [protected int a = 1;]@10", - "Insert [int a = 1]@20", - "Insert [a = 1]@24"); - - edits.VerifyRudeDiagnostics(); - } - - [Fact] - public void FieldInsert_IntoStruct() + public void Field_Insert_IntoStruct() { var src1 = @" struct S @@ -10954,7 +11016,7 @@ struct S } [Fact] - public void FieldInsert_IntoLayoutClass_Auto() + public void Field_Insert_IntoLayoutClass_Auto() { var src1 = @" using System.Runtime.InteropServices; @@ -10991,7 +11053,7 @@ class C } [Fact] - public void FieldInsert_IntoLayoutClass_Explicit() + public void Field_Insert_IntoLayoutClass_Explicit() { var src1 = @" using System.Runtime.InteropServices; @@ -11030,7 +11092,7 @@ class C } [Fact] - public void FieldInsert_IntoLayoutClass_Sequential() + public void Field_Insert_IntoLayoutClass_Sequential() { var src1 = @" using System.Runtime.InteropServices; @@ -11063,7 +11125,7 @@ class C } [Fact] - public void FieldInsert_WithInitializersAndLambdas1() + public void Field_Insert_WithInitializersAndLambdas1() { var src1 = @" using System; @@ -11109,7 +11171,7 @@ public C() } [Fact] - public void FieldInsert_ConstructorReplacingImplicitConstructor_WithInitializersAndLambdas() + public void Field_Insert_ConstructorReplacingImplicitConstructor_WithInitializersAndLambdas() { var src1 = @" using System; @@ -11150,7 +11212,7 @@ class C } [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] - public void FieldInsert_ParameterlessConstructorInsert_WithInitializersAndLambdas() + public void Field_Insert_ParameterlessConstructorInsert_WithInitializersAndLambdas() { var src1 = @" using System; @@ -11196,7 +11258,7 @@ public C(int x) {} } [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] - public void FieldInsert_ConstructorInsert_WithInitializersAndLambdas1() + public void Field_Insert_ConstructorInsert_WithInitializersAndLambdas1() { var src1 = @" using System; @@ -11241,7 +11303,7 @@ class C } [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] - public void FieldInsert_ConstructorInsert_WithInitializersButNoExistingLambdas1() + public void Field_Insert_ConstructorInsert_WithInitializersButNoExistingLambdas1() { var src1 = @" using System; @@ -11282,7 +11344,7 @@ class C } [Fact] - public void FieldInsert_NotSupportedByRuntime() + public void Field_Insert_NotSupportedByRuntime() { var src1 = "class C { }"; var src2 = "class C { public int a = 1; }"; @@ -11295,7 +11357,7 @@ public void FieldInsert_NotSupportedByRuntime() } [Fact] - public void FieldInsert_Static_NotSupportedByRuntime() + public void Field_Insert_Static_NotSupportedByRuntime() { var src1 = "class C { }"; var src2 = "class C { public static int a = 1; }"; @@ -11327,7 +11389,8 @@ class C "Update [public int a = 1, x = 1;]@18 -> [[System.Obsolete]public int a = 1, x = 1;]@18"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "a = 1", FeaturesResources.field)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public int a = 1, x = 1", FeaturesResources.field), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public int a = 1, x = 1", FeaturesResources.field)); } [Fact] @@ -11336,12 +11399,12 @@ public void Field_Attribute_Add() var src1 = @" class C { - public int a; + public int a, b; }"; var src2 = @" class C { - [System.Obsolete]public int a; + [System.Obsolete]public int a, b; }"; var edits = GetTopEdits(src1, src2); @@ -11350,7 +11413,8 @@ class C ActiveStatementsDescription.Empty, new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.a")) + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.a")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.b")) }, capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } @@ -11406,7 +11470,7 @@ public void Field_Attribute_DeleteInsertUpdate_WithInitializer() } [Fact] - public void FieldDelete1() + public void Field_Delete1() { var src1 = "class C { int a = 1; }"; var src2 = "class C { }"; @@ -11423,7 +11487,7 @@ public void FieldDelete1() } [Fact] - public void FieldUnsafeModifierUpdate() + public void Field_UnsafeModifier_Update() { var src1 = "struct Node { unsafe Node* left; }"; var src2 = "struct Node { Node* left; }"; @@ -11436,7 +11500,7 @@ public void FieldUnsafeModifierUpdate() } [Fact] - public void FieldModifierAndTypeUpdate() + public void Field_ModifierAndType_Update() { var src1 = "struct Node { unsafe Node* left; }"; var src2 = "struct Node { Node left; }"; @@ -11451,36 +11515,95 @@ public void FieldModifierAndTypeUpdate() Diagnostic(RudeEditKind.TypeUpdate, "Node left", FeaturesResources.field)); } - [Fact] - public void FieldTypeUpdateNullable() + [Theory] + [InlineData("string", "string?")] + [InlineData("object", "dynamic")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void Field_Type_Update_RuntimeTypeUnchanged(string oldType, string newType) + { + var src1 = "class C { " + oldType + " F, G; }"; + var src2 = "class C { " + newType + " F, G; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void Field_Type_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C { " + oldType + " F, G; }"; + var src2 = "class C { " + newType + " F, G; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, newType + " F, G", FeaturesResources.field), + Diagnostic(RudeEditKind.TypeUpdate, newType + " F, G", FeaturesResources.field)); + } + + [Theory] + [InlineData("string", "string?")] + [InlineData("object", "dynamic")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void Field_Event_Type_Update_RuntimeTypeUnchanged(string oldType, string newType) { - var src1 = "class C { int left; }"; - var src2 = "class C { int? left; }"; + var src1 = "class C { event System.Action<" + oldType + "> F, G; }"; + var src2 = "class C { event System.Action<" + newType + "> F, G; }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Update [int left]@10 -> [int? left]@10"); + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void Field_Event_Type_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C { event System.Action<" + oldType + "> F, G; }"; + var src2 = "class C { event System.Action<" + newType + "> F, G; }"; + + var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "int? left", FeaturesResources.field)); + Diagnostic(RudeEditKind.TypeUpdate, "event System.Action<" + newType + "> F, G", FeaturesResources.event_), + Diagnostic(RudeEditKind.TypeUpdate, "event System.Action<" + newType + "> F, G", FeaturesResources.event_)); } [Fact] - public void FieldTypeUpdateNonNullable() + public void Field_Type_Update_ReorderRemoveAdd() { - var src1 = "class C { int? left; }"; - var src2 = "class C { int left; }"; + var src1 = "class C { int F, G, H; bool U; }"; + var src2 = "class C { string G, F; double V, U; }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Update [int? left]@10 -> [int left]@10"); + edits.VerifyEdits( + "Update [int F, G, H]@10 -> [string G, F]@10", + "Reorder [G]@17 -> @17", + "Update [bool U]@23 -> [double V, U]@23", + "Insert [V]@30", + "Delete [H]@20"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "int left", FeaturesResources.field)); + Diagnostic(RudeEditKind.Move, "G", FeaturesResources.field), + Diagnostic(RudeEditKind.TypeUpdate, "string G, F", FeaturesResources.field), + Diagnostic(RudeEditKind.TypeUpdate, "string G, F", FeaturesResources.field), + Diagnostic(RudeEditKind.TypeUpdate, "double V, U", FeaturesResources.field), + Diagnostic(RudeEditKind.Delete, "string G, F", DeletedSymbolDisplay(FeaturesResources.field, "H"))); } [Fact] - public void EventFieldReorder() + public void Field_Event_Reorder() { var src1 = "class C { int a = 0; int b = 1; event int c = 2; }"; var src2 = "class C { event int c = 2; int a = 0; int b = 1; }"; @@ -11495,7 +11618,7 @@ public void EventFieldReorder() } [Fact] - public void EventField_Partial_InsertDelete() + public void Field_Event_Partial_InsertDelete() { var srcA1 = "partial class C { }"; var srcB1 = "partial class C { event int E = 2; }"; @@ -11551,7 +11674,19 @@ public void Property_Modifiers_Update(string oldModifiers, string newModifiers = } [Fact] - public void PropertyWithExpressionBody_Update() + public void Property_ExpressionBody_Rename() + { + var src1 = "class C { int P => 1; }"; + var src2 = "class C { int Q => 1; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "int Q", FeaturesResources.property_)); + } + + [Fact] + public void Property_ExpressionBody_Update() { var src1 = "class C { int P => 1; }"; var src2 = "class C { int P => 2; }"; @@ -11567,7 +11702,7 @@ public void PropertyWithExpressionBody_Update() } [Fact, WorkItem(48628, "https://github.com/dotnet/roslyn/issues/48628")] - public void PropertyWithExpressionBody_ModifierUpdate() + public void Property_ExpressionBody_ModifierUpdate() { var src1 = "class C { int P => 1; }"; var src2 = "class C { unsafe int P => 1; }"; @@ -11790,7 +11925,7 @@ public void Property_GetterExpressionBodyWithSetterToGetterBlockBodyWithSetter() } [Fact] - public void PropertyRename1() + public void Property_Rename1() { var src1 = "class C { int P { get { return 1; } } }"; var src2 = "class C { int Q { get { return 1; } } }"; @@ -11802,7 +11937,7 @@ public void PropertyRename1() } [Fact] - public void PropertyRename2() + public void Property_Rename2() { var src1 = "class C { int I.P { get { return 1; } } }"; var src2 = "class C { int J.P { get { return 1; } } }"; @@ -11813,6 +11948,18 @@ public void PropertyRename2() Diagnostic(RudeEditKind.Renamed, "int J.P", FeaturesResources.property_)); } + [Fact] + public void Property_RenameAndUpdate() + { + var src1 = "class C { int P { get { return 1; } } }"; + var src2 = "class C { int Q { get { return 2; } } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "int Q", FeaturesResources.property_)); + } + [Fact] public void PropertyDelete() { @@ -11896,7 +12043,7 @@ public void PropertyTypeUpdate() "Update [int P { get; set; }]@10 -> [char P { get; set; }]@10"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "char P", FeaturesResources.auto_property)); + Diagnostic(RudeEditKind.TypeUpdate, "char P", FeaturesResources.property_)); } [Fact] @@ -11908,7 +12055,7 @@ public void PropertyUpdate_AddAttribute() var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int P", FeaturesResources.auto_property)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int P", FeaturesResources.property_)); } [Fact] @@ -11924,7 +12071,7 @@ public void PropertyUpdate_AddAttribute_SupportedByRuntime() new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -11962,7 +12109,7 @@ public void PropertyAccessorUpdate_AddAttribute_SupportedByRuntime() edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").GetMethod) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -11976,7 +12123,7 @@ public void PropertyAccessorUpdate_AddAttribute_SupportedByRuntime2() edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").SetMethod) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -12349,7 +12496,7 @@ public void Property_ReadOnlyRef_Update() "Update [int P { get; }]@13 -> [ref readonly int P { get; }]@13"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "ref readonly int P", FeaturesResources.auto_property)); + Diagnostic(RudeEditKind.TypeUpdate, "ref readonly int P", FeaturesResources.property_)); } [Fact] @@ -12512,9 +12659,9 @@ struct S }"; var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.property_getter), - Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.property_getter), - Diagnostic(RudeEditKind.ModifiersUpdate, "set", CSharpFeaturesResources.property_setter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int P1", CSharpFeaturesResources.property_getter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int P4", CSharpFeaturesResources.property_getter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int P4", CSharpFeaturesResources.property_setter), Diagnostic(RudeEditKind.ModifiersUpdate, "readonly get", CSharpFeaturesResources.property_getter), Diagnostic(RudeEditKind.ModifiersUpdate, "readonly set", CSharpFeaturesResources.property_setter)); } @@ -12945,7 +13092,7 @@ public void Indexer_GetterAndSetterBlockBodiesToExpressionBody() "Delete [set { Console.WriteLine(0); }]@46"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int].set"))); + Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int a].set"))); } [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] @@ -13039,21 +13186,6 @@ public void Tuple_TypeUpdate() Diagnostic(RudeEditKind.TypeUpdate, "(string, int) M()", FeaturesResources.method)); } - [Fact] - public void TupleNames_TypeUpdate() - { - var src1 = "class C { (int a, int) M() { throw new System.Exception(); } }"; - var src2 = "class C { (int notA, int) M() { throw new System.Exception(); } }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Update [(int a, int) M() { throw new System.Exception(); }]@10 -> [(int notA, int) M() { throw new System.Exception(); }]@10"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "(int notA, int) M()", FeaturesResources.method)); - } - [Fact] public void TupleElementDelete() { @@ -13212,7 +13344,7 @@ public T this[int i] edits.VerifyEdits("Delete [get { return arr[i]; }]@58"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public T this[int i]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_getter, "this[int].get"))); + Diagnostic(RudeEditKind.Delete, "public T this[int i]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_getter, "this[int i].get"))); } [Fact] @@ -13233,7 +13365,7 @@ class C edits.VerifyEdits("Delete [set { }]@61"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public int this[int i]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int].set"))); + Diagnostic(RudeEditKind.Delete, "public int this[int i]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int i].set"))); } [Fact, WorkItem(1174850, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1174850")] @@ -13428,7 +13560,7 @@ struct S }"; var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.indexer_getter)); + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int this[int x]", CSharpFeaturesResources.indexer_getter)); } [Fact] @@ -13452,9 +13584,9 @@ struct S }"; var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.indexer_getter), - Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.indexer_getter), - Diagnostic(RudeEditKind.ModifiersUpdate, "set", CSharpFeaturesResources.indexer_setter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int this[int x]", CSharpFeaturesResources.indexer_getter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int this[sbyte x]", CSharpFeaturesResources.indexer_getter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly int this[sbyte x]", CSharpFeaturesResources.indexer_setter), Diagnostic(RudeEditKind.ModifiersUpdate, "readonly get", CSharpFeaturesResources.indexer_getter), Diagnostic(RudeEditKind.ModifiersUpdate, "readonly set", CSharpFeaturesResources.indexer_setter)); } @@ -13794,17 +13926,6 @@ public void ParameterRename_Indexer2() "Update [int b]@33 -> [int x]@33"); } - [Fact] - public void ParameterModifierUpdate1() - { - var src1 = @"class C { public int this[int a, ref int b] { get { return 0; } } }"; - var src2 = @"class C { public int this[int a, int b] { get { return 0; } } }"; - - var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Update [ref int b]@33 -> [int b]@33"); - } - [Fact] public void ParameterInsert1() { @@ -13885,27 +14006,141 @@ public void ParameterReorderAndUpdate() "Update [int a]@24 -> [int c]@31"); } + [Theory] + [InlineData("string", "string?")] + [InlineData("object", "dynamic")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void Parameter_Type_Update_RuntimeTypeUnchanged(string oldType, string newType) + { + var src1 = "class C { static void M(" + oldType + " a) {} }"; + var src2 = "class C { static void M(" + newType + " a) {} }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void Parameter_Type_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C { static void M(" + oldType + " a) {} }"; + var src2 = "class C { static void M(" + newType + " a) {} }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, newType + " a", FeaturesResources.parameter)); + } + [Fact] - public void ParameterAttributeInsert_SupportedByRuntime() + public void Parameter_Type_Nullable() { - var attribute = "public class AAttribute : System.Attribute { }\n\n"; + var src1 = @" +#nullable enable +class C { static void M(string a) { } } +"; + var src2 = @" +#nullable disable +class C { static void M(string a) { } } +"; - var src1 = attribute + @"class C { public void M(int a) {} }"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics(); + } + + [Theory] + [InlineData("this")] + [InlineData("ref")] + [InlineData("out")] + [InlineData("params")] + public void Parameter_Modifier_Remove(string modifier) + { + var src1 = @"static class C { static void F(" + modifier + " int[] a) { } }"; + var src2 = @"static class C { static void F(int[] a) { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "int[] a", FeaturesResources.parameter)); + } + + [Theory] + [InlineData("int a = 1", "int a = 2")] + [InlineData("int a = 1", "int a")] + [InlineData("int a", "int a = 2")] + [InlineData("object a = null", "object a")] + [InlineData("object a", "object a = null")] + [InlineData("double a = double.NaN", "double a = 1.2")] + public void Parameter_Initializer_Update(string oldParameter, string newParameter) + { + var src1 = @"static class C { static void F(" + oldParameter + ") { } }"; + var src2 = @"static class C { static void F(" + newParameter + ") { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, newParameter, FeaturesResources.parameter)); + } + + [Fact] + public void Parameter_Initializer_NaN() + { + var src1 = @"static class C { static void F(double a = System.Double.NaN) { } }"; + var src2 = @"static class C { static void F(double a = double.NaN) { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics(); + } + + [Fact] + public void Parameter_Initializer_InsertDeleteUpdate() + { + var srcA1 = @"partial class C { }"; + var srcB1 = @"partial class C { public static void F(int x = 1) {} }"; + + var srcA2 = @"partial class C { public static void F(int x = 2) {} }"; + var srcB2 = @"partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.InitializerUpdate, "int x = 2", FeaturesResources.parameter) + }), + DocumentResults(), + }); + } + + [Fact] + public void Parameter_Attribute_Insert() + { + var attribute = "public class A : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M(int a) {} }"; var src2 = attribute + @"class C { public void M([A]int a) {} } "; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [int a]@72 -> [[A]int a]@72"); + "Update [int a]@63 -> [[A]int a]@63"); edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] - public void ParameterAttributeInsert_SupportedByRuntime_NonCustomAttribute() + public void Parameter_Attribute_Insert_SupportedByRuntime_NonCustomAttribute() { var src1 = @"class C { public void M(int a) {} }"; var src2 = @"class C { public void M([System.Runtime.InteropServices.InAttribute]int a) {} } "; @@ -13916,12 +14151,12 @@ public void ParameterAttributeInsert_SupportedByRuntime_NonCustomAttribute() "Update [int a]@24 -> [[System.Runtime.InteropServices.InAttribute]int a]@24"); edits.VerifyRudeDiagnostics( - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities, Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } [Fact] - public void ParameterAttributeInsert_SupportedByRuntime_SecurityAttribute1() + public void Parameter_Attribute_Insert_SupportedByRuntime_SecurityAttribute1() { var attribute = "public class AAttribute : System.Security.Permissions.SecurityAttribute { }\n\n"; @@ -13934,12 +14169,12 @@ public void ParameterAttributeInsert_SupportedByRuntime_SecurityAttribute1() "Update [int a]@101 -> [[A]int a]@101"); edits.VerifyRudeDiagnostics( - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities, Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } [Fact] - public void ParameterAttributeInsert_SupportedByRuntime_SecurityAttribute2() + public void Parameter_Attribute_Insert_SupportedByRuntime_SecurityAttribute2() { var attribute = "public class BAttribute : System.Security.Permissions.SecurityAttribute { }\n\n" + "public class AAttribute : BAttribute { }\n\n"; @@ -13953,12 +14188,12 @@ public void ParameterAttributeInsert_SupportedByRuntime_SecurityAttribute2() "Update [int a]@143 -> [[A]int a]@143"); edits.VerifyRudeDiagnostics( - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities, Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } [Fact] - public void ParameterAttributeInsert1() + public void Parameter_Attribute_Insert_NotSupportedByRuntime1() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -13974,7 +14209,7 @@ public void ParameterAttributeInsert1() } [Fact] - public void ParameterAttributeInsert2() + public void Parameter_Attribute_Insert_NotSupportedByRuntime2() { var attribute = "public class AAttribute : System.Attribute { }\n\n" + "public class BAttribute : System.Attribute { }\n\n"; @@ -13991,7 +14226,7 @@ public void ParameterAttributeInsert2() } [Fact] - public void ParameterAttributeDelete() + public void Parameter_Attribute_Delete_NotSupportedByRuntime() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -14007,7 +14242,7 @@ public void ParameterAttributeDelete() } [Fact] - public void ParameterAttributeUpdate() + public void Parameter_Attribute_Update_NotSupportedByRuntime() { var attribute = "public class AAttribute : System.Attribute { }\n\n" + "public class BAttribute : System.Attribute { }\n\n"; @@ -14024,9 +14259,48 @@ public void ParameterAttributeUpdate() Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } - #endregion + [Fact] + public void Parameter_Attribute_Update() + { + var attribute = "class A : System.Attribute { public A(int x) {} } "; - #region Method Type Parameter + var src1 = attribute + "class C { void F([A(0)]int a) {} }"; + var src2 = attribute + "class C { void F([A(1)]int a) {} }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[A(0)]int a]@67 -> [[A(1)]int a]@67"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")) }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void Parameter_Attribute_Update_WithBodyUpdate() + { + var attribute = "class A : System.Attribute { public A(int x) {} } "; + + var src1 = attribute + "class C { void F([A(0)]int a) { F(0); } }"; + var src2 = attribute + "class C { void F([A(1)]int a) { F(1); } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [void F([A(0)]int a) { F(0); }]@60 -> [void F([A(1)]int a) { F(1); }]@60", + "Update [[A(0)]int a]@67 -> [[A(1)]int a]@67"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")) }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + #endregion + + #region Method Type Parameter [Fact] public void MethodTypeParameterInsert1() @@ -14111,7 +14385,7 @@ public void MethodTypeParameterReorderAndUpdate() } [Fact] - public void MethodTypeParameterAttributeInsert1() + public void MethodTypeParameter_Attribute_Insert1() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -14129,7 +14403,7 @@ public void MethodTypeParameterAttributeInsert1() } [Fact] - public void MethodTypeParameterAttributeInsert2() + public void MethodTypeParameter_Attribute_Insert2() { var attribute = "public class AAttribute : System.Attribute { }\n\n" + "public class BAttribute : System.Attribute { }\n\n"; @@ -14148,7 +14422,7 @@ public void MethodTypeParameterAttributeInsert2() } [Fact] - public void MethodTypeParameterAttributeDelete() + public void MethodTypeParameter_Attribute_Delete() { var attribute = "public class AAttribute : System.Attribute { }\n\n"; @@ -14166,7 +14440,7 @@ public void MethodTypeParameterAttributeDelete() } [Fact] - public void MethodTypeParameterAttributeUpdate() + public void MethodTypeParameter_Attribute_Update_NotSupportedByRuntime() { var attribute = "public class AAttribute : System.Attribute { }\n\n" + "public class BAttribute : System.Attribute { }\n\n"; @@ -14183,6 +14457,44 @@ public void MethodTypeParameterAttributeUpdate() Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } + [Fact] + public void MethodTypeParameter_Attribute_Update() + { + var attribute = "class A : System.Attribute { public A(int x) {} } "; + + var src1 = attribute + "class C { void F<[A(0)]T>(T a) {} }"; + var src2 = attribute + "class C { void F<[A(1)]T>(T a) {} }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[A(0)]T]@67 -> [[A(1)]T]@67"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")) }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void MethodTypeParameter_Attribute_Update_WithBodyUpdate() + { + var attribute = "class A : System.Attribute { public A(int x) {} } "; + + var src1 = attribute + "class C { void F<[A(0)]T>(T a) { F(0); } }"; + var src2 = attribute + "class C { void F<[A(1)]T>(T a) { F(1); } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [void F<[A(0)]T>(T a) { F(0); }]@60 -> [void F<[A(1)]T>(T a) { F(1); }]@60", + "Update [[A(0)]T]@67 -> [[A(1)]T]@67"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.GenericMethodUpdate, "void F<[A(1)]T>(T a)", FeaturesResources.method), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); + } + #endregion #region Type Type Parameter @@ -14200,7 +14512,7 @@ public void TypeTypeParameterInsert1() "Insert [A]@8"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.Insert, "A", FeaturesResources.type_parameter)); } [Fact] @@ -14244,7 +14556,7 @@ public void TypeTypeParameterDelete2() "Delete [A]@8"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_parameter)); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.type_parameter, "A"))); } [Fact] @@ -14345,7 +14657,7 @@ public void TypeTypeParameterAttributeInsert_SupportedByRuntime() edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")) }, - capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); } [Fact] @@ -14387,53 +14699,94 @@ public void TypeTypeParameterAttributeUpdate() #region Type Parameter Constraints - [Fact] - public void TypeConstraintInsert_Class() + [Theory] + [InlineData("nonnull")] + [InlineData("struct")] + [InlineData("class")] + [InlineData("new()")] + [InlineData("unmanaged")] + [InlineData("System.IDisposable")] + [InlineData("System.Delegate")] + public void TypeConstraint_Insert(string newConstraint) { - var src1 = "class C { }"; - var src2 = "class C where T : class { }"; + var src1 = "class C { }"; + var src2 = "class C where T : " + newConstraint + " { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [where T : class]@11"); + "Insert [where T : " + newConstraint + "]@13"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "where T : class", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.ChangingConstraints, "where T : " + newConstraint, FeaturesResources.type_parameter)); } - [Fact] - public void TypeConstraintInsert_Unmanaged() + [Theory] + [InlineData("nonnull")] + [InlineData("struct")] + [InlineData("class")] + [InlineData("new()")] + [InlineData("unmanaged")] + [InlineData("System.IDisposable")] + [InlineData("System.Delegate")] + public void TypeConstraint_Delete(string oldConstraint) { - var src1 = "class C { }"; - var src2 = "class C where T : unmanaged { }"; + var src1 = "class C where T : " + oldConstraint + " { }"; + var src2 = "class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [where T : unmanaged]@11"); + "Delete [where T : " + oldConstraint + "]@13"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "where T : unmanaged", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter)); } - [Fact] - public void TypeConstraintInsert_DoubleStatement_New() + [Theory] + [InlineData("string", "string?")] + [InlineData("(int a, int b)", "(int a, int c)")] + public void TypeConstraint_Update_RuntimeTypeUnchanged(string oldType, string newType) { - var src1 = "class C where T : class { }"; - var src2 = "class C where S : new() where T : class { }"; + // note: dynamic is not allowed in constraints + var src1 = "class C where T : System.Collections.Generic.List<" + oldType + "> {}"; + var src2 = "class C where T : System.Collections.Generic.List<" + newType + "> {}"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Insert [where S : new()]@13"); + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))); + } + + [Theory] + [InlineData("int", "string")] + [InlineData("int", "int?")] + [InlineData("(int a, int b)", "(int a, double b)")] + public void TypeConstraint_Update_RuntimeTypeChanged(string oldType, string newType) + { + var src1 = "class C where T : System.Collections.Generic.List<" + oldType + "> {}"; + var src2 = "class C where T : System.Collections.Generic.List<" + newType + "> {}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingConstraints, "where T : System.Collections.Generic.List<" + newType + ">", FeaturesResources.type_parameter)); + } + + [Fact] + public void TypeConstraint_Delete_WithParameter() + { + var src1 = "class C where S : new() where T : class { }"; + var src2 = "class C where S : new() { }"; + + var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "where S : new()", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.type_parameter, "T"))); } [Fact] - public void TypeConstraintInsert_DoubleStatement_Unmanaged() + public void TypeConstraint_MultipleClauses_Insert() { var src1 = "class C where T : class { }"; var src2 = "class C where S : unmanaged where T : class { }"; @@ -14444,115 +14797,733 @@ public void TypeConstraintInsert_DoubleStatement_Unmanaged() "Insert [where S : unmanaged]@13"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "where S : unmanaged", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.ChangingConstraints, "where S : unmanaged", FeaturesResources.type_parameter)); } [Fact] - public void TypeConstraintDelete_Class() + public void TypeConstraint_MultipleClauses_Delete() { - var src1 = "class C where T : class { }"; - var src2 = "class C { }"; + var src1 = "class C where S : new() where T : class { }"; + var src2 = "class C where T : class { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Delete [where T : class]@13"); + "Delete [where S : new()]@13"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter)); } [Fact] - public void TypeConstraintDelete_Unmanaged() + public void TypeConstraint_MultipleClauses_Reorder() { - var src1 = "class C where T : unmanaged { }"; - var src2 = "class C { }"; + var src1 = "class C where S : struct where T : class { }"; + var src2 = "class C where T : class where S : struct { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Delete [where T : unmanaged]@13"); + "Reorder [where T : class]@30 -> @13"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_constraint)); + edits.VerifyRudeDiagnostics(); } [Fact] - public void TypeConstraintDelete_DoubleStatement_New() + public void TypeConstraint_MultipleClauses_UpdateAndReorder() { var src1 = "class C where S : new() where T : class { }"; - var src2 = "class C where T : class { }"; + var src2 = "class C where T : class, I where S : class, new() { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Delete [where S : new()]@13"); + "Reorder [where T : class]@29 -> @13", + "Reorder [T]@10 -> @8", + "Update [where T : class]@29 -> [where T : class, I]@13", + "Update [where S : new()]@13 -> [where S : class, new()]@32"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.Move, "T", FeaturesResources.type_parameter), + Diagnostic(RudeEditKind.ChangingConstraints, "where T : class, I", FeaturesResources.type_parameter), + Diagnostic(RudeEditKind.ChangingConstraints, "where S : class, new()", FeaturesResources.type_parameter)); } + #endregion + + #region Top Level Statements + [Fact] - public void TypeConstraintDelete_DoubleStatement_Unmanaged() + public void TopLevelStatements_Update() { - var src1 = "class C where S : unmanaged where T : class { }"; - var src2 = "class C where T : class { }"; + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var src2 = @" +using System; +Console.WriteLine(""Hello World""); +"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Delete [where S : unmanaged]@13"); + edits.VerifyEdits("Update [Console.WriteLine(\"Hello\");]@19 -> [Console.WriteLine(\"Hello World\");]@19"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.type_constraint)); + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } [Fact] - public void TypeConstraintReorder_Class() + public void TopLevelStatements_InsertAndUpdate() { - var src1 = "class C where S : struct where T : class { }"; - var src2 = "class C where T : class where S : struct { }"; + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var src2 = @" +using System; +Console.WriteLine(""Hello World""); +Console.WriteLine(""What is your name?""); +var name = Console.ReadLine(); +"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Reorder [where T : class]@30 -> @13"); + "Update [Console.WriteLine(\"Hello\");]@19 -> [Console.WriteLine(\"Hello World\");]@19", + "Insert [Console.WriteLine(\"What is your name?\");]@54", + "Insert [var name = Console.ReadLine();]@96"); - edits.VerifyRudeDiagnostics(); + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + + [Fact] + public void TopLevelStatements_Insert_NoImplicitMain() + { + var src1 = @" +using System; +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello World""); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Insert [Console.WriteLine(\"Hello World\");]@19"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("$.
$"))); } [Fact] - public void TypeConstraintReorder_Unmanaged() + public void TopLevelStatements_Insert_ImplicitMain() { - var src1 = "class C where S : struct where T : unmanaged { }"; - var src2 = "class C where T : unmanaged where S : struct { }"; + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var src2 = @" +using System; +Console.WriteLine(""Hello""); +Console.WriteLine(""World""); +"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Reorder [where T : unmanaged]@30 -> @13"); + edits.VerifyEdits("Insert [Console.WriteLine(\"World\");]@48"); - edits.VerifyRudeDiagnostics(); + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); } [Fact] - public void TypeConstraintUpdateAndReorder() + public void TopLevelStatements_Delete_NoImplicitMain() { - var src1 = "class C where S : new() where T : class { }"; - var src2 = "class C where T : class, I where S : class, new() { }"; + var src1 = @" +using System; + +Console.WriteLine(""Hello World""); +"; + var src2 = @" +using System; +"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Reorder [where T : class]@29 -> @13", - "Reorder [T]@10 -> @8", - "Update [where T : class]@29 -> [where T : class, I]@13", - "Update [where S : new()]@13 -> [where S : class, new()]@32"); + edits.VerifyEdits("Delete [Console.WriteLine(\"Hello World\");]@19"); + + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_Delete_ImplicitMain() + { + var src1 = @" +using System; + +Console.WriteLine(""Hello""); +Console.WriteLine(""World""); +"; + var src2 = @" +using System; + +Console.WriteLine(""Hello""); +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Delete [Console.WriteLine(\"World\");]@48"); + + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + + [Fact] + public void TopLevelStatements_StackAlloc() + { + var src1 = @"unsafe { var x = stackalloc int[3]; System.Console.Write(1); }"; + var src2 = @"unsafe { var x = stackalloc int[3]; System.Console.Write(2); }"; + + var edits = GetTopEdits(src1, src2); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "T", FeaturesResources.type_parameter), - Diagnostic(RudeEditKind.TypeUpdate, "where T : class, I", FeaturesResources.type_constraint), - Diagnostic(RudeEditKind.TypeUpdate, "where S : class, new()", FeaturesResources.type_constraint)); + Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc", CSharpFeaturesResources.global_statement)); + } + + [Fact] + public void TopLevelStatements_VoidToInt1() + { + var src1 = @" +using System; + +Console.Write(1); +"; + var src2 = @" +using System; + +Console.Write(1); +return 1; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); + } + + [Fact] + public void TopLevelStatements_VoidToInt2() + { + var src1 = @" +using System; + +Console.Write(1); + +return; +"; + var src2 = @" +using System; + +Console.Write(1); +return 1; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); + } + + [Fact] + public void TopLevelStatements_VoidToInt3() + { + var src1 = @" +using System; + +Console.Write(1); + +int Goo() +{ + return 1; +} +"; + var src2 = @" +using System; + +Console.Write(1); +return 1; + +int Goo() +{ + return 1; +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); + } + + [Fact] + public void TopLevelStatements_AddAwait() + { + var src1 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +"; + var src2 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +await Task.Delay(200); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "await", CSharpFeaturesResources.await_expression)); + } + + [Fact] + public void TopLevelStatements_DeleteAwait() + { + var src1 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +await Task.Delay(200); +"; + var src2 = @" +using System.Threading.Tasks; + +await Task.Delay(100); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); + } + + [Fact] + public void TopLevelStatements_VoidToTask() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "await Task.Delay(100);")); + } + + [Fact] + public void TopLevelStatements_TaskToTaskInt() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +return 1; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;")); + } + + [Fact] + public void TopLevelStatements_VoidToTaskInt() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +return await GetInt(); + +Task GetInt() +{ + return Task.FromResult(1); +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return await GetInt();")); + } + + [Fact] + public void TopLevelStatements_IntToVoid1() + { + var src1 = @" +using System; + +Console.Write(1); + +return 1; +"; + var src2 = @" +using System; + +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);")); + } + + [Fact] + public void TopLevelStatements_IntToVoid2() + { + var src1 = @" +using System; + +Console.Write(1); + +return 1; +"; + var src2 = @" +using System; + +Console.Write(1); +return; +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return;")); + } + + [Fact] + public void TopLevelStatements_IntToVoid3() + { + var src1 = @" +using System; + +Console.Write(1); +return 1; + +int Goo() +{ + return 1; +} +"; + var src2 = @" +using System; + +Console.Write(1); + +int Goo() +{ + return 1; +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "int Goo()\r\n{\r\n return 1;\r\n}")); + } + + [Fact] + public void TopLevelStatements_IntToVoid4() + { + var src1 = @" +using System; + +Console.Write(1); +return 1; + +public class C +{ + public int Goo() + { + return 1; + } +} +"; + var src2 = @" +using System; + +Console.Write(1); + +public class C +{ + public int Goo() + { + return 1; + } +} +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);")); + } + + [Fact] + public void TopLevelStatements_TaskToVoid() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"), + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); + } + + [Fact] + public void TopLevelStatements_TaskIntToTask() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +return 1; +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +await Task.Delay(100); +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);")); + } + + [Fact] + public void TopLevelStatements_TaskIntToVoid() + { + var src1 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +return await GetInt(); + +Task GetInt() +{ + return Task.FromResult(1); +} +"; + var src2 = @" +using System; +using System.Threading.Tasks; + +Console.Write(1); +"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"), + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.await_expression)); + } + + [Fact] + public void TopLevelStatements_WithLambda_Insert() + { + var src1 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; +"; + var src2 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(1); +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"), syntaxMap[0]) }); + } + + [Fact] + public void TopLevelStatements_WithLambda_Update() + { + var src1 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(1); + +public class C { } +"; + var src2 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(2); + +public class C { } +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"), syntaxMap[0]) }); + } + + [Fact] + public void TopLevelStatements_WithLambda_Delete() + { + var src1 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +Console.WriteLine(1); + +public class C { } +"; + var src2 = @" +using System; + +Func a = () => { return 1; }; +Func> b = () => () => { return 1; }; + +public class C { } +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"), syntaxMap[0]) }); + } + + [Fact] + public void TopLevelStatements_UpdateMultiple() + { + var src1 = @" +using System; + +Console.WriteLine(1); +Console.WriteLine(2); + +public class C { } +"; + var src2 = @" +using System; + +Console.WriteLine(3); +Console.WriteLine(4); + +public class C { } +"; + var edits = GetTopEdits(src1, src2); + + // Since each individual statement is a separate update to a separate node, this just validates we correctly + // only analyze the things once + edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$"))); + } + + [Fact] + public void TopLevelStatements_MoveToOtherFile() + { + var srcA1 = @" +using System; + +Console.WriteLine(1); + +public class A +{ +}"; + var srcB1 = @" +using System; + +public class B +{ +}"; + + var srcA2 = @" +using System; + +public class A +{ +}"; + var srcB2 = @" +using System; + +Console.WriteLine(2); + +public class B +{ +}"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new [] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("$.
$")) }), + }); } #endregion diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index e0d74c1c3fd7c..bbeaec5418239 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -170,6 +170,11 @@ namespace NS code, indentationLine: 3, expectedIndentation: 4); + + AssertSmartIndent( + code, + indentationLine: 4, + expectedIndentation: 4); } [WpfFact] @@ -203,6 +208,32 @@ namespace NS expectedIndentation: 4); } + [WpfFact] + [Trait(Traits.Feature, Traits.Features.SmartIndent)] + public void FileScopedNamespace() + { + var code = @"using System; + +namespace NS; + + +"; + AssertSmartIndent( + code, + indentationLine: 1, + expectedIndentation: 0); + + AssertSmartIndent( + code, + indentationLine: 2, + expectedIndentation: 0); + + AssertSmartIndent( + code, + indentationLine: 4, + expectedIndentation: 0); + } + [WpfFact] [Trait(Traits.Feature, Traits.Features.SmartIndent)] public void Class() diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs index af3a2f82a80f5..8156a2a56398e 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs @@ -107,6 +107,27 @@ await TestAsync(testHost, composition, content, async w => }); } + [Theory] + [CombinatorialData] + public async Task FindClassInFileScopedNamespace(TestHost testHost, Composition composition) + { + var content = XElement.Parse(@" + + + +namespace FileScopedNS; +class Goo { } + + + +"); + await TestAsync(testHost, composition, content, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); + } + [Theory] [CombinatorialData] public async Task FindVerbatimClass(TestHost testHost, Composition composition) diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs index c476df6205119..d1fe367290ccb 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs @@ -401,5 +401,19 @@ public async Task TestAfterRecord() await VerifyKeywordAsync( @"record $$"); } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterFileScopedNamespace() + { + await VerifyKeywordAsync( +@"namespace NS; $$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterAttributeFileScopedNamespace() + { + await VerifyKeywordAsync( +@"namespace NS; [Attr] $$"); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs index 88f2441e98b7d..d05ad1b05aedc 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs @@ -83,6 +83,34 @@ await VerifyKeywordAsync(SourceCodeKind.Regular, $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterPreviousFileScopedNamespace() + { + await VerifyAbsenceAsync(SourceCodeKind.Regular, +@"namespace N; +$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterUsingInFileScopedNamespace() + { + await VerifyAbsenceAsync(SourceCodeKind.Regular, +@"namespace N; +using U; +$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterUsingInNamespace() + { + await VerifyKeywordAsync(SourceCodeKind.Regular, +@"namespace N +{ + using U; + $$ +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterPreviousNamespace_Interactive() { @@ -107,6 +135,26 @@ await VerifyAbsenceAsync(SourceCodeKind.Script, $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestNotAfterExternInFileScopedNamespace() + { + await VerifyAbsenceAsync(SourceCodeKind.Regular, +@"namespace N; +extern alias A; +$$"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterExternInNamespace() + { + await VerifyKeywordAsync(SourceCodeKind.Regular, +@"namespace N +{ + extern alias A; + $$ +}"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterUsing() { diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs index ab43e259be52d..4ef315b6fa638 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs @@ -194,6 +194,14 @@ await VerifyKeywordAsync( $$"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestAfterFileScopedNamespace() + { + await VerifyKeywordAsync( +@"namespace N; +$$"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestNotAfterUsingKeyword_InsideNamespace() { @@ -416,6 +424,14 @@ namespace NS {}"); } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + public async Task TestBeforeFileScopedNamespace() + { + await VerifyKeywordAsync( +@"$$ +namespace NS;"); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestBeforeClass() { diff --git a/src/EditorFeatures/Core.Wpf/Adornments/AdornmentManager.cs b/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs similarity index 67% rename from src/EditorFeatures/Core.Wpf/Adornments/AdornmentManager.cs rename to src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs index 1ffa5890307cf..c4d8b92712a1a 100644 --- a/src/EditorFeatures/Core.Wpf/Adornments/AdornmentManager.cs +++ b/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs @@ -22,44 +22,38 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments /// /// UI manager for graphic overlay tags. These tags will simply paint something related to the text. /// - internal class AdornmentManager where T : GraphicsTag + internal abstract class AbstractAdornmentManager where T : GraphicsTag { - private readonly object _invalidatedSpansLock = new object(); + private readonly object _invalidatedSpansLock = new(); private readonly IThreadingContext _threadingContext; - /// View that created us. - private readonly IWpfTextView _textView; - - /// Layer where we draw adornments. - private readonly IAdornmentLayer _adornmentLayer; - - /// Aggregator that tells us where to draw. - private readonly ITagAggregator _tagAggregator; - /// Notification system about operations we do private readonly IAsynchronousOperationListener _asyncListener; /// Spans that are invalidated, and need to be removed from the layer.. private List _invalidatedSpans; - public static AdornmentManager Create( - IThreadingContext threadingContext, - IWpfTextView textView, - IViewTagAggregatorFactoryService aggregatorService, - IAsynchronousOperationListener asyncListener, - string adornmentLayerName) - { - Contract.ThrowIfNull(threadingContext); - Contract.ThrowIfNull(textView); - Contract.ThrowIfNull(aggregatorService); - Contract.ThrowIfNull(adornmentLayerName); - Contract.ThrowIfNull(asyncListener); + /// View that created us. + protected readonly IWpfTextView TextView; - return new AdornmentManager(threadingContext, textView, aggregatorService, asyncListener, adornmentLayerName); - } + /// Layer where we draw adornments. + protected readonly IAdornmentLayer AdornmentLayer; + + /// Aggregator that tells us where to draw. + protected readonly ITagAggregator TagAggregator; - internal AdornmentManager( + /// + /// MUST BE CALLED ON UI THREAD!!!! This method touches WPF. + /// + /// This is where we apply visuals to the text. + /// + /// It happens when another region of the view becomes visible or there is a change in tags. + /// For us the end result is the same - get tags from tagger and update visuals correspondingly. + /// + protected abstract void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection); + + internal AbstractAdornmentManager( IThreadingContext threadingContext, IWpfTextView textView, IViewTagAggregatorFactoryService tagAggregatorFactoryService, @@ -73,8 +67,8 @@ internal AdornmentManager( Contract.ThrowIfNull(asyncListener); _threadingContext = threadingContext; - _textView = textView; - _adornmentLayer = textView.GetAdornmentLayer(adornmentLayerName); + TextView = textView; + AdornmentLayer = textView.GetAdornmentLayer(adornmentLayerName); textView.LayoutChanged += OnLayoutChanged; _asyncListener = asyncListener; @@ -82,20 +76,20 @@ internal AdornmentManager( Contract.ThrowIfFalse(textView.VisualElement.Dispatcher.CheckAccess()); textView.Closed += OnTextViewClosed; - _tagAggregator = tagAggregatorFactoryService.CreateTagAggregator(textView); + TagAggregator = tagAggregatorFactoryService.CreateTagAggregator(textView); - _tagAggregator.TagsChanged += OnTagsChanged; + TagAggregator.TagsChanged += OnTagsChanged; } private void OnTextViewClosed(object sender, System.EventArgs e) { // release the aggregator - _tagAggregator.TagsChanged -= OnTagsChanged; - _tagAggregator.Dispose(); + TagAggregator.TagsChanged -= OnTagsChanged; + TagAggregator.Dispose(); // unhook from view - _textView.Closed -= OnTextViewClosed; - _textView.LayoutChanged -= OnLayoutChanged; + TextView.Closed -= OnTextViewClosed; + TextView.LayoutChanged -= OnLayoutChanged; // At this point, this object should be available for garbage collection. } @@ -110,10 +104,10 @@ private void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) using (_asyncListener.BeginAsyncOperation(GetType() + ".OnLayoutChanged")) { // Make sure we're on the UI thread. - Contract.ThrowIfFalse(_textView.VisualElement.Dispatcher.CheckAccess()); + Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); var reformattedSpans = e.NewOrReformattedSpans; - var viewSnapshot = _textView.TextSnapshot; + var viewSnapshot = TextView.TextSnapshot; // No need to remove tags as these spans are reformatted anyways. UpdateSpans_CallOnlyOnUIThread(reformattedSpans, removeOldTags: false); @@ -182,7 +176,7 @@ private void OnTagsChanged(object sender, TagsChangedEventArgs e) if (needToScheduleUpdate) { // schedule an update - _threadingContext.JoinableTaskFactory.WithPriority(_textView.VisualElement.Dispatcher, DispatcherPriority.Render).RunAsync(async () => + _threadingContext.JoinableTaskFactory.WithPriority(TextView.VisualElement.Dispatcher, DispatcherPriority.Render).RunAsync(async () => { using (_asyncListener.BeginAsyncOperation(GetType() + ".OnTagsChanged.2")) { @@ -199,13 +193,13 @@ private void OnTagsChanged(object sender, TagsChangedEventArgs e) /// /// This function is used to update invalidates spans. ///
- private void UpdateInvalidSpans() + protected void UpdateInvalidSpans() { using (_asyncListener.BeginAsyncOperation(GetType().Name + ".UpdateInvalidSpans.1")) using (Logger.LogBlock(FunctionId.Tagger_AdornmentManager_UpdateInvalidSpans, CancellationToken.None)) { // this method should only run on UI thread as we do WPF here. - Contract.ThrowIfFalse(_textView.VisualElement.Dispatcher.CheckAccess()); + Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); List invalidated; lock (_invalidatedSpansLock) @@ -214,39 +208,28 @@ private void UpdateInvalidSpans() _invalidatedSpans = null; } - if (_textView.IsClosed) + if (TextView.IsClosed) { return; // already closed } if (invalidated != null) { - var viewSnapshot = _textView.TextSnapshot; + var viewSnapshot = TextView.TextSnapshot; var invalidatedNormalized = TranslateAndNormalize(invalidated, viewSnapshot); UpdateSpans_CallOnlyOnUIThread(invalidatedNormalized, removeOldTags: true); } } } - /// - /// MUST BE CALLED ON UI THREAD!!!! This method touches WPF. - /// - /// This is where we apply visuals to the text. - /// - /// It happens when another region of the view becomes visible or there is a change in tags. - /// For us the end result is the same - get tags from tagger and update visuals correspondingly. - /// - private void UpdateSpans_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection, bool removeOldTags) + protected void UpdateSpans_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection, bool removeOldTags) { Contract.ThrowIfNull(changedSpanCollection); // this method should only run on UI thread as we do WPF here. - Contract.ThrowIfFalse(_textView.VisualElement.Dispatcher.CheckAccess()); + Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); - var viewSnapshot = _textView.TextSnapshot; - var visualSnapshot = _textView.VisualSnapshot; - - var viewLines = _textView.TextViewLines; + var viewLines = TextView.TextViewLines; if (viewLines == null || viewLines.Count == 0) { return; // nothing to draw on @@ -260,72 +243,58 @@ private void UpdateSpans_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection cha // is there any effect on the view? if (viewLines.IntersectsBufferSpan(changedSpan)) { - _adornmentLayer.RemoveAdornmentsByVisualSpan(changedSpan); + AdornmentLayer.RemoveAdornmentsByVisualSpan(changedSpan); } } } - foreach (var changedSpan in changedSpanCollection) + AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(changedSpanCollection); + } + + protected bool ShouldDrawTag(SnapshotSpan snapshotSpan, IMappingTagSpan mappingTagSpan) + { + var mappedPoint = GetMappedPoint(snapshotSpan, mappingTagSpan); + if (mappedPoint is null) { - // is there any effect on the view? - if (!viewLines.IntersectsBufferSpan(changedSpan)) - { - continue; - } + return false; + } - var tagSpans = _tagAggregator.GetTags(changedSpan); - foreach (var tagMappingSpan in tagSpans) - { - // We don't want to draw line separators if they would intersect a collapsed outlining - // region. So we test if we can map the start of the line separator up to our visual - // snapshot. If we can't, then we just skip it. - var point = tagMappingSpan.Span.Start.GetPoint(changedSpan.Snapshot, PositionAffinity.Predecessor); - if (point == null) - { - continue; - } + if (!TryMapToSingleSnapshotSpan(mappingTagSpan.Span, TextView.TextSnapshot, out var span)) + { + return false; + } - var mappedPoint = _textView.BufferGraph.MapUpToSnapshot( - point.Value, PointTrackingMode.Negative, PositionAffinity.Predecessor, _textView.VisualSnapshot); - if (mappedPoint == null) - { - continue; - } + if (!TextView.TextViewLines.IntersectsBufferSpan(span)) + { + return false; + } - if (!TryMapToSingleSnapshotSpan(tagMappingSpan.Span, viewSnapshot, out var span)) - { - continue; - } + return true; + } - if (!viewLines.IntersectsBufferSpan(span)) - { - // span is outside of the view so we will not get geometry for it, but may - // spent a lot of time trying. - continue; - } + protected SnapshotPoint? GetMappedPoint(SnapshotSpan snapshotSpan, IMappingTagSpan mappingTagSpan) + { + var point = mappingTagSpan.Span.Start.GetPoint(snapshotSpan.Snapshot, PositionAffinity.Predecessor); + if (point == null) + { + return null; + } - // add the visual to the adornment layer. - var geometry = viewLines.GetMarkerGeometry(span); - if (geometry != null) - { - var tag = tagMappingSpan.Tag; - var graphicsResult = tag.GetGraphics(_textView, geometry); - _adornmentLayer.AddAdornment( - behavior: AdornmentPositioningBehavior.TextRelative, - visualSpan: span, - tag: tag, - adornment: graphicsResult.VisualElement, - removedCallback: delegate { graphicsResult.Dispose(); }); - } - } + var mappedPoint = TextView.BufferGraph.MapUpToSnapshot( + point.Value, PointTrackingMode.Negative, PositionAffinity.Predecessor, TextView.VisualSnapshot); + if (mappedPoint == null) + { + return null; } + + return mappedPoint; } // Map the mapping span to the visual snapshot. note that as a result of projection // topology, originally single span may be mapped into several spans. Visual adornments do // not make much sense on disjoint spans. We will not decorate spans that could not make it // in one piece. - private static bool TryMapToSingleSnapshotSpan(IMappingSpan mappingSpan, ITextSnapshot viewSnapshot, out SnapshotSpan span) + protected static bool TryMapToSingleSnapshotSpan(IMappingSpan mappingSpan, ITextSnapshot viewSnapshot, out SnapshotSpan span) { // IMappingSpan.GetSpans is a surprisingly expensive function that allocates multiple // lists and collection if the view buffer is same as anchor we could just map the diff --git a/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManagerProvider.cs b/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManagerProvider.cs index b14b99ceca4df..c6265c6c55bc8 100644 --- a/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManagerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManagerProvider.cs @@ -18,23 +18,25 @@ internal abstract class AbstractAdornmentManagerProvider : IWpfTextViewCreationListener where TTag : GraphicsTag { - private readonly IThreadingContext _threadingContext; - private readonly IViewTagAggregatorFactoryService _tagAggregatorFactoryService; - private readonly IAsynchronousOperationListener _asyncListener; + protected readonly IThreadingContext ThreadingContext; + protected readonly IViewTagAggregatorFactoryService TagAggregatorFactoryService; + protected readonly IAsynchronousOperationListener AsyncListener; protected AbstractAdornmentManagerProvider( IThreadingContext threadingContext, IViewTagAggregatorFactoryService tagAggregatorFactoryService, IAsynchronousOperationListenerProvider listenerProvider) { - _threadingContext = threadingContext; - _tagAggregatorFactoryService = tagAggregatorFactoryService; - _asyncListener = listenerProvider.GetListener(this.FeatureAttributeName); + ThreadingContext = threadingContext; + TagAggregatorFactoryService = tagAggregatorFactoryService; + AsyncListener = listenerProvider.GetListener(this.FeatureAttributeName); } protected abstract string FeatureAttributeName { get; } protected abstract string AdornmentLayerName { get; } + protected abstract void CreateAdornmentManager(IWpfTextView textView); + public void TextViewCreated(IWpfTextView textView) { if (textView == null) @@ -47,8 +49,7 @@ public void TextViewCreated(IWpfTextView textView) return; } - // the manager keeps itself alive by listening to text view events. - AdornmentManager.Create(_threadingContext, textView, _tagAggregatorFactoryService, _asyncListener, AdornmentLayerName); + CreateAdornmentManager(textView); } } } diff --git a/src/EditorFeatures/Core.Wpf/Adornments/GraphicsTag.cs b/src/EditorFeatures/Core.Wpf/Adornments/GraphicsTag.cs index 572d8149448a4..79c77bae7b240 100644 --- a/src/EditorFeatures/Core.Wpf/Adornments/GraphicsTag.cs +++ b/src/EditorFeatures/Core.Wpf/Adornments/GraphicsTag.cs @@ -7,6 +7,7 @@ using System.Windows.Media; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Formatting; using Microsoft.VisualStudio.Text.Tagging; namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments @@ -45,6 +46,6 @@ protected virtual void Initialize(IWpfTextView view) /// /// This method allows corresponding adornment manager to ask for a graphical glyph. /// - public abstract GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds); + public abstract GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds, TextFormattingRunProperties format); } } diff --git a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs new file mode 100644 index 0000000000000..1d21a3f730845 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs @@ -0,0 +1,70 @@ +// 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.Editor.Implementation.Adornments; +using Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.LineSeparators +{ + internal class LineSeparatorAdornmentManager : AbstractAdornmentManager + { + public LineSeparatorAdornmentManager(IThreadingContext threadingContext, IWpfTextView textView, + IViewTagAggregatorFactoryService tagAggregatorFactoryService, IAsynchronousOperationListener asyncListener, string adornmentLayerName) + : base(threadingContext, textView, tagAggregatorFactoryService, asyncListener, adornmentLayerName) + { + } + + protected override void AddAdornmentsToAdornmentLayer_CallOnlyOnUIThread(NormalizedSnapshotSpanCollection changedSpanCollection) + { + // this method should only run on UI thread as we do WPF here. + Contract.ThrowIfFalse(TextView.VisualElement.Dispatcher.CheckAccess()); + + var viewSnapshot = TextView.TextSnapshot; + var viewLines = TextView.TextViewLines; + + foreach (var changedSpan in changedSpanCollection) + { + // is there any effect on the view? + if (!viewLines.IntersectsBufferSpan(changedSpan)) + { + continue; + } + + var tagSpans = TagAggregator.GetTags(changedSpan); + foreach (var tagMappingSpan in tagSpans) + { + if (!ShouldDrawTag(changedSpan, tagMappingSpan)) + { + continue; + } + + if (!TryMapToSingleSnapshotSpan(tagMappingSpan.Span, TextView.TextSnapshot, out var span)) + { + continue; + } + + // add the visual to the adornment layer. + var geometry = viewLines.GetMarkerGeometry(span); + if (geometry != null) + { + var tag = tagMappingSpan.Tag; + var graphicsResult = tag.GetGraphics(TextView, geometry, format: null); + AdornmentLayer.AddAdornment( + behavior: AdornmentPositioningBehavior.TextRelative, + visualSpan: span, + tag: tag, + adornment: graphicsResult.VisualElement, + removedCallback: delegate { graphicsResult.Dispose(); }); + } + } + } + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManagerProvider.cs b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManagerProvider.cs index 04596b37595cf..8c07406d74aab 100644 --- a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManagerProvider.cs +++ b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManagerProvider.cs @@ -7,6 +7,7 @@ using System; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; +using Microsoft.CodeAnalysis.Editor.LineSeparators; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; @@ -49,5 +50,11 @@ public LineSeparatorAdornmentManagerProvider( protected override string FeatureAttributeName => FeatureAttribute.LineSeparators; protected override string AdornmentLayerName => LayerName; + + protected override void CreateAdornmentManager(IWpfTextView textView) + { + // the manager keeps itself alive by listening to text view events. + _ = new LineSeparatorAdornmentManager(ThreadingContext, textView, TagAggregatorFactoryService, AsyncListener, AdornmentLayerName); + } } } diff --git a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTag.cs b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTag.cs index 840c413f7d6d6..a3f85f0e6015f 100644 --- a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTag.cs +++ b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorTag.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Editor.Implementation.Adornments; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Formatting; namespace Microsoft.CodeAnalysis.Editor.Implementation.LineSeparators { @@ -34,7 +35,7 @@ public LineSeparatorTag(IEditorFormatMap editorFormatMap) /// /// Creates a very long line at the bottom of bounds. /// - public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) + public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds, TextFormattingRunProperties format) { Initialize(view); @@ -45,6 +46,7 @@ public override GraphicsResult GetGraphics(IWpfTextView view, Geometry bounds) Height = 1, Width = view.ViewportWidth }; + void viewportWidthChangedHandler(object s, EventArgs e) { border.Width = view.ViewportWidth; diff --git a/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueSaveFileCommandHandler.cs b/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueSaveFileCommandHandler.cs deleted file mode 100644 index a8c80e2830e9b..0000000000000 --- a/src/EditorFeatures/Core/Implementation/EditAndContinue/EditAndContinueSaveFileCommandHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel.Composition; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.EditAndContinue; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Commanding; -using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; -using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; -using VSCommanding = Microsoft.VisualStudio.Commanding; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.EditAndContinue -{ - [Export] - [Export(typeof(VSCommanding.ICommandHandler))] - [ContentType(ContentTypeNames.RoslynContentType)] - [Name(PredefinedCommandHandlerNames.EditAndContinueFileSave)] - internal sealed class EditAndContinueSaveFileCommandHandler : IChainedCommandHandler - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public EditAndContinueSaveFileCommandHandler() - { - } - - public string DisplayName => PredefinedCommandHandlerNames.EditAndContinueFileSave; - - void IChainedCommandHandler.ExecuteCommand(SaveCommandArgs args, Action nextCommandHandler, CommandExecutionContext executionContext) - { - var textContainer = args.SubjectBuffer.AsTextContainer(); - - if (Workspace.TryGetWorkspace(textContainer, out var workspace)) - { - var documentId = workspace.GetDocumentIdInCurrentContext(textContainer); - if (documentId != null) - { - // ignoring source-generated files since they shouldn't be modified and saved: - var currentDocument = workspace.CurrentSolution.GetDocument(documentId); - if (currentDocument != null) - { - var proxy = new RemoteEditAndContinueServiceProxy(workspace); - - // fire and forget - _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(currentDocument, CancellationToken.None)).ReportNonFatalErrorAsync(); - } - } - } - - nextCommandHandler(); - } - - public VSCommanding.CommandState GetCommandState(SaveCommandArgs args, Func nextCommandHandler) - => nextCommandHandler(); - } -} - diff --git a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs index f46977f745bb9..6cb4b1af0b2ae 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs @@ -34,7 +34,6 @@ public void ToDiagnostic() RudeEditKind.FieldKindUpdate, RudeEditKind.TypeKindUpdate, RudeEditKind.AccessorKindUpdate, - RudeEditKind.MethodKindUpdate, RudeEditKind.DeclareLibraryUpdate, RudeEditKind.DeclareAliasUpdate, RudeEditKind.InsertDllImport, @@ -49,18 +48,15 @@ public void ToDiagnostic() RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, RudeEditKind.SwitchBetweenLambdaAndLocalFunction, RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier, - RudeEditKind.AddRecordPositionalParameter, - RudeEditKind.DeleteRecordPositionalParameter, RudeEditKind.NotSupportedByRuntime, RudeEditKind.MakeMethodAsync, - RudeEditKind.MakeMethodIterator + RudeEditKind.MakeMethodIterator, + RudeEditKind.ChangeImplicitMainReturnType }; var arg2 = new HashSet() { - RudeEditKind.ConstraintKindUpdate, RudeEditKind.InsertIntoStruct, - RudeEditKind.ConstraintKindUpdate, RudeEditKind.InsertIntoStruct, RudeEditKind.ChangingCapturedVariableType, RudeEditKind.AccessingCapturedVariableInLambda, diff --git a/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs b/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs index 3d80871bbeb82..76ce9e5009611 100644 --- a/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs +++ b/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs @@ -1118,8 +1118,8 @@ void M(ref int i) { } var method_v1 = type1_v1.GetMembers("M").Single(); var method_v2 = type1_v2.GetMembers("M").Single(); - var trueComp = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: true, tupleNamesMustMatch: false); - var falseComp = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: false, tupleNamesMustMatch: false); + var trueComp = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false); + var falseComp = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: false); Assert.False(trueComp.Equals(method_v1, method_v2)); Assert.False(trueComp.Equals(method_v2, method_v1)); @@ -1131,6 +1131,128 @@ void M(ref int i) { } falseComp.GetHashCode(method_v2)); } + [Fact] + public async Task TestNullable() + { + var csharpCode1 = +@" +#nullable enable +class T +{ + string? A; + string[]? B; + dynamic? C; + dynamic?[]? D; +}"; + + var csharpCode2 = +@" +#nullable enable +class T +{ + string A; + string[] B; + dynamic C; + dynamic[] D; +}"; + + using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1); + using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2); + var t1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single(); + var t2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single(); + + var a1 = ((IFieldSymbol)t1.GetMembers("A").Single()).Type; + var b1 = ((IFieldSymbol)t1.GetMembers("B").Single()).Type; + var c1 = ((IFieldSymbol)t1.GetMembers("C").Single()).Type; + var d1 = ((IFieldSymbol)t1.GetMembers("D").Single()).Type; + var a2 = ((IFieldSymbol)t2.GetMembers("A").Single()).Type; + var b2 = ((IFieldSymbol)t2.GetMembers("B").Single()).Type; + var c2 = ((IFieldSymbol)t2.GetMembers("C").Single()).Type; + var d2 = ((IFieldSymbol)t2.GetMembers("D").Single()).Type; + + Assert.Equal(NullableAnnotation.Annotated, a1.NullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, a2.NullableAnnotation); + + var ignoreComparer = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true); + var notIgnoreComparer = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false); + + Assert.True(ignoreComparer.Equals(a1, a2)); + Assert.True(ignoreComparer.Equals(b1, b2)); + Assert.True(ignoreComparer.Equals(c1, c2)); + Assert.True(ignoreComparer.Equals(d1, d2)); + Assert.False(notIgnoreComparer.Equals(a1, a2)); + Assert.False(notIgnoreComparer.Equals(b1, b2)); + Assert.False(notIgnoreComparer.Equals(c1, c2)); + Assert.False(notIgnoreComparer.Equals(d1, d2)); + + // The hashcodes of distinct objects don't have to be distinct. + Assert.Equal(ignoreComparer.GetHashCode(a1), ignoreComparer.GetHashCode(a2)); + Assert.Equal(ignoreComparer.GetHashCode(b1), ignoreComparer.GetHashCode(b2)); + Assert.Equal(ignoreComparer.GetHashCode(c1), ignoreComparer.GetHashCode(c2)); + Assert.Equal(ignoreComparer.GetHashCode(d1), ignoreComparer.GetHashCode(d2)); + } + + [Fact] + public async Task TestNullableDisableVsEnable() + { + var csharpCode1 = +@" +#nullable disable +class T +{ + string A; + string[] B; + dynamic C; + dynamic[] D; +}"; + + var csharpCode2 = +@" +#nullable enable +class T +{ + string A; + string[] B; + dynamic C; + dynamic[] D; +}"; + + using var workspace1 = TestWorkspace.CreateCSharp(csharpCode1); + using var workspace2 = TestWorkspace.CreateCSharp(csharpCode2); + var t1 = (await workspace1.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single(); + var t2 = (await workspace2.CurrentSolution.Projects.Single().GetCompilationAsync()).GlobalNamespace.GetTypeMembers("T").Single(); + + var a1 = ((IFieldSymbol)t1.GetMembers("A").Single()).Type; + var b1 = ((IFieldSymbol)t1.GetMembers("B").Single()).Type; + var c1 = ((IFieldSymbol)t1.GetMembers("C").Single()).Type; + var d1 = ((IFieldSymbol)t1.GetMembers("D").Single()).Type; + var a2 = ((IFieldSymbol)t2.GetMembers("A").Single()).Type; + var b2 = ((IFieldSymbol)t2.GetMembers("B").Single()).Type; + var c2 = ((IFieldSymbol)t2.GetMembers("C").Single()).Type; + var d2 = ((IFieldSymbol)t2.GetMembers("D").Single()).Type; + + Assert.Equal(NullableAnnotation.None, a1.NullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, a2.NullableAnnotation); + + var ignoreComparer = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true); + var notIgnoreComparer = new SymbolEquivalenceComparer(assemblyComparerOpt: null, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: false); + + Assert.True(ignoreComparer.Equals(a1, a2)); + Assert.True(ignoreComparer.Equals(b1, b2)); + Assert.True(ignoreComparer.Equals(c1, c2)); + Assert.True(ignoreComparer.Equals(d1, d2)); + Assert.False(notIgnoreComparer.Equals(a1, a2)); + Assert.False(notIgnoreComparer.Equals(b1, b2)); + Assert.False(notIgnoreComparer.Equals(c1, c2)); + Assert.False(notIgnoreComparer.Equals(d1, d2)); + + // The hashcodes of distinct objects don't have to be distinct. + Assert.Equal(ignoreComparer.GetHashCode(a1), ignoreComparer.GetHashCode(a2)); + Assert.Equal(ignoreComparer.GetHashCode(b1), ignoreComparer.GetHashCode(b2)); + Assert.Equal(ignoreComparer.GetHashCode(c1), ignoreComparer.GetHashCode(c2)); + Assert.Equal(ignoreComparer.GetHashCode(d1), ignoreComparer.GetHashCode(d2)); + } + [Fact] public async Task TestCSharpReducedExtensionMethodsAreEquivalent() { @@ -1316,7 +1438,7 @@ public void AssemblyComparer1() var tb2 = (ITypeSymbol)b2.GlobalNamespace.GetMembers("T").Single(); var tb3 = (ITypeSymbol)b3.GlobalNamespace.GetMembers("T").Single(); - var identityComparer = new SymbolEquivalenceComparer(AssemblySymbolIdentityComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false); + var identityComparer = new SymbolEquivalenceComparer(AssemblySymbolIdentityComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: false); // same name: Assert.True(SymbolEquivalenceComparer.IgnoreAssembliesInstance.Equals(ta1, ta2)); @@ -1402,7 +1524,7 @@ .method public instance int32[] F( // 3 var type1 = (ITypeSymbol)c1.GlobalNamespace.GetMembers("C").Single(); var type2 = (ITypeSymbol)c2.GlobalNamespace.GetMembers("C").Single(); - var identityComparer = new SymbolEquivalenceComparer(AssemblySymbolIdentityComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false); + var identityComparer = new SymbolEquivalenceComparer(AssemblySymbolIdentityComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: false); var f1 = type1.GetMembers("F"); var f2 = type2.GetMembers("F"); diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb index 617336626710c..432d07e3fbf0a 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb @@ -54,6 +54,32 @@ class Program Await TestAPIAndFeature(input, kind, host) End Function + + Public Async Function TestThisConstructorInitializerSameFile_FileScopedNamespace(kind As TestKind, host As TestHost) As Task + Dim input = + + + +namespace FileScopedNS; + +class Program +{ + private int v; + public Program() : $$[|this|](4) + { + } + + public {|Definition:Program|}(int v) + { + this.v = v; + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function + Public Async Function TestThisConstructorInitializerDifferentFile1(kind As TestKind, host As TestHost) As Task Dim input = diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb index ee1cc0729587b..c329a73fe8b35 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_XmlDoc.vb @@ -765,6 +765,25 @@ class c End Using End Function + + Public Async Function CommitParam_Record(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + , showCompletionInArgumentLists:=showCompletionInArgumentLists) + + state.SendInvokeCompletionList() + Await state.AssertCompletionSession() + Await state.AssertSelectedCompletionItem(displayText:="param name=""I""") + state.SendReturn() + Await state.AssertNoCompletionSession() + + ' /// Public Async Function CommitParamNoOpenAngle(showCompletionInArgumentLists As Boolean) As Task diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index 88b41f12b85b4..c016d6cc6552d 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -146,6 +146,8 @@ internal void VerifySemantics(EditScript[] editScripts, TargetFramew for (var documentIndex = 0; documentIndex < documentCount; documentIndex++) { + var assertMessagePrefix = (documentCount > 0) ? $"Document #{documentIndex}" : null; + var expectedResult = expectedResults[documentIndex]; var includeFirstLineInDiagnostics = expectedResult.Diagnostics.Any(d => d.FirstLine != null) == true; @@ -169,13 +171,13 @@ internal void VerifySemantics(EditScript[] editScripts, TargetFramew var oldText = oldDocument.GetTextSynchronously(default); var newText = newDocument.GetTextSynchronously(default); - VerifyDiagnostics(expectedResult.Diagnostics, result.RudeEditErrors.ToDescription(newText, includeFirstLineInDiagnostics)); + VerifyDiagnostics(expectedResult.Diagnostics, result.RudeEditErrors.ToDescription(newText, includeFirstLineInDiagnostics), assertMessagePrefix); if (!expectedResult.SemanticEdits.IsDefault) { if (result.HasChanges) { - VerifySemanticEdits(expectedResult.SemanticEdits, result.SemanticEdits, oldModel.Compilation, newModel.Compilation, oldRoot, newRoot); + VerifySemanticEdits(expectedResult.SemanticEdits, result.SemanticEdits, oldModel.Compilation, newModel.Compilation, oldRoot, newRoot, assertMessagePrefix); allEdits.AddRange(result.SemanticEdits); } @@ -212,8 +214,8 @@ internal void VerifySemantics(EditScript[] editScripts, TargetFramew public static void VerifyDiagnostics(IEnumerable expected, IEnumerable actual, SourceText newSource) => VerifyDiagnostics(expected, actual.ToDescription(newSource, expected.Any(d => d.FirstLine != null))); - public static void VerifyDiagnostics(IEnumerable expected, IEnumerable actual) - => AssertEx.SetEqual(expected, actual, itemSeparator: ",\r\n"); + public static void VerifyDiagnostics(IEnumerable expected, IEnumerable actual, string? message = null) + => AssertEx.SetEqual(expected, actual, message: message, itemSeparator: ",\r\n"); private void VerifySemanticEdits( ImmutableArray expectedSemanticEdits, @@ -221,12 +223,14 @@ private void VerifySemanticEdits( Compilation oldCompilation, Compilation newCompilation, SyntaxNode oldRoot, - SyntaxNode newRoot) + SyntaxNode newRoot, + string? message = null) { // string comparison to simplify understanding why a test failed: AssertEx.Equal( expectedSemanticEdits.Select(e => $"{e.Kind}: {e.SymbolProvider(newCompilation)}"), - actualSemanticEdits.NullToEmpty().Select(e => $"{e.Kind}: {e.Symbol.Resolve(newCompilation).Symbol}")); + actualSemanticEdits.NullToEmpty().Select(e => $"{e.Kind}: {e.Symbol.Resolve(newCompilation).Symbol}"), + message: message); for (var i = 0; i < actualSemanticEdits.Length; i++) { diff --git a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 988a4ea86afb6..625f9948e5159 100644 --- a/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/EditorFeatures/TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -399,7 +399,7 @@ static LSP.Location ConvertTextSpanWithTextToLocation(TextSpan span, SourceText private static RequestDispatcher CreateRequestDispatcher(TestWorkspace workspace) { - var factory = workspace.ExportProvider.GetExportedValue(); + var factory = workspace.ExportProvider.GetExportedValue(); return factory.CreateRequestDispatcher(); } diff --git a/src/EditorFeatures/Text/ContentTypeNames.cs b/src/EditorFeatures/Text/ContentTypeNames.cs index bc19035769692..0baa8d493e491 100644 --- a/src/EditorFeatures/Text/ContentTypeNames.cs +++ b/src/EditorFeatures/Text/ContentTypeNames.cs @@ -14,5 +14,7 @@ internal static class ContentTypeNames public const string XamlContentType = "XAML"; public const string JavaScriptContentTypeName = "JavaScript"; public const string TypeScriptContentTypeName = "TypeScript"; + public const string FSharpContentType = "F#"; + public const string FSharpSignatureHelpContentType = "F# Signature Help"; } } diff --git a/src/EditorFeatures/VisualBasicTest/ChangeSignature/AddParameterTests.vb b/src/EditorFeatures/VisualBasicTest/ChangeSignature/AddParameterTests.vb index 358f561f10ea0..0053746e98b52 100644 --- a/src/EditorFeatures/VisualBasicTest/ChangeSignature/AddParameterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/ChangeSignature/AddParameterTests.vb @@ -825,5 +825,36 @@ End Class]]>.NormalizedValue() Await TestChangeSignatureViaCommandAsync(LanguageNames.VisualBasic, markup, updatedSignature:=permutation, expectedUpdatedInvocationDocumentCode:=updatedCode) End Function + + + + Public Async Function TestAddParameter_NoLastWhitespaceTrivia() As Task + + Dim markup = +'''
+''' +Sub $$M(a As Integer) +End Sub +End Class]]>.NormalizedValue() + Dim permutation = + { + New AddedParameterOrExistingIndex(0), + New AddedParameterOrExistingIndex(New AddedParameter(Nothing, "Integer", "b", CallSiteKind.Value), "Integer") + } + + Dim updatedCode = + ''' + ''' + ''' + Sub M(a As Integer, b As Integer) + End Sub +End Class]]>.NormalizedValue() + + Await TestChangeSignatureViaCommandAsync(LanguageNames.VisualBasic, markup, updatedSignature:=permutation, expectedUpdatedInvocationDocumentCode:=updatedCode) + End Function End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb index 1a465c7cc1b3a..dbe805088b668 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb @@ -175,7 +175,7 @@ Class C Me.value = a End Sub - Function F(a As C, b As C) As Integer + Function G(a As C, b As C) As Integer Me.value = a Return 0 End Function @@ -217,7 +217,7 @@ Class C Me.value = a*8 End Sub - Function F(a As C, b As C) As Integer + Function G(a As C, b As C) As Integer Me.value = a*8 End Function End Class @@ -264,7 +264,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo(Integer)"))) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo(a As Integer)"))) End Sub @@ -1503,10 +1503,10 @@ End Class Dim src2 = " Class C - Dim a = 0 + Dim a As Integer = 0 Sub Main - Dim b = 0 + Dim b As Integer = 0 End Sub End Class " @@ -1514,7 +1514,7 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ActiveStatementUpdate, "b = 0")) + Diagnostic(RudeEditKind.ActiveStatementUpdate, "b As Integer = 0")) End Sub @@ -1533,18 +1533,17 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) - edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + edits.VerifyRudeDiagnostics(active) End Sub Public Sub Initializer_InitToAsNew() Dim src1 = " Class C - Dim a = 0 + Dim a As Integer = 0 Sub Main - Dim b = 0 + Dim b As Integer = 0 End Sub End Class " @@ -1945,7 +1944,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ModifiersUpdate, "a As Integer = 1", FeaturesResources.const_field)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Private Const a As Integer = 1", FeaturesResources.const_field)) End Sub @@ -1990,7 +1989,8 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ModifiersUpdate, "a As Integer = 1", FeaturesResources.const_field)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Private Const a As Integer = 1, b As Integer = 2", FeaturesResources.const_field), + Diagnostic(RudeEditKind.ModifiersUpdate, "Private Const a As Integer = 1, b As Integer = 2", FeaturesResources.const_field)) End Sub @@ -2144,7 +2144,7 @@ End Class Dim src2 = " Class C - Dim a + Dim a As D Sub New End Sub @@ -2225,7 +2225,7 @@ End Class Dim src2 = " Class C - Dim a + Dim a As D Sub New End Sub diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb index fd8e2d9f86ae4..2e153ed184fe0 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb @@ -72,20 +72,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Optional activeStatements As ActiveStatementsDescription = Nothing, Optional semanticEdits As SemanticEditDescription() = Nothing, Optional diagnostics As RudeEditDiagnosticDescription() = Nothing, - Optional targetFrameworks As TargetFramework() = Nothing) + Optional targetFrameworks As TargetFramework() = Nothing, + Optional capabilities As EditAndContinueCapabilities? = Nothing) VerifySemantics( {editScript}, {New DocumentAnalysisResultsDescription(activeStatements, semanticEdits, diagnostics)}, - targetFrameworks) + targetFrameworks, + capabilities) End Sub Friend Sub VerifySemantics(editScripts As EditScript(Of SyntaxNode)(), expected As DocumentAnalysisResultsDescription(), - Optional targetFrameworks As TargetFramework() = Nothing) + Optional targetFrameworks As TargetFramework() = Nothing, + Optional capabilities As EditAndContinueCapabilities? = Nothing) For Each framework In If(targetFrameworks, {TargetFramework.NetStandard20, TargetFramework.NetCoreApp}) Dim validator = New VisualBasicEditAndContinueTestHelpers() - validator.VerifySemantics(editScripts, framework, expected) + validator.VerifySemantics(editScripts, framework, expected, capabilities) Next End Sub End Module diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb index 17c7b3e9dfd88..fb6bc5c551a2e 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb @@ -2421,7 +2421,7 @@ Class C End Class" Dim edits = GetTopEdits(src1, src2) - ' TODO allow creating a new leaf closure + ' TODO allow creating a new leaf closure: : https://github.com/dotnet/roslyn/issues/54672 edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.CapturingVariable, "F", "Me")) End Sub diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index 976d8b43337a6..d0750b0576f5e 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -736,7 +736,7 @@ Option Strict Off End Sub - Public Sub BaseTypeUpdate1() + Public Sub Type_BaseType_Update1() Dim src1 = "Class C : End Class" Dim src2 = "Class C : Inherits D : End Class" Dim edits = GetTopEdits(src1, src2) @@ -749,7 +749,7 @@ Option Strict Off End Sub - Public Sub BaseTypeUpdate2() + Public Sub Type_BaseType_Update2() Dim src1 = "Class C : Inherits D1 : End Class" Dim src2 = "Class C : Inherits D2 : End Class" Dim edits = GetTopEdits(src1, src2) @@ -762,7 +762,26 @@ Option Strict Off End Sub - Public Sub BaseInterfaceUpdate1() + Public Sub Type_BaseType_TupleElementName_Update() + Dim src1 = "Class C : Inherits System.Collections.Generic.List(Of (a As Integer, b As Integer)) : End Class" + Dim src2 = "Class C : Inherits System.Collections.Generic.List(Of (a As Integer, c As Integer)) : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C"))}) + End Sub + + + Public Sub Type_BaseType_Alias_Update() + Dim src1 = "Imports A = System.Int32 : Imports B = System.Int32 : Class C : Inherits List(Of A) : End Class" + Dim src2 = "Imports A = System.Int32 : Imports B = System.Int32 : Class C : Inherits List(Of B) : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics() + End Sub + + + Public Sub Type_BaseInterface_Update1() Dim src1 = "Class C : End Class" Dim src2 = "Class C : Implements IDisposable : End Class" Dim edits = GetTopEdits(src1, src2) @@ -775,7 +794,7 @@ Option Strict Off End Sub - Public Sub BaseInterfaceUpdate2() + Public Sub Type_BaseInterface_Update2() Dim src1 = "Class C : Implements IGoo, IBar : End Class" Dim src2 = "Class C : Implements IGoo : End Class" Dim edits = GetTopEdits(src1, src2) @@ -788,7 +807,7 @@ Option Strict Off End Sub - Public Sub BaseInterfaceUpdate3() + Public Sub Type_BaseInterface_Update3() Dim src1 = "Class C : Implements IGoo : Implements IBar : End Class" Dim src2 = "Class C : Implements IBar : Implements IGoo : End Class" Dim edits = GetTopEdits(src1, src2) @@ -800,6 +819,70 @@ Option Strict Off Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "Class C", FeaturesResources.class_)) End Sub + + Public Sub Type_Base_Partial() + Dim srcA1 = "Partial Class C : Inherits B : Implements I : End Class" + Dim srcB1 = "Partial Class C : Implements J : End Class" + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : Inherits B : Implements I, J : End Class" + + Dim srcC = " +Class B : End Class +Interface I : End Interface +Interface J : End Interface +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC, srcC)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults() + }) + End Sub + + + Public Sub Type_Base_Partial_InsertDelete() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = "" + Dim srcC1 = "Partial Class C : End Class" + + Dim srcA2 = "" + Dim srcB2 = "Partial Class C : Inherits D : End Class" + Dim srcC2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "Partial Class C", FeaturesResources.class_)}), + DocumentResults() + }) + End Sub + + + Public Sub Type_Base_InsertDelete() + Dim srcA1 = "" + Dim srcB1 = "Partial Class C : Inherits B : Implements I : End Class" + Dim srcA2 = "Partial Class C : Inherits B : Implements I : End Class" + Dim srcB2 = "" + + Dim srcC = " +Class B : End Class +Interface I : End Interface +Interface J : End Interface +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC, srcC)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults() + }) + End Sub + Public Sub ClassInsert_AbstractVirtualOverride() Dim src1 = "" @@ -1380,10 +1463,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As UShort]@11") + "Update [Enum Color]@0 -> [Enum Color As UShort]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As UShort", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "Enum Color", FeaturesResources.enum_)) End Sub @@ -1393,10 +1476,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As UShort]@11 -> [As Long]@11") + "Update [Enum Color As UShort]@0 -> [Enum Color As Long]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Enum Color", FeaturesResources.enum_)) + Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "Enum Color", FeaturesResources.enum_)) End Sub @@ -1406,10 +1489,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As UShort]@11") + "Update [Enum Color As UShort]@0 -> [Enum Color]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Enum Color", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "Enum Color", FeaturesResources.enum_)) End Sub @@ -1642,7 +1725,7 @@ End Class End Sub - Public Sub Delegates_Update_ReturnType1() + Public Sub Delegates_ReturnType_Update() Dim src1 = "Public Delegate Function D()" Dim src2 = "Public Delegate Sub D()" Dim edits = GetTopEdits(src1, src2) @@ -1655,44 +1738,42 @@ End Class End Sub - Public Sub Delegates_Update_ReturnType2() + Public Sub Delegates_ReturnType_Update2() Dim src1 = "Public Delegate Function D() As Integer" Dim src2 = "Public Delegate Sub D()" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Public Delegate Function D() As Integer]@0 -> [Public Delegate Sub D()]@0", - "Delete [As Integer]@29") + "Update [Public Delegate Function D() As Integer]@0 -> [Public Delegate Sub D()]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Sub D()", FeaturesResources.delegate_), - Diagnostic(RudeEditKind.Delete, "Public Delegate Sub D()", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Sub D()", FeaturesResources.delegate_)) End Sub - Public Sub Delegates_Update_ReturnType3() + Public Sub Delegates_ReturnType_Update3() Dim src1 = "Public Delegate Function D()" Dim src2 = "Public Delegate Function D() As Integer" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As Integer]@29") + "Update [Public Delegate Function D()]@0 -> [Public Delegate Function D() As Integer]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Function D()", FeaturesResources.delegate_)) End Sub - Public Sub Delegates_Update_ReturnType4() + Public Sub Delegates_ReturnType_Update4() Dim src1 = "Public Delegate Function D() As Integer" Dim src2 = "Public Delegate Function D()" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As Integer]@29") + "Update [Public Delegate Function D() As Integer]@0 -> [Public Delegate Function D()]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Delegate Function D()", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Function D()", FeaturesResources.delegate_)) End Sub @@ -1703,8 +1784,7 @@ End Class edits.VerifyEdits( "Insert [a As Integer]@27", - "Insert [a]@27", - "Insert [As Integer]@29") + "Insert [a]@27") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter)) @@ -1718,11 +1798,10 @@ End Class edits.VerifyEdits( "Delete [a As Integer]@27", - "Delete [a]@27", - "Delete [As Integer]@29") + "Delete [a]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Delegate Function D()", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.Delete, "Public Delegate Function D()", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) End Sub @@ -1735,7 +1814,7 @@ End Class "Update [a]@27 -> [b]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "b", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.Renamed, "Public Delegate Function D(b As Integer)", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) End Sub @@ -1745,23 +1824,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@29 -> [As Byte]@29") + "Update [a As Integer]@27 -> [a As Byte]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "a As Byte", FeaturesResources.parameter)) - End Sub - - - Public Sub Delegates_Parameter_UpdateModifier() - Dim src1 = "Public Delegate Function D(a As Integer()) As Integer" - Dim src2 = "Public Delegate Function D(ParamArray a As Integer()) As Integer" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits( - "Update [a As Integer()]@27 -> [ParamArray a As Integer()]@27") - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "ParamArray a As Integer()", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Function D(a As Byte)", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) End Sub @@ -1773,8 +1839,13 @@ End Class edits.VerifyEdits( "Update [a As Integer]@27 -> [ a As Integer]@27") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Delegate Function D( a As Integer)", FeaturesResources.delegate_)) + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("D.Invoke")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("D.BeginInvoke")) + }, + capabilities:=EditAndContinueTestHelpers.Net6RuntimeCapabilities) End Sub @@ -1788,7 +1859,7 @@ End Class "Insert [T]@30") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "(Of T)", VBFeaturesResources.type_parameters)) + Diagnostic(RudeEditKind.Insert, "T", FeaturesResources.type_parameter)) End Sub @@ -1802,7 +1873,7 @@ End Class "Delete [T]@30") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Delegate Function D()", VBFeaturesResources.type_parameters)) + Diagnostic(RudeEditKind.Delete, "Public Delegate Function D()", DeletedSymbolDisplay(FeaturesResources.type_parameter, "T"))) End Sub @@ -1982,14 +2053,11 @@ End Class "Insert [Public Sub New(a As Integer, b As Integer) : End Sub]@31", "Insert [Public Property P As Integer]@88", "Insert [Public Sub New(a As Integer, b As Integer)]@31", - "Insert [As Integer]@106", "Insert [(a As Integer, b As Integer)]@45", "Insert [a As Integer]@46", "Insert [b As Integer]@60", "Insert [a]@46", - "Insert [As Integer]@48", - "Insert [b]@60", - "Insert [As Integer]@62") + "Insert [b]@60") edits.VerifyRudeDiagnostics() End Sub @@ -2343,7 +2411,7 @@ End Structure DocumentResults( diagnostics:= { - Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter) + Diagnostic(RudeEditKind.ChangingParameterTypes, "Delegate Sub D(a As Integer)", FeaturesResources.delegate_) }), DocumentResults() }) @@ -2364,7 +2432,7 @@ End Structure { DocumentResults(), DocumentResults( - diagnostics:={Diagnostic(RudeEditKind.Delete, "Partial Structure S", DeletedSymbolDisplay(FeaturesResources.method, "F2(Byte)"))}), + diagnostics:={Diagnostic(RudeEditKind.Delete, "Partial Structure S", DeletedSymbolDisplay(FeaturesResources.method, "F2(x As Byte)"))}), DocumentResults( semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("S").GetMember(Of NamedTypeSymbol)("C").GetMember("F2"))}) }) @@ -2397,7 +2465,7 @@ End Structure Dim srcC1 = "Partial Class C : End Class" Dim srcA2 = "" - Dim srcB2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : End Class" Dim srcC2 = "Partial Class C : End Class" EditAndContinueValidation.VerifySemantics( @@ -2405,9 +2473,13 @@ End Structure { DocumentResults(), DocumentResults( - diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C", FeaturesResources.class_)}), + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C")) + }), DocumentResults() - }) + }, + capabilities:=EditAndContinueTestHelpers.Net6RuntimeCapabilities) End Sub @@ -2425,7 +2497,7 @@ End Structure { DocumentResults(), DocumentResults( - diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C(Of T As New)", FeaturesResources.class_)}), + diagnostics:={Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter)}), DocumentResults() }) End Sub @@ -2473,13 +2545,21 @@ End Structure Dim srcC2 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" Dim srcD2 = "Partial Class C" + vbCrLf + "Sub G() : End Sub : End Class" + Dim srcE = " +Class A : Inherits Attribute : End Class +Class B : Inherits Attribute : End Class +" + EditAndContinueValidation.VerifySemantics( - {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)}, + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2), GetTopEdits(srcE, srcE)}, { DocumentResults(), DocumentResults(), - DocumentResults(diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C", FeaturesResources.class_)}), - DocumentResults(diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C", FeaturesResources.class_)}) + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"))}), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.G"))}), + DocumentResults() }) End Sub @@ -3014,6 +3094,120 @@ End Class" }) End Sub + + Public Sub PartialMember_DeleteInsert_MethodAddParameter() + Dim srcA1 = " +Partial Structure S +End Structure +" + Dim srcB1 = " +Partial Structure S + Sub F() + End Sub +End Structure +" + + Dim srcA2 = " +Partial Structure S + Sub F(a As Integer) + End Sub +End Structure +" + Dim srcB2 = " +Partial Structure S +End Structure +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("S.F")) + }), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.Delete, "Partial Structure S", DeletedSymbolDisplay(FeaturesResources.method, "F()")) + }) + }) + End Sub + + + Public Sub PartialMember_DeleteInsert_UpdateMethodParameterType() + Dim srcA1 = " +Partial Structure S +End Structure +" + Dim srcB1 = " +Partial Structure S + Sub F(a As Integer) + End Sub +End Structure +" + + Dim srcA2 = " +Partial Structure S + Sub F(a As Byte) + End Sub +End Structure +" + Dim srcB2 = " +Partial Structure S +End Structure +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("S.F")) + }), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.Delete, "Partial Structure S", DeletedSymbolDisplay(FeaturesResources.method, "F(a As Integer)")) + }) + }) + End Sub + + + Public Sub PartialMember_DeleteInsert_MethodAddTypeParameter() + Dim srcA1 = " +Partial Structure S +End Structure +" + Dim srcB1 = " +Partial Structure S + Sub F() + End Sub +End Structure +" + + Dim srcA2 = " +Partial Structure S + Sub F(Of T)() + End Sub +End Structure +" + Dim srcB2 = " +Partial Structure S +End Structure +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.InsertGenericMethod, "Sub F(Of T)()", FeaturesResources.method) + }), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.Delete, "Partial Structure S", DeletedSymbolDisplay(FeaturesResources.method, "F()")) + }) + }) + End Sub + #End Region #Region "Methods" @@ -3153,7 +3347,7 @@ End Class" "Update [Sub Goo()]@8 -> [Function Goo()]@8") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.MethodKindUpdate, "Function Goo()", FeaturesResources.method)) + Diagnostic(RudeEditKind.TypeUpdate, "Function Goo()", FeaturesResources.method)) End Sub @@ -3167,7 +3361,7 @@ End Class" "Update [Sub Goo()]@12 -> [Function Goo()]@12") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.MethodKindUpdate, "Function Goo()", FeaturesResources.method)) + Diagnostic(RudeEditKind.TypeUpdate, "Function Goo()", FeaturesResources.method)) End Sub @@ -3220,11 +3414,10 @@ End Class" "Delete [Sub goo(a As Integer)]@8", "Delete [(a As Integer)]@15", "Delete [a As Integer]@16", - "Delete [a]@16", - "Delete [As Integer]@18") + "Delete [a]@16") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(Integer)"))) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(a As Integer)"))) End Sub @@ -3239,11 +3432,10 @@ End Class" "Delete [ Sub goo(a As Integer)]@11", "Delete [(a As Integer)]@29", "Delete [a As Integer]@30", - "Delete [a]@30", - "Delete [As Integer]@32") + "Delete [a]@30") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(Integer)"))) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(a As Integer)"))) End Sub @@ -3270,8 +3462,7 @@ End Class" "Insert [Private Function F(a As Integer)]@11", "Insert [(a As Integer)]@29", "Insert [a As Integer]@30", - "Insert [a]@30", - "Insert [As Integer]@32") + "Insert [a]@30") edits.VerifySemantics( ActiveStatementsDescription.Empty, @@ -3289,8 +3480,7 @@ End Class" "Insert [Private Function F(Optional a As Integer = 1)]@11", "Insert [(Optional a As Integer = 1)]@29", "Insert [Optional a As Integer = 1]@30", - "Insert [a]@39", - "Insert [As Integer]@41") + "Insert [a]@39") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F"))}) @@ -3411,21 +3601,17 @@ Imports System.Runtime.InteropServices "Insert [a As Integer]@27", "Insert [b As Integer]@41", "Insert [a]@27", - "Insert [As Integer]@29", "Insert [b]@41", - "Insert [As Integer]@43", "Delete [Sub f(a As Integer, b As Integer)" & vbLf & "a = b : End Sub]@33", "Delete [Sub f(a As Integer, b As Integer)]@33", "Delete [(a As Integer, b As Integer)]@38", "Delete [a As Integer]@39", "Delete [a]@39", - "Delete [As Integer]@41", "Delete [b As Integer]@53", - "Delete [b]@53", - "Delete [As Integer]@55") + "Delete [b]@53") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "f(Integer, Integer)"))) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "f(a As Integer, b As Integer)"))) End Sub @@ -4199,8 +4385,8 @@ End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.operator_, "+(C, C)")), - Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.operator_, "CType(C)"))) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.operator_, "+(d As C, g As C)")), + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.operator_, "CType(d As C)"))) End Sub @@ -4307,6 +4493,17 @@ End Class #End Region #Region "Constructors" + + Public Sub Constructor_SharedModifier_Remove() + ' Note that all tokens are aligned to avoid trivia edits. + Dim src1 = "Class C" & vbLf & "Shared Sub New() : End Sub : End Class" + Dim src2 = "Class C" & vbLf & "Public Sub New() : End Sub : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "Public Sub New()", FeaturesResources.constructor)) + End Sub + Public Sub ConstructorInitializer_Update1() Dim src1 = "Class C" & vbLf & "Public Sub New(a As Integer)" & vbLf & "MyBase.New(a) : End Sub : End Class" @@ -4353,8 +4550,7 @@ End Class edits.VerifyEdits( "Update [(a As Integer)]@22 -> [(a As Integer, b As Integer)]@22", "Insert [b As Integer]@37", - "Insert [b]@37", - "Insert [As Integer]@39") + "Insert [b]@37") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "b As Integer", FeaturesResources.parameter)) @@ -5311,7 +5507,7 @@ End Class #Region "Declare" - Public Sub Declare_Update1() + Public Sub Declare_Library_Update() Dim src1 As String = "Class C : Declare Ansi Function Goo Lib ""Bar"" () As Integer : End Class" Dim src2 As String = "Class C : Declare Ansi Function Goo Lib ""Baz"" () As Integer : End Class" @@ -5323,21 +5519,23 @@ End Class Diagnostic(RudeEditKind.DeclareLibraryUpdate, "Declare Ansi Function Goo Lib ""Baz"" ()", FeaturesResources.method)) End Sub - - Public Sub Declare_Update2() - Dim src1 As String = "Class C : Declare Ansi Function Goo Lib ""Bar"" () As Integer : End Class" - Dim src2 As String = "Class C : Declare Unicode Function Goo Lib ""Bar"" () As Integer : End Class" + + + + Public Sub Declare_Modifier_Update(oldModifiers As String, newModifiers As String) + Dim src1 As String = "Class C : Declare " & oldModifiers & " Function Goo Lib ""Bar"" () As Integer : End Class" + Dim src2 As String = "Class C : Declare " & newModifiers & " Function Goo Lib ""Bar"" () As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Declare Ansi Function Goo Lib ""Bar"" () As Integer]@10 -> [Declare Unicode Function Goo Lib ""Bar"" () As Integer]@10") + "Update [Declare " & oldModifiers & " Function Goo Lib ""Bar"" () As Integer]@10 -> [Declare " & newModifiers & " Function Goo Lib ""Bar"" () As Integer]@10") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Declare Unicode Function Goo Lib ""Bar"" ()", FeaturesResources.method)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Declare " & newModifiers & " Function Goo Lib ""Bar"" ()", FeaturesResources.method)) End Sub - Public Sub Declare_Update3() + Public Sub Declare_Alias_Add() Dim src1 As String = "Class C : Declare Ansi Function Goo Lib ""Bar"" () As Integer : End Class" Dim src2 As String = "Class C : Declare Ansi Function Goo Lib ""Bar"" Alias ""Al"" () As Integer : End Class" @@ -5350,7 +5548,7 @@ End Class End Sub - Public Sub Declare_Update4() + Public Sub Declare_Alias_Update() Dim src1 As String = "Class C : Declare Ansi Function Goo Lib ""Bar"" Alias ""A1"" () As Integer : End Class" Dim src2 As String = "Class C : Declare Ansi Function Goo Lib ""Bar"" Alias ""A2"" () As Integer : End Class" @@ -5370,8 +5568,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( "Delete [Declare Ansi Function Goo Lib ""Bar"" () As Integer]@10", - "Delete [()]@46", - "Delete [As Integer]@49") + "Delete [()]@46") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo()"))) @@ -5385,8 +5582,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( "Insert [Declare Ansi Function Goo Lib ""Bar"" () As Integer]@10", - "Insert [()]@46", - "Insert [As Integer]@49") + "Insert [()]@46") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.InsertDllImport, "Declare Ansi Function Goo Lib ""Bar"" ()", FeaturesResources.method)) @@ -5400,8 +5596,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( "Insert [Private Declare Ansi Function Goo Lib ""Bar"" () As Integer]@10", - "Insert [()]@54", - "Insert [As Integer]@57") + "Insert [()]@54") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.InsertDllImport, "Private Declare Ansi Function Goo Lib ""Bar"" ()", FeaturesResources.method)) @@ -5442,6 +5637,29 @@ End Class DocumentResults(semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("M.ExternSub"))}) }) End Sub + + + Public Sub Declare_ToDllImport_Update() + Dim src1 As String = " +Class C + Declare Unicode Function Goo Lib ""Bar"" () As Integer +End Class +" + + Dim src2 As String = " +Class C + + Shared Function Goo() As Integer + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + + ' TODO: this should work + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Shared Function Goo()", FeaturesResources.method), + Diagnostic(RudeEditKind.ModifiersUpdate, "Shared Function Goo()", FeaturesResources.method)) + End Sub #End Region #Region "Fields" @@ -5462,10 +5680,11 @@ End Class Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Update [" & oldModifiers & "Dim F As Integer = 0]@10 -> [" & newModifiers & "Dim F As Integer = 0]@10") + edits.VerifyEdits( + "Update [" & oldModifiers & "Dim F As Integer = 0]@10 -> [" & newModifiers & "Dim F As Integer = 0]@10") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "F As Integer = 0", FeaturesResources.field)) + Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "Dim F As Integer = 0", FeaturesResources.field)) End Sub @@ -5505,7 +5724,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@16 -> [As Boolean]@16") + "Update [a As Integer]@14 -> [a As Boolean]@14") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.TypeUpdate, "a As Boolean", FeaturesResources.field)) @@ -5536,8 +5755,7 @@ End Class "Update [Dim b As Object, c As Object]@10 -> [Dim b, c As Object]@10", "Update [b As Object]@14 -> [b, c As Object]@14", "Move [c]@27 -> @17", - "Delete [c As Object]@27", - "Delete [As Object]@29") + "Delete [c As Object]@27") edits.VerifySemantics() End Sub @@ -5552,8 +5770,7 @@ End Class "Update [Dim b, c As Object]@10 -> [Dim b As Object, c As Object]@10", "Update [b, c As Object]@14 -> [b As Object]@14", "Insert [c As Object]@27", - "Move [c]@17 -> @27", - "Insert [As Object]@29") + "Move [c]@17 -> @27") edits.VerifySemantics() End Sub @@ -5629,64 +5846,77 @@ End Class edits.VerifyEdits( "Update [Dim b As Object, c As Object]@10 -> [Dim b As Object]@10", "Delete [c As Object]@27", - "Delete [c]@27", - "Delete [As Object]@29") + "Delete [c]@27") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Dim b As Object", DeletedSymbolDisplay(FeaturesResources.field, "c"))) End Sub - Public Sub Field_TypeUpdate2() - Dim src1 = "Class C : Dim a, b As Integer, c?, d() As New D() : End Class" - Dim src2 = "Class C : Dim a?, b() As Integer, c, d As New D() : End Class" + Public Sub Field_TypeUpdate2a() + Dim src1 = "Class C : Dim a, b As Integer : End Class" + Dim src2 = "Class C : Dim a?, b() As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( "Update [a]@14 -> [a?]@14", - "Update [b]@18 -> [b()]@18", - "Update [c?]@34 -> [c]@34", - "Update [d()]@38 -> [d]@38") + "Update [b]@18 -> [b()]@18") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.TypeUpdate, "a?", FeaturesResources.field), - Diagnostic(RudeEditKind.TypeUpdate, "b()", FeaturesResources.field), - Diagnostic(RudeEditKind.TypeUpdate, "c", FeaturesResources.field), - Diagnostic(RudeEditKind.TypeUpdate, "d", FeaturesResources.field)) + Diagnostic(RudeEditKind.TypeUpdate, "b()", FeaturesResources.field)) End Sub - Public Sub Field_TypeUpdate3() - Dim src1 = "Class C : Dim a(3) As Integer, c(2,2) : End Class" - Dim src2 = "Class C : Dim a(2) As Integer, c(2) : End Class" + Public Sub Field_TypeUpdate_ArraySizeChange() + Dim src1 = "Class C : Dim a(3) As Integer : End Class" + Dim src2 = "Class C : Dim a(2) As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [a(3)]@14 -> [a(2)]@14", - "Update [c(2,2)]@31 -> [c(2)]@31") + "Update [a(3)]@14 -> [a(2)]@14") + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(), preserveLocalVariables:=True)}) + End Sub + + + Public Sub Field_TypeUpdate_ArrayRankChange() + Dim src1 = "Class C : Dim c(2,2) : End Class" + Dim src2 = "Class C : Dim c(2) : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [c(2,2)]@14 -> [c(2)]@14") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.TypeUpdate, "c(2)", FeaturesResources.field)) End Sub - Public Sub Field_TypeUpdate4a() + Public Sub Field_TypeUpdate_NullableUnchanged() + Dim src1 = "Class C : Dim a? As Integer : End Class" + Dim src2 = "Class C : Dim a As Integer? : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub Field_TypeUpdate_ArrayBoundsUnchanged() Dim src1 = "Class C : Dim a As Integer() : End Class" Dim src2 = "Class C : Dim a() As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [a]@14 -> [a()]@14", - "Update [As Integer()]@16 -> [As Integer]@18") + "Update [a As Integer()]@14 -> [a() As Integer]@14", + "Update [a]@14 -> [a()]@14") - ' TODO: the type didn't really change, we can allow this - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "a()", FeaturesResources.field), - Diagnostic(RudeEditKind.TypeUpdate, "a() As Integer", FeaturesResources.field)) + edits.VerifyRudeDiagnostics() End Sub - Public Sub Field_TypeUpdate4b() + Public Sub Field_TypeUpdate_ScalarToVector() Dim src1 = "Class C : Dim a As Integer : End Class" Dim src2 = "Class C : Dim a(1) As Integer : End Class" Dim edits = GetTopEdits(src1, src2) @@ -5696,29 +5926,31 @@ End Class End Sub - Public Sub Field_TypeUpdate5() + Public Sub Field_TypeUpdate_AsClause_Add() Dim src1 = "Class C : Dim a, b : End Class" Dim src2 = "Class C : Dim a, b As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As Integer]@19") + "Update [a, b]@14 -> [a, b As Integer]@14") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.TypeUpdate, "a, b As Integer", FeaturesResources.field), + Diagnostic(RudeEditKind.TypeUpdate, "a, b As Integer", FeaturesResources.field)) End Sub - Public Sub Field_TypeUpdate6() + Public Sub Field_TypeUpdate_AsClause_Remove() Dim src1 = "Class C : Dim a, b As Integer : End Class" Dim src2 = "Class C : Dim a, b : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As Integer]@19") + "Update [a, b As Integer]@14 -> [a, b]@14") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "a, b", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.TypeUpdate, "a, b", FeaturesResources.field), + Diagnostic(RudeEditKind.TypeUpdate, "a, b", FeaturesResources.field)) End Sub @@ -5739,11 +5971,9 @@ End Class edits.VerifyEdits( "Insert [Event a As Action]@10", - "Insert [As Action]@18", "Delete [Dim a As Action]@10", "Delete [a As Action]@14", - "Delete [a]@14", - "Delete [As Action]@16") + "Delete [a]@14") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))) @@ -5759,9 +5989,7 @@ End Class "Insert [Dim a As Action]@10", "Insert [a As Action]@14", "Insert [a]@14", - "Insert [As Action]@16", - "Delete [Event a As Action]@10", - "Delete [As Action]@18") + "Delete [Event a As Action]@10") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.event_, "a"))) @@ -5777,7 +6005,7 @@ End Class "Update [Dim a As WE]@10 -> [WithEvents a As WE]@10") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "a As WE", VBFeaturesResources.WithEvents_field)) + Diagnostic(RudeEditKind.ModifiersUpdate, "WithEvents a As WE", VBFeaturesResources.WithEvents_field)) End Sub @@ -5790,7 +6018,7 @@ End Class "Update [WithEvents a As WE]@10 -> [Dim a As WE]@10") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "a As WE", FeaturesResources.field)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Dim a As WE", FeaturesResources.field)) End Sub @@ -6348,10 +6576,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@21 -> [As Char]@21") + "Update [Property P As Integer]@10 -> [Property P As Char]@10") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Property P", FeaturesResources.auto_property)) + Diagnostic(RudeEditKind.TypeUpdate, "Property P", FeaturesResources.property_)) End Sub @@ -6375,6 +6603,74 @@ End Class semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("P"))}) End Sub + + Public Sub Property_Accessor_Get_AccessibilityModifier_Remove() + ' Note that all tokens are aligned to avoid trivia edits. + Dim src1 = " +Class C + Public Property F As Integer + Friend _ + Get + Return Nothing + End Get + Set + End Set + End Property +End Class" + Dim src2 = " +Class C + Public Property F As Integer + Private _ + Get + Return Nothing + End Get + Set + End Set + End Property +End Class" + Dim edits = GetTopEdits(src1, src2) + + Dim decl = "Private _ + Get" + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingAccessibility, decl, FeaturesResources.property_accessor)) + End Sub + + + Public Sub Property_Accessor_Set_AccessibilityModifier_Remove() + ' Note that all tokens are aligned to avoid trivia edits. + Dim src1 = " +Class C + Public Property F As Integer + Get + Return Nothing + End Get + Friend _ + Set + End Set + End Property +End Class" + Dim src2 = " +Class C + Public Property F As Integer + Get + Return Nothing + End Get + Private _ + Set + End Set + End Property +End Class" + Dim edits = GetTopEdits(src1, src2) + + Dim decl = "Private _ + Set" + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingAccessibility, decl, FeaturesResources.property_accessor)) + End Sub + Public Sub PrivatePropertyAccessorAddSetter() Dim src1 = "Class C : Private _p As Integer : Private ReadOnly Property P As Integer" & vbLf & "Get" & vbLf & "Return 1 : End Get : End Property : End Class" @@ -6387,8 +6683,7 @@ End Class "Insert [Set(value As Integer)]@87", "Insert [(value As Integer)]@90", "Insert [value As Integer]@91", - "Insert [value]@91", - "Insert [As Integer]@97") + "Insert [value]@91") edits.VerifyRudeDiagnostics() End Sub @@ -6419,11 +6714,10 @@ End Class "Delete [Set(value As Integer)]@87", "Delete [(value As Integer)]@90", "Delete [value As Integer]@91", - "Delete [value]@91", - "Delete [As Integer]@97") + "Delete [value]@91") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Private ReadOnly Property P", DeletedSymbolDisplay(VBFeaturesResources.property_accessor, "P(Integer)"))) + Diagnostic(RudeEditKind.Delete, "Private ReadOnly Property P", DeletedSymbolDisplay(FeaturesResources.property_accessor, "P(value As Integer)"))) End Sub @@ -6449,7 +6743,7 @@ End Class "Update [ReadOnly Property P As Integer]@10 -> [ReadOnly Property Q As Integer]@10") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "ReadOnly Property Q", FeaturesResources.auto_property)) + Diagnostic(RudeEditKind.Renamed, "ReadOnly Property Q", FeaturesResources.property_)) End Sub @@ -6854,8 +7148,8 @@ End Class edits.VerifyEdits( "Update [Property a As Integer]@10 -> [Property a As Integer = 0]@10") - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -6865,12 +7159,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Property a As New Integer()]@10 -> [Property a As Integer = 0]@10", - "Insert [As Integer]@21") + "Update [Property a As New Integer()]@10 -> [Property a As Integer = 0]@10") - ' TODO: we could detect that the type haven't changed and allow this - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -6885,8 +7177,8 @@ End Class "Delete [Shared Sub New()]@33", "Delete [()]@47") - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -7478,9 +7770,10 @@ End Class "Update [a As Integer = 1]@18 -> [a, b As Integer]@18", "Insert [b]@21") - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.b")), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.b")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -7491,12 +7784,12 @@ End Class edits.VerifyEdits( "Update [a As C = New C]@18 -> [a, b As New C]@18", - "Insert [b]@21", - "Delete [As C]@20") + "Insert [b]@21") - ' TODO: allow this edit - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "a, b As New C", VBFeaturesResources.as_clause)) + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.b")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -7509,8 +7802,7 @@ End Class "Insert [Private b As Integer]@33", "Update [a, b As Integer = 1]@18 -> [a As Integer]@18", "Insert [b As Integer]@41", - "Move [b]@21 -> @41", - "Insert [As Integer]@43") + "Move [b]@21 -> @41") edits.VerifySemantics(semanticEdits:= { @@ -7624,8 +7916,7 @@ End Class edits.VerifyEdits( "Delete [Private Dim a As Integer = 1]@10", "Delete [a As Integer = 1]@22", - "Delete [a]@22", - "Delete [As Integer]@24") + "Delete [a]@22") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))) @@ -7638,8 +7929,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [Private Property a As Integer = 1]@10", - "Delete [As Integer]@29") + "Delete [Private Property a As Integer = 1]@10") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.auto_property, "a"))) @@ -7739,21 +8029,21 @@ End Class Public Sub ConstField_Delete() Dim src1 = "Class C : Const x = 0 : End Class" - Dim src2 = "Class C : Dim x = 0 : End Class" + Dim src2 = "Class C : Dim x As Integer = 0 : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "x = 0", FeaturesResources.field)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Dim x As Integer = 0", FeaturesResources.field)) End Sub Public Sub ConstField_Add() - Dim src1 = "Class C : Dim x = 0 : End Class" + Dim src1 = "Class C : Dim x As Integer = 0 : End Class" Dim src2 = "Class C : Const x = 0 : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "x = 0", FeaturesResources.const_field)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Const x = 0", FeaturesResources.const_field)) End Sub @@ -8538,6 +8828,61 @@ End Class" Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "Event E", FeaturesResources.event_)) End Sub + + Public Sub Event_Accessor_Attribute_Update() + Dim srcAttribute = " +Class A + Inherits Attribute + + Sub New(a As Integer) + End Sub +End Class +" + + Dim src1 = " +Class C + Custom Event E As Action + + AddHandler(value As Action) + End AddHandler + + RemoveHandler(value As Action) + End RemoveHandler + + RaiseEvent() + End RaiseEvent + End Event +End Class +" + srcAttribute + + Dim src2 = " +Class C + Custom Event E As Action + + AddHandler(value As Action) + End AddHandler + + RemoveHandler(value As Action) + End RemoveHandler + + RaiseEvent() + End RaiseEvent + End Event +End Class +" + srcAttribute + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E").AddMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E").RemoveMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E").RaiseMethod) + }, + capabilities:=EditAndContinueTestHelpers.Net6RuntimeCapabilities) + End Sub + Public Sub EventAccessorReorder1() Dim src1 = "Class C : " & @@ -8593,33 +8938,47 @@ End Class" Public Sub Event_UpdateType() - Dim src1 = "Class C : " & - "Custom Event E As Action" & vbLf & - "AddHandler(value As Action) : End AddHandler" & vbLf & - "RemoveHandler(value As Action) : End RemoveHandler" & vbLf & - "RaiseEvent() : End RaiseEvent : " & - "End Event : " & - "End Class" + Dim src1 = " +Imports System - Dim src2 = "Class C : " & - "Custom Event E As Action(Of T)" & vbLf & - "AddHandler(value As Action(Of T)) : End AddHandler" & vbLf & - "RemoveHandler(value As Action(Of T)) : End RemoveHandler" & vbLf & - "RaiseEvent() : End RaiseEvent : " & - "End Event : " & - "End Class" +Class C + Custom Event E As Action + AddHandler(value As Action) + End AddHandler + RemoveHandler(value As Action) + End RemoveHandler + RaiseEvent() + End RaiseEvent + End Event +End Class +" + + Dim src2 = " +Imports System + +Class C + Custom Event E As Action(Of String) + AddHandler(value As Action(Of String)) + End AddHandler + RemoveHandler(value As Action(Of String)) + End RemoveHandler + RaiseEvent() + End RaiseEvent + End Event +End Class +" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Action]@25 -> [As Action(Of T)]@25", - "Update [As Action]@52 -> [As Action(Of T)]@58", - "Update [As Action]@100 -> [As Action(Of T)]@112") + "Update [Custom Event E As Action]@33 -> [Custom Event E As Action(Of String)]@33", + "Update [value As Action]@78 -> [value As Action(Of String)]@89", + "Update [value As Action]@142 -> [value As Action(Of String)]@164") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.TypeUpdate, "Event E", FeaturesResources.event_), - Diagnostic(RudeEditKind.TypeUpdate, "value As Action(Of T)", FeaturesResources.parameter), - Diagnostic(RudeEditKind.TypeUpdate, "value As Action(Of T)", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.TypeUpdate, "value As Action(Of String)", FeaturesResources.parameter), + Diagnostic(RudeEditKind.TypeUpdate, "value As Action(Of String)", FeaturesResources.parameter)) End Sub @@ -8800,19 +9159,6 @@ End Class Diagnostic(RudeEditKind.Renamed, "b", FeaturesResources.parameter)) End Sub - - Public Sub ParameterUpdate_Modifier() - Dim src1 = "Class C : " & vbLf & "Public ReadOnly Property P(ByRef a As Integer) As Integer" & vbLf & "Get" & vbLf & "Return 0 : End Get : End Property : End Class" - Dim src2 = "Class C : " & vbLf & "Public ReadOnly Property P(a As Integer) As Integer" & vbLf & "Get" & vbLf & "Return 0 : End Get : End Property : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits( - "Update [ByRef a As Integer]@38 -> [a As Integer]@38") - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "a As Integer", FeaturesResources.parameter)) - End Sub - Public Sub ParameterUpdate_AsClause1() Dim src1 = "Class C : " & vbLf & "Public ReadOnly Property P(a As Integer) As Integer" & vbLf & "Get" & vbLf & "Return 0 : End Get : End Property : End Class" @@ -8820,7 +9166,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@40 -> [As Object]@40") + "Update [a As Integer]@38 -> [a As Object]@38") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.TypeUpdate, "a As Object", FeaturesResources.parameter)) @@ -8833,10 +9179,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As Integer]@40") + "Update [a As Integer]@38 -> [a]@38") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "a", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.TypeUpdate, "a", FeaturesResources.parameter)) End Sub @@ -8849,7 +9195,7 @@ End Class "Update [a As Integer]@38 -> [Optional a As Integer = 0]@38") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Optional a As Integer = 0", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.InitializerUpdate, "Optional a As Integer = 0", FeaturesResources.parameter)) End Sub @@ -8873,8 +9219,7 @@ End Class edits.VerifyEdits( "Insert [a As Integer]@24", - "Insert [a]@24", - "Insert [As Integer]@26") + "Insert [a]@24") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter)) @@ -8889,8 +9234,7 @@ End Class edits.VerifyEdits( "Update [(a As Integer)]@23 -> [(a As Integer, ByRef b As Integer)]@23", "Insert [ByRef b As Integer]@38", - "Insert [b]@44", - "Insert [As Integer]@46") + "Insert [b]@44") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "ByRef b As Integer", FeaturesResources.parameter)) @@ -8917,11 +9261,10 @@ End Class edits.VerifyEdits( "Insert [(a As Integer)]@23", "Insert [a As Integer]@24", - "Insert [a]@24", - "Insert [As Integer]@26") + "Insert [a]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "(a As Integer)", VBFeaturesResources.parameters)) + Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter)) End Sub @@ -8932,11 +9275,10 @@ End Class edits.VerifyEdits( "Delete [a As Integer]@24", - "Delete [a]@24", - "Delete [As Integer]@26") + "Delete [a]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M()", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.Delete, "Public Sub M()", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) End Sub @@ -8948,11 +9290,10 @@ End Class edits.VerifyEdits( "Update [(a As Integer, b As Integer)]@23 -> [(b As Integer)]@23", "Delete [a As Integer]@24", - "Delete [a]@24", - "Delete [As Integer]@26") + "Delete [a]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M(b As Integer)", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.Delete, "Public Sub M(b As Integer)", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) End Sub @@ -8976,11 +9317,10 @@ End Class edits.VerifyEdits( "Delete [(a As Integer)]@23", "Delete [a As Integer]@24", - "Delete [a]@24", - "Delete [As Integer]@26") + "Delete [a]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M", VBFeaturesResources.parameters)) + Diagnostic(RudeEditKind.Delete, "Public Sub M", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) End Sub @@ -9011,6 +9351,66 @@ End Class Diagnostic(RudeEditKind.Renamed, "c", FeaturesResources.parameter)) End Sub + + + + Public Sub Parameter_Modifier_Remove(modifier As String) + Dim src1 = "Module M" & vbLf & "Sub F(" & modifier & " a As Integer()) : End Sub : End Module" + Dim src2 = "Module M" & vbLf & "Sub F(a As Integer()) : End Sub : End Module" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "a As Integer()", FeaturesResources.parameter)) + End Sub + + + + + + + + + Public Sub Parameter_Initializer_Update(oldParameter As String, newParameter As String) + Dim src1 = "Module M" & vbLf & "Sub F(" & oldParameter & ") : End Sub : End Module" + Dim src2 = "Module M" & vbLf & "Sub F(" & newParameter & ") : End Sub : End Module" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, newParameter, FeaturesResources.parameter)) + End Sub + + + Public Sub Parameter_Initializer_NaN() + Dim src1 = "Module M" & vbLf & "Sub F(Optional a As Double = System.Double.NaN) : End Sub : End Module" + Dim src2 = "Module M" & vbLf & "Sub F(Optional a As Double = Double.NaN) : End Sub : End Module" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub Parameter_Initializer_InsertDeleteUpdate() + Dim srcA1 = "Partial Class C" & vbLf & "End Class" + Dim srcB1 = "Partial Class C" & vbLf & "Public Shared Sub F(Optional x As Integer = 1) : End Sub : End Class" + + Dim srcA2 = "Partial Class C" & vbLf & "Public Shared Sub F(Optional x As Integer = 2) : End Sub : End Class" + Dim srcB2 = "Partial Class C" & vbLf & "End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + diagnostics:= + { + Diagnostic(RudeEditKind.InitializerUpdate, "Optional x As Integer = 2", FeaturesResources.parameter) + }), + DocumentResults() + }) + End Sub + Public Sub ParameterAttributeInsert1() Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" @@ -9076,7 +9476,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@161 -> [As Integer]@161") + "Update [Public Function M() As Integer]@141 -> [Public Function M() As Integer]@141") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method)) @@ -9101,7 +9501,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@31 -> [As Integer]@31") + "Update [Public Function M() As Integer]@11 -> [Public Function M() As Integer]@11") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method)) @@ -9114,7 +9514,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@31 -> [As Integer]@31") + "Update [Public Function M() As Integer]@11 -> [Public Function M() As Integer]@11") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method)) @@ -9127,10 +9527,11 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As Integer]@31") + "Update [Public Function M() As Integer]@11 -> [Public Function M()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Function M()", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method), + Diagnostic(RudeEditKind.TypeUpdate, "Public Function M()", FeaturesResources.method)) End Sub @@ -9140,10 +9541,11 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As Integer]@31") + "Update [Public Function M()]@11 -> [Public Function M() As Integer]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method), + Diagnostic(RudeEditKind.TypeUpdate, "Public Function M()", FeaturesResources.method)) End Sub @@ -9153,7 +9555,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As Integer]@31 -> [As Object]@31") + "Update [Public Function M() As Integer]@11 -> [Public Function M() As Object]@11") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.TypeUpdate, "Public Function M()", FeaturesResources.method)) @@ -9174,7 +9576,8 @@ End Class "Insert [A]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "(Of A)", VBFeaturesResources.type_parameters)) + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, " ", FeaturesResources.method), + Diagnostic(RudeEditKind.Insert, "A", FeaturesResources.type_parameter)) End Sub @@ -9188,6 +9591,7 @@ End Class "Insert [B]@30") edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, " ", FeaturesResources.method), Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.type_parameter)) End Sub @@ -9202,7 +9606,7 @@ End Class "Delete [A]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M()", VBFeaturesResources.type_parameters)) + Diagnostic(RudeEditKind.Delete, "Public Sub M()", DeletedSymbolDisplay(FeaturesResources.type_parameter, "A"))) End Sub @@ -9216,7 +9620,8 @@ End Class "Delete [A]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M(Of B)()", FeaturesResources.type_parameter)) + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "", FeaturesResources.method), + Diagnostic(RudeEditKind.Delete, "Public Sub M(Of B)()", DeletedSymbolDisplay(FeaturesResources.type_parameter, "A"))) End Sub @@ -9274,7 +9679,7 @@ End Class "Insert [A]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "(Of A)", VBFeaturesResources.type_parameters)) + Diagnostic(RudeEditKind.Insert, "A", FeaturesResources.type_parameter)) End Sub @@ -9302,7 +9707,7 @@ End Class "Delete [A]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", VBFeaturesResources.type_parameters)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.type_parameter, "A"))) End Sub @@ -9316,7 +9721,7 @@ End Class "Delete [A]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C(Of B)", FeaturesResources.type_parameter)) + Diagnostic(RudeEditKind.Delete, "Class C(Of B)", DeletedSymbolDisplay(FeaturesResources.type_parameter, "A"))) End Sub @@ -9370,11 +9775,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As Class]@13", - "Insert [Class]@16") + "Update [T]@11 -> [T As Class]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As Class", FeaturesResources.type_constraint)) + Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter)) End Sub @@ -9384,11 +9788,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As New]@13", - "Insert [New]@16") + "Update [S]@11 -> [S As New]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As New", FeaturesResources.type_constraint)) + Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter)) End Sub @@ -9398,11 +9801,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As Class]@16", - "Delete [Class]@19") + "Update [T As Class]@14 -> [T]@14") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "T", FeaturesResources.type_constraint)) + Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter)) End Sub @@ -9413,11 +9815,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As New]@13", - "Delete [New]@16") + "Update [S As New]@11 -> [S]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "S", FeaturesResources.type_constraint)) + Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter)) End Sub @@ -9427,12 +9828,12 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Structure]@16 -> [Class]@16", - "Update [Class]@32 -> [Structure]@28") + "Update [S As Structure]@11 -> [S As Class]@11", + "Update [T As Class]@27 -> [T As Structure]@23") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ConstraintKindUpdate, "Class", "Structure", "Class"), - Diagnostic(RudeEditKind.ConstraintKindUpdate, "Structure", "Class", "Structure")) + Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter), + Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter)) End Sub @@ -9442,14 +9843,12 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As New]@13 -> [As {New, J}]@13", - "Update [As Class]@23 -> [As {Class, I}]@28", - "Insert [J]@22", - "Insert [I]@39") + "Update [S As New]@11 -> [S As {New, J}]@11", + "Update [T As Class]@21 -> [T As {Class, I}]@26") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "J", FeaturesResources.type_constraint), - Diagnostic(RudeEditKind.Insert, "I", FeaturesResources.type_constraint)) + Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter), + Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter)) End Sub @@ -9459,9 +9858,9 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [As New]@13 -> [As {New}]@13", - "Update [As Class]@23 -> [As {Class}]@25", - "Update [As I]@35 -> [As {I}]@39") + "Update [S As New]@11 -> [S As {New}]@11", + "Update [T As Class]@21 -> [T As {Class}]@23", + "Update [U As I]@33 -> [U As {I}]@37") edits.VerifyRudeDiagnostics() End Sub @@ -9473,9 +9872,10 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Reorder [J]@20 -> @17") + "Update [S As {I, J}]@11 -> [S As {J, I}]@11") - edits.VerifyRudeDiagnostics() + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter)) End Sub #End Region diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index 81b7738b5a72d..1a46c3ec8d8d2 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -123,14 +123,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests End Namespace -Structure S(Of T As {New, Class, I}) +Structure S(Of T As {New, Class, I}) Inherits B End Structure -Structure S(Of T As {New, Class, I}) +Structure S(Of T As {New, Class, I}) End Structure -Structure S(Of T As {New, Class, I}) +Structure S(Of T As {New, Class, I}) End Structure diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs index 9eabf0b15734e..69fa80e320486 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs @@ -351,6 +351,7 @@ internal override bool IsInterface internal override bool IsRecord => false; internal override bool IsRecordStruct => false; internal override bool HasPossibleWellKnownCloneMethod() => false; + internal override bool IsInterpolatedStringHandlerType => false; [Conditional("DEBUG")] internal static void VerifyTypeParameters(Symbol container, ImmutableArray typeParameters) diff --git a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs index 5ebd9cb87ab0b..e350faa680817 100644 --- a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs +++ b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -42,7 +43,9 @@ internal sealed class CSharpChangeSignatureService : AbstractChangeSignatureServ SyntaxKind.DelegateDeclaration, SyntaxKind.SimpleLambdaExpression, SyntaxKind.ParenthesizedLambdaExpression, - SyntaxKind.LocalFunctionStatement); + SyntaxKind.LocalFunctionStatement, + SyntaxKind.RecordStructDeclaration, + SyntaxKind.RecordDeclaration); private static readonly ImmutableArray _declarationAndInvocableKinds = _declarationKinds.Concat(ImmutableArray.Create( @@ -85,7 +88,9 @@ internal sealed class CSharpChangeSignatureService : AbstractChangeSignatureServ SyntaxKind.NameMemberCref, SyntaxKind.AnonymousMethodExpression, SyntaxKind.ParenthesizedLambdaExpression, - SyntaxKind.SimpleLambdaExpression); + SyntaxKind.SimpleLambdaExpression, + SyntaxKind.RecordStructDeclaration, + SyntaxKind.RecordDeclaration); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -270,7 +275,9 @@ public override async Task ChangeSignatureAsync( if (updatedNode.IsKind(SyntaxKind.MethodDeclaration) || updatedNode.IsKind(SyntaxKind.ConstructorDeclaration) || updatedNode.IsKind(SyntaxKind.IndexerDeclaration) || - updatedNode.IsKind(SyntaxKind.DelegateDeclaration)) + updatedNode.IsKind(SyntaxKind.DelegateDeclaration) || + updatedNode.IsKind(SyntaxKind.RecordStructDeclaration) || + updatedNode.IsKind(SyntaxKind.RecordDeclaration)) { var updatedLeadingTrivia = UpdateParamTagsInLeadingTrivia(document, updatedNode, declarationSymbol, signaturePermutation); if (updatedLeadingTrivia != default && !updatedLeadingTrivia.IsEmpty) @@ -286,6 +293,12 @@ public override async Task ChangeSignatureAsync( return method.WithParameterList(method.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } + if (updatedNode is RecordDeclarationSyntax { ParameterList: not null } record) + { + var updatedParameters = UpdateDeclaration(record.ParameterList.Parameters, signaturePermutation, CreateNewParameterSyntax); + return record.WithParameterList(record.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); + } + if (updatedNode.IsKind(SyntaxKind.LocalFunctionStatement, out LocalFunctionStatementSyntax? localFunction)) { var updatedParameters = UpdateDeclaration(localFunction.ParameterList.Parameters, signaturePermutation, CreateNewParameterSyntax); @@ -745,13 +758,14 @@ private ImmutableArray UpdateParamTagsInLeadingTrivia(Document doc return GetPermutedDocCommentTrivia(document, node, permutedParamNodes); } - private static ImmutableArray VerifyAndPermuteParamNodes(IEnumerable paramNodes, ISymbol declarationSymbol, SignatureChange updatedSignature) + private ImmutableArray VerifyAndPermuteParamNodes(IEnumerable paramNodes, ISymbol declarationSymbol, SignatureChange updatedSignature) { // Only reorder if count and order match originally. var originalParameters = updatedSignature.OriginalConfiguration.ToListOfParameters(); var reorderedParameters = updatedSignature.UpdatedConfiguration.ToListOfParameters(); - var declaredParameters = declarationSymbol.GetParameters(); + var declaredParameters = GetParameters(declarationSymbol); + if (paramNodes.Count() != declaredParameters.Length) { return ImmutableArray.Empty; @@ -875,5 +889,20 @@ protected override bool SupportsOptionalAndParamsArrayParametersSimultaneously() protected override SyntaxToken CommaTokenWithElasticSpace() => Token(SyntaxKind.CommaToken).WithTrailingTrivia(ElasticSpace); + + protected override bool TryGetRecordPrimaryConstructor(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol? primaryConstructor) + => typeSymbol.TryGetRecordPrimaryConstructor(out primaryConstructor); + + protected override ImmutableArray GetParameters(ISymbol declarationSymbol) + { + var declaredParameters = declarationSymbol.GetParameters(); + if (declarationSymbol is INamedTypeSymbol namedTypeSymbol && + namedTypeSymbol.TryGetRecordPrimaryConstructor(out var primaryConstructor)) + { + declaredParameters = primaryConstructor.Parameters; + } + + return declaredParameters; + } } } diff --git a/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs index 3b6c50f2cb124..a19257ab8ec87 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs @@ -81,7 +81,7 @@ public override ImmutableArray FixableDiagnosticIds .WithAdditionalAnnotations(Formatter.Annotation); root = root.ReplaceNode(returnStatement, yieldStatement); - return new MyCodeAction(CSharpFeaturesResources.Replace_return_with_yield_return, document.WithSyntaxRoot(root)); + return new MyCodeAction(document.WithSyntaxRoot(root)); } private static bool TryGetExpressionType( @@ -222,8 +222,8 @@ protected override bool TryGetNode( private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Document newDocument) - : base(title, c => Task.FromResult(newDocument)) + public MyCodeAction(Document newDocument) + : base(CSharpFeaturesResources.Replace_return_with_yield_return, c => Task.FromResult(newDocument), nameof(CSharpFeaturesResources.Replace_return_with_yield_return)) { } } diff --git a/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpChangeToIEnumerableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpChangeToIEnumerableCodeFixProvider.cs index bdcfc384bad64..564d6224c5b4f 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpChangeToIEnumerableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Iterator/CSharpChangeToIEnumerableCodeFixProvider.cs @@ -133,7 +133,7 @@ private static bool TryGetIEnumerableSymbols(SemanticModel model, out INamedType private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Document newDocument) - : base(title, c => Task.FromResult(newDocument)) + : base(title, c => Task.FromResult(newDocument), title) { } } diff --git a/src/Features/CSharp/Portable/CodeFixes/UseInterpolatedVerbatimString/CSharpUseInterpolatedVerbatimStringCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/UseInterpolatedVerbatimString/CSharpUseInterpolatedVerbatimStringCodeFixProvider.cs index 9524ba7990593..fbeabc2b41b9e 100644 --- a/src/Features/CSharp/Portable/CodeFixes/UseInterpolatedVerbatimString/CSharpUseInterpolatedVerbatimStringCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/UseInterpolatedVerbatimString/CSharpUseInterpolatedVerbatimStringCodeFixProvider.cs @@ -80,7 +80,7 @@ private static void AddEdits( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Use_interpolated_verbatim_string, createChangedDocument) + : base(FeaturesResources.Use_interpolated_verbatim_string, createChangedDocument, nameof(FeaturesResources.Use_interpolated_verbatim_string)) { } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs index 728fb9e0bc517..8b3f95398ff49 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/ConvertLocalFunctionToMethod/CSharpConvertLocalFunctionToMethodCodeRefactoringProvider.cs @@ -60,7 +60,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( new MyCodeAction( - CSharpFeaturesResources.Convert_to_method, c => UpdateDocumentAsync(root, document, parentBlock, localFunction, c)), localFunction.Span); } @@ -317,8 +316,8 @@ private static string GenerateUniqueMethodName(ISymbol declaredSymbol) private sealed class MyCodeAction : CodeActions.CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public MyCodeAction(Func> createChangedDocument) + : base(CSharpFeaturesResources.Convert_to_method, createChangedDocument, nameof(CSharpFeaturesResources.Convert_to_method)) { } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs index c3c2897a99e8b..a6031b3de94c3 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs @@ -90,7 +90,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( new MyCodeAction( - CSharpFeaturesResources.Inline_temporary_variable, c => InlineTemporaryAsync(document, variableDeclarator, c)), variableDeclarator.Span); } @@ -631,8 +630,8 @@ private static bool IsInDeconstructionAssignmentLeft(ExpressionSyntax node) private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public MyCodeAction(Func> createChangedDocument) + : base(CSharpFeaturesResources.Inline_temporary_variable, createChangedDocument, nameof(CSharpFeaturesResources.Inline_temporary_variable)) { } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/LambdaSimplifier/LambdaSimplifierCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/LambdaSimplifier/LambdaSimplifierCodeRefactoringProvider.cs index b76a2dce544b5..1c8c35af22d13 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/LambdaSimplifier/LambdaSimplifierCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/LambdaSimplifier/LambdaSimplifierCodeRefactoringProvider.cs @@ -285,7 +285,7 @@ private static InvocationExpressionSyntax TryGetInvocationExpression( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs index 732e3fd84a659..e8abbbb2b2cf2 100644 --- a/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/CodeRefactorings/UseExplicitOrImplicitType/AbstractUseTypeCodeRefactoringProvider.cs @@ -132,7 +132,7 @@ private async Task UpdateDocumentAsync(Document document, TypeSyntax t private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs index 57a8d38828ad3..f096d239f8fff 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -365,6 +366,18 @@ private string GetAttributeValue(XmlAttributeSyntax attribute) } } + protected override ImmutableArray GetParameters(ISymbol declarationSymbol) + { + var declaredParameters = declarationSymbol.GetParameters(); + if (declarationSymbol is INamedTypeSymbol namedTypeSymbol && + namedTypeSymbol.TryGetRecordPrimaryConstructor(out var primaryConstructor)) + { + declaredParameters = primaryConstructor.Parameters; + } + + return declaredParameters; + } + private static readonly CompletionItemRules s_defaultRules = CompletionItemRules.Create( filterCharacterRules: FilterRules, diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs index 25636c745941b..cb1cc5ff656ca 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs @@ -116,7 +116,8 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context // | if (token.Kind() == SyntaxKind.SemicolonToken) { - if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective, SyntaxKind.UsingDirective)) + if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective, SyntaxKind.UsingDirective) + && !token.Parent.Parent.IsKind(SyntaxKind.FileScopedNamespaceDeclaration)) { return true; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs index b9adb86cabfcd..b8e23691c8eb3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs @@ -79,7 +79,9 @@ internal static bool IsUsingDirectiveContext(CSharpSyntaxContext context, bool f // root: u| - // ns Goo { u| + // namespace N { u| + + // namespace N; u| // extern alias a; // u| @@ -98,8 +100,8 @@ internal static bool IsUsingDirectiveContext(CSharpSyntaxContext context, bool f return IsValidContextAtTheRoot(context, originalToken, cancellationToken); } - if (token.Kind() == SyntaxKind.OpenBraceToken && - token.Parent.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax _)) + if ((token.Kind() == SyntaxKind.OpenBraceToken && token.Parent.IsKind(SyntaxKind.NamespaceDeclaration)) + || (token.Kind() == SyntaxKind.SemicolonToken && token.Parent.IsKind(SyntaxKind.FileScopedNamespaceDeclaration))) { // a child using can't come before externs var nextToken = originalToken.GetNextToken(includeSkipped: true); diff --git a/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs index 985d8e16945d2..fae50a6885574 100644 --- a/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertBetweenRegularAndVerbatimString/AbstractConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider.cs @@ -187,7 +187,7 @@ private class MyCodeAction : CodeAction.DocumentChangeAction internal override CodeActionPriority Priority => CodeActionPriority.Low; public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs b/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs index c1b084b4c2b67..207699bfdee64 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/BreakpointSpans.cs @@ -801,6 +801,11 @@ internal static TextSpan GetEnvelope(SyntaxNode declaration) return TextSpan.FromBounds(firstSpan.Start, lastSpan.End); } + if (declaration is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + return TextSpan.FromBounds(unit.Members[0].SpanStart, unit.Members.OfType().Last().Span.End); + } + var body = SyntaxUtilities.TryGetMethodDeclarationBody(declaration); if (body == null) { diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index b55a9e9a39ab9..362af78ee0b3a 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -151,6 +151,7 @@ internal override bool TryFindMemberDeclaration(SyntaxNode? root, SyntaxNode nod /// - for method-like member declarations with block bodies (methods, operators, constructors, destructors, accessors). /// - for variable declarators of fields, properties with an initializer expression, or /// for method-like member declarations with expression bodies (methods, properties, indexers, operators) + /// - for top level statements /// /// A null reference otherwise. /// @@ -161,6 +162,13 @@ internal override bool TryFindMemberDeclaration(SyntaxNode? root, SyntaxNode nod return variableDeclarator.Initializer?.Value; } + if (node is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + // For top level statements, where there is no syntax node to represent the entire body of the synthesized + // main method we just use the compilation unit itself + return node; + } + return SyntaxUtilities.TryGetMethodDeclarationBody(node); } @@ -169,6 +177,11 @@ internal override bool IsDeclarationWithSharedBody(SyntaxNode declaration) protected override ImmutableArray GetCapturedVariables(SemanticModel model, SyntaxNode memberBody) { + if (memberBody is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + return model.AnalyzeDataFlow(((GlobalStatementSyntax)unit.Members[0]).Statement, unit.Members.OfType().Last().Statement)!.Captured; + } + Debug.Assert(memberBody.IsKind(SyntaxKind.Block) || memberBody is ExpressionSyntax); return model.AnalyzeDataFlow(memberBody).Captured; } @@ -639,8 +652,15 @@ private static IEnumerable GetChildNodes(SyntaxNode root, SyntaxNode } } - internal override void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode, ISymbol oldSymbol, ISymbol newSymbol) + internal override void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode, ISymbol oldSymbol, ISymbol newSymbol, CancellationToken cancellationToken) { + // Global statements have a declaring syntax reference to the compilation unit itself, which we can just ignore + // for the purposes of declaration rude edits + if (oldNode.IsKind(SyntaxKind.CompilationUnit) || newNode.IsKind(SyntaxKind.CompilationUnit)) + { + return; + } + // Compiler generated methods of records have a declaring syntax reference to the record declaration itself // but their explicitly implemented counterparts reference the actual member. Compiler generated properties // of records reference the parameter that names them. @@ -652,7 +672,7 @@ internal override void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder a.IsKind(SyntaxKind.SetAccessorDeclaration))) { @@ -683,14 +701,12 @@ internal override void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder p.Name).SequenceEqual(newSymbol.GetParameters().Select(p => p.Name))) + newNode is MethodDeclarationSyntax && + !oldSymbol.GetParameters().Select(p => p.Name).SequenceEqual(newSymbol.GetParameters().Select(p => p.Name))) { // TODO: Remove this requirement with https://github.com/dotnet/roslyn/issues/52563 // Explicitly implemented methods must have parameter names that match the compiler generated versions @@ -704,9 +720,7 @@ newNode is MethodDeclarationSyntax && RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, GetDiagnosticSpan(newNode, EditKind.Update), oldNode, - new[] { - oldSymbol.ToDisplayString(SymbolDisplayFormats.NameFormat) - })); + new[] { oldSymbol.ToDisplayString(SymbolDisplayFormats.NameFormat) })); } } @@ -721,7 +735,7 @@ protected override void ReportLocalFunctionsDeclarationRudeEdits(ArrayBuilder().LastOrDefault()?.Span ?? default; + } + + return GetDiagnosticSpan(node, EditKind.Delete); + } + protected override string LineDirectiveKeyword => "line"; @@ -1100,14 +1125,30 @@ internal override bool IsRecordDeclaration(SyntaxNode node) => node.IsKind(SyntaxKind.RecordDeclaration, SyntaxKind.RecordStructDeclaration); internal override SyntaxNode? TryGetContainingTypeDeclaration(SyntaxNode node) - => node.Parent!.FirstAncestorOrSelf(); + => node is CompilationUnitSyntax ? null : node.Parent!.FirstAncestorOrSelf(); internal override bool HasBackingField(SyntaxNode propertyOrIndexerDeclaration) => propertyOrIndexerDeclaration.IsKind(SyntaxKind.PropertyDeclaration, out PropertyDeclarationSyntax? propertyDecl) && SyntaxUtilities.HasBackingField(propertyDecl); - internal override SyntaxNode? TryGetAssociatedMemberDeclaration(SyntaxNode node) - => node.Parent.IsParentKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration, SyntaxKind.EventDeclaration) ? node.Parent.Parent : null; + internal override bool TryGetAssociatedMemberDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? declaration) + { + if (node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter)) + { + Contract.ThrowIfFalse(node.IsParentKind(SyntaxKind.ParameterList, SyntaxKind.TypeParameterList, SyntaxKind.BracketedParameterList)); + declaration = node.Parent!.Parent!; + return true; + } + + if (node.Parent.IsParentKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration, SyntaxKind.EventDeclaration)) + { + declaration = node.Parent.Parent!; + return true; + } + + declaration = null; + return false; + } internal override bool IsDeclarationWithInitializer(SyntaxNode declaration) => declaration is VariableDeclaratorSyntax { Initializer: not null } || declaration is PropertyDeclarationSyntax { Initializer: not null }; @@ -1163,7 +1204,7 @@ internal override bool IsPartial(INamedTypeSymbol type) protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference reference, CancellationToken cancellationToken) => reference.GetSyntax(cancellationToken); - protected override OneOrMany<(ISymbol? oldSymbol, ISymbol? newSymbol)> GetSymbolsForEdit( + protected override OneOrMany<(ISymbol? oldSymbol, ISymbol? newSymbol, EditKind editKind)> GetSymbolEdits( EditKind editKind, SyntaxNode? oldNode, SyntaxNode? newNode, @@ -1172,14 +1213,15 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen IReadOnlyDictionary editMap, CancellationToken cancellationToken) { - var oldSymbol = (oldNode != null) ? GetSymbolForEdit(oldNode, editKind, oldModel!, cancellationToken) : null; - var newSymbol = (newNode != null) ? GetSymbolForEdit(newNode, editKind, newModel, cancellationToken) : null; + var oldSymbol = (oldNode != null) ? GetSymbolForEdit(oldNode, oldModel!, cancellationToken) : null; + var newSymbol = (newNode != null) ? GetSymbolForEdit(newNode, newModel, cancellationToken) : null; switch (editKind) { case EditKind.Update: Contract.ThrowIfNull(oldNode); Contract.ThrowIfNull(newNode); + Contract.ThrowIfNull(oldModel); // Certain updates of a property/indexer node affects its accessors. // Return all affected symbols for these updates. @@ -1198,7 +1240,7 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen var oldGetterSymbol = ((IPropertySymbol)oldSymbol).GetMethod; var newGetterSymbol = ((IPropertySymbol)newSymbol).GetMethod; - return OneOrMany.Create(ImmutableArray.Create((oldSymbol, newSymbol), (oldGetterSymbol, newGetterSymbol))); + return OneOrMany.Create(ImmutableArray.Create((oldSymbol, newSymbol, editKind), (oldGetterSymbol, newGetterSymbol, editKind))); } // 2) Property/indexer declarations differ in readonly keyword. @@ -1211,18 +1253,18 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen var oldPropertySymbol = (IPropertySymbol)oldSymbol; var newPropertySymbol = (IPropertySymbol)newSymbol; - using var _ = ArrayBuilder<(ISymbol?, ISymbol?)>.GetInstance(out var builder); + using var _ = ArrayBuilder<(ISymbol?, ISymbol?, EditKind)>.GetInstance(out var builder); - builder.Add((oldPropertySymbol, newPropertySymbol)); + builder.Add((oldPropertySymbol, newPropertySymbol, editKind)); if (oldPropertySymbol.GetMethod != null && newPropertySymbol.GetMethod != null && oldPropertySymbol.GetMethod.IsReadOnly != newPropertySymbol.GetMethod.IsReadOnly) { - builder.Add((oldPropertySymbol.GetMethod, newPropertySymbol.GetMethod)); + builder.Add((oldPropertySymbol.GetMethod, newPropertySymbol.GetMethod, editKind)); } if (oldPropertySymbol.SetMethod != null && newPropertySymbol.SetMethod != null && oldPropertySymbol.SetMethod.IsReadOnly != newPropertySymbol.SetMethod.IsReadOnly) { - builder.Add((oldPropertySymbol.SetMethod, newPropertySymbol.SetMethod)); + builder.Add((oldPropertySymbol.SetMethod, newPropertySymbol.SetMethod, editKind)); } return OneOrMany.Create(builder.ToImmutable()); @@ -1231,6 +1273,32 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen static bool DiffersInReadOnlyModifier(SyntaxTokenList oldModifiers, SyntaxTokenList newModifiers) => (oldModifiers.IndexOf(SyntaxKind.ReadOnlyKeyword) >= 0) != (newModifiers.IndexOf(SyntaxKind.ReadOnlyKeyword) >= 0); + // Change in attributes or modifiers of a field affects all its variable declarations. + if (oldNode is BaseFieldDeclarationSyntax oldField && newNode is BaseFieldDeclarationSyntax newField) + { + return GetFieldSymbolUpdates(oldField.Declaration.Variables, newField.Declaration.Variables); + } + + // Chnage in type of a field affects all its variable declarations. + if (oldNode is VariableDeclarationSyntax oldVariableDeclaration && newNode is VariableDeclarationSyntax newVariableDeclaration) + { + return GetFieldSymbolUpdates(oldVariableDeclaration.Variables, newVariableDeclaration.Variables); + } + + OneOrMany<(ISymbol?, ISymbol?, EditKind)> GetFieldSymbolUpdates(SeparatedSyntaxList oldVariables, SeparatedSyntaxList newVariables) + { + if (oldVariables.Count == 1 && newVariables.Count == 1) + { + return OneOrMany.Create((oldModel.GetDeclaredSymbol(oldVariables[0], cancellationToken), newModel.GetDeclaredSymbol(newVariables[0], cancellationToken), EditKind.Update)); + } + + var result = from oldVariable in oldVariables + join newVariable in newVariables on oldVariable.Identifier.Text equals newVariable.Identifier.Text + select (oldModel.GetDeclaredSymbol(oldVariable, cancellationToken), newModel.GetDeclaredSymbol(newVariable, cancellationToken), EditKind.Update); + + return OneOrMany.Create(result.ToImmutableArray()); + } + break; case EditKind.Delete: @@ -1245,7 +1313,7 @@ static bool DiffersInReadOnlyModifier(SyntaxTokenList oldModifiers, SyntaxTokenL if (HasEdit(editMap, node.Parent, editKind) && !HasEdit(editMap, node.Parent.Parent, editKind)) { - return OneOrMany<(ISymbol?, ISymbol?)>.Empty; + return OneOrMany<(ISymbol?, ISymbol?, EditKind)>.Empty; } } @@ -1257,52 +1325,49 @@ static bool DiffersInReadOnlyModifier(SyntaxTokenList oldModifiers, SyntaxTokenL { var oldGetterSymbol = ((IPropertySymbol?)oldSymbol)?.GetMethod; var newGetterSymbol = ((IPropertySymbol?)newSymbol)?.GetMethod; - return OneOrMany.Create(ImmutableArray.Create((oldSymbol, newSymbol), (oldGetterSymbol, newGetterSymbol))); + return OneOrMany.Create(ImmutableArray.Create((oldSymbol, newSymbol, editKind), (oldGetterSymbol, newGetterSymbol, editKind))); + } + + // Inserting/deleting a type parameter constraint should result in an update of the corresponding type parameter symbol: + if (node.IsKind(SyntaxKind.TypeParameterConstraintClause)) + { + return OneOrMany.Create(ImmutableArray.Create((oldSymbol, newSymbol, EditKind.Update))); + } + + // Inserting/deleting a global statement should result in an update of the implicit main method: + if (node.IsKind(SyntaxKind.GlobalStatement)) + { + return OneOrMany.Create(ImmutableArray.Create((oldSymbol, newSymbol, EditKind.Update))); } break; } return (editKind == EditKind.Delete ? oldSymbol : newSymbol) is null ? - OneOrMany<(ISymbol?, ISymbol?)>.Empty : new OneOrMany<(ISymbol?, ISymbol?)>((oldSymbol, newSymbol)); + OneOrMany<(ISymbol?, ISymbol?, EditKind)>.Empty : new OneOrMany<(ISymbol?, ISymbol?, EditKind)>((oldSymbol, newSymbol, editKind)); } private static ISymbol? GetSymbolForEdit( SyntaxNode node, - EditKind editKind, SemanticModel model, CancellationToken cancellationToken) { - if (editKind == EditKind.Update) + if (node.IsKind(SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration)) { - if (node.IsKind(SyntaxKind.Parameter)) - { - // If this is a parameter of a delegate, the symbol will be the Invoke method, but we need to go back up to the delegate itself - // so the analysis can see the attributes - var parameterSymbol = model.GetRequiredDeclaredSymbol(node, cancellationToken); - if (parameterSymbol.ContainingSymbol is IMethodSymbol { MethodKind: MethodKind.DelegateInvoke } invokeMethodSymbol) - { - parameterSymbol = invokeMethodSymbol.ContainingSymbol; - } - - return parameterSymbol; - } - - if (node is FieldDeclarationSyntax field) - { - // If attributes on a field change then we get the field declaration here, but GetDeclaredSymbol needs an actual variable declaration - // Fortunately attributes are shared across all of them, so we don't need to be too fancy - return model.GetDeclaredSymbol(field.Declaration.Variables.First(), cancellationToken); - } + return null; } - else if (node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter)) + + if (node.IsKind(SyntaxKind.TypeParameterConstraintClause)) { - return null; + var constraintClause = (TypeParameterConstraintClauseSyntax)node; + var symbolInfo = model.GetSymbolInfo(constraintClause.Name, cancellationToken); + return symbolInfo.Symbol; } - if (node.IsKind(SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration)) + // Top level code always lives in a synthesized Main method + if (node.IsKind(SyntaxKind.GlobalStatement)) { - return null; + return model.GetEnclosingSymbol(node.SpanStart, cancellationToken); } var symbol = model.GetDeclaredSymbol(node, cancellationToken); @@ -1388,36 +1453,6 @@ internal override bool QueryClauseLambdasTypeEquivalent(SemanticModel oldModel, } } - protected override void ReportLambdaSignatureRudeEdits( - SemanticModel oldModel, - SyntaxNode oldLambdaBody, - SemanticModel newModel, - SyntaxNode newLambdaBody, - EditAndContinueCapabilities capabilities, - ArrayBuilder diagnostics, - out bool hasErrors, - CancellationToken cancellationToken) - { - base.ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaBody, capabilities, diagnostics, out hasErrors, cancellationToken); - - if (IsLocalFunctionBody(oldLambdaBody) != IsLocalFunctionBody(newLambdaBody)) - { - var newLambda = GetLambda(newLambdaBody); - diagnostics.Add(new RudeEditDiagnostic( - RudeEditKind.SwitchBetweenLambdaAndLocalFunction, - GetDiagnosticSpan(newLambda, EditKind.Update), - newLambda, - new[] { GetDisplayName(newLambda) })); - hasErrors = true; - } - } - - private static bool IsLocalFunctionBody(SyntaxNode lambdaBody) - { - var lambda = LambdaUtilities.GetLambda(lambdaBody); - return lambda.Kind() == SyntaxKind.LocalFunctionStatement; - } - private static bool GroupBySignatureComparer(ImmutableArray oldParameters, ITypeSymbol oldReturnType, ImmutableArray newParameters, ITypeSymbol newReturnType) { // C# spec paragraph 7.16.2.6 "Groupby clauses": @@ -1433,7 +1468,7 @@ private static bool GroupBySignatureComparer(ImmutableArray ol // C> GroupBy(Func keySelector); // C> GroupBy(Func keySelector, Func elementSelector); - if (!s_assemblyEqualityComparer.Equals(oldReturnType, newReturnType)) + if (!TypesEquivalent(oldReturnType, newReturnType, exact: false)) { return false; } @@ -1444,14 +1479,14 @@ private static bool GroupBySignatureComparer(ImmutableArray ol // The types of the lambdas have to be the same if present. // The element selector may be added/removed. - if (!s_assemblyEqualityComparer.ParameterEquivalenceComparer.Equals(oldParameters[0], newParameters[0])) + if (!ParameterTypesEquivalent(oldParameters[0], newParameters[0], exact: false)) { return false; } if (oldParameters.Length == newParameters.Length && newParameters.Length == 2) { - return s_assemblyEqualityComparer.ParameterEquivalenceComparer.Equals(oldParameters[1], newParameters[1]); + return ParameterTypesEquivalent(oldParameters[1], newParameters[1], exact: false); } return true; @@ -1481,7 +1516,6 @@ private static bool GroupBySignatureComparer(ImmutableArray ol return default(TextSpan); case SyntaxKind.GlobalStatement: - // TODO: return node.Span; case SyntaxKind.ExternAliasDirective: @@ -1489,7 +1523,8 @@ private static bool GroupBySignatureComparer(ImmutableArray ol return node.Span; case SyntaxKind.NamespaceDeclaration: - var ns = (NamespaceDeclarationSyntax)node; + case SyntaxKind.FileScopedNamespaceDeclaration: + var ns = (BaseNamespaceDeclarationSyntax)node; return TextSpan.FromBounds(ns.NamespaceKeyword.SpanStart, ns.Name.Span.End); case SyntaxKind.ClassDeclaration: @@ -1810,6 +1845,29 @@ internal override TextSpan GetLambdaParameterDiagnosticSpan(SyntaxNode lambda, i } } + internal override string GetDisplayName(INamedTypeSymbol symbol) + => symbol.TypeKind switch + { + TypeKind.Struct => symbol.IsRecord ? CSharpFeaturesResources.record_struct : CSharpFeaturesResources.struct_, + TypeKind.Class => symbol.IsRecord ? CSharpFeaturesResources.record_ : FeaturesResources.class_, + _ => base.GetDisplayName(symbol) + }; + + internal override string GetDisplayName(IPropertySymbol symbol) + => symbol.IsIndexer ? CSharpFeaturesResources.indexer : base.GetDisplayName(symbol); + + internal override string GetDisplayName(IMethodSymbol symbol) + => symbol.MethodKind switch + { + MethodKind.PropertyGet => symbol.AssociatedSymbol is IPropertySymbol { IsIndexer: true } ? CSharpFeaturesResources.indexer_getter : CSharpFeaturesResources.property_getter, + MethodKind.PropertySet => symbol.AssociatedSymbol is IPropertySymbol { IsIndexer: true } ? CSharpFeaturesResources.indexer_setter : CSharpFeaturesResources.property_setter, + MethodKind.StaticConstructor => FeaturesResources.static_constructor, + MethodKind.Destructor => CSharpFeaturesResources.destructor, + MethodKind.Conversion => CSharpFeaturesResources.conversion_operator, + MethodKind.LocalFunction => FeaturesResources.local_function, + _ => base.GetDisplayName(symbol) + }; + protected override string? TryGetDisplayName(SyntaxNode node, EditKind editKind) => TryGetDisplayNameImpl(node, editKind); @@ -2103,7 +2161,6 @@ private readonly struct EditClassifier private readonly SyntaxNode? _newNode; private readonly EditKind _kind; private readonly TextSpan? _span; - private readonly bool _classifyStatementSyntax; public EditClassifier( CSharpEditAndContinueAnalyzer analyzer, @@ -2112,8 +2169,7 @@ public EditClassifier( SyntaxNode? newNode, EditKind kind, Match? match = null, - TextSpan? span = null, - bool classifyStatementSyntax = false) + TextSpan? span = null) { RoslynDebug.Assert(oldNode != null || newNode != null); @@ -2127,7 +2183,6 @@ public EditClassifier( _kind = kind; _span = span; _match = match; - _classifyStatementSyntax = classifyStatementSyntax; } private void ReportError(RudeEditKind kind, SyntaxNode? spanNode = null, SyntaxNode? displayNode = null) @@ -2207,8 +2262,6 @@ private void ClassifyReorder(SyntaxNode newNode) switch (newNode.Kind()) { case SyntaxKind.GlobalStatement: - // TODO: - ReportError(RudeEditKind.Move); return; case SyntaxKind.ExternAliasDirective: @@ -2273,8 +2326,8 @@ private void ClassifyInsert(SyntaxNode node) switch (node.Kind()) { case SyntaxKind.GlobalStatement: - // TODO: - ReportError(RudeEditKind.Insert); + // An insert of a global statement is actually an update to the synthesized main so we need to check some extra things + ClassifyUpdate((GlobalStatementSyntax)node); return; case SyntaxKind.ExternAliasDirective: @@ -2282,40 +2335,6 @@ private void ClassifyInsert(SyntaxNode node) ReportError(RudeEditKind.Insert); return; - case SyntaxKind.UsingDirective: - // We don't report rude edits for using directives though we also don't do any - // special processing, so inserting a using directive that changes semantics in - // unedited code will not issue edits for that code. Inserting a using directive - // and then consunming it will result in edits for the changes code as per usual. - return; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.StructDeclaration: - case SyntaxKind.RecordDeclaration: - case SyntaxKind.RecordStructDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.DelegateDeclaration: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.EventDeclaration: - case SyntaxKind.IndexerDeclaration: - case SyntaxKind.DestructorDeclaration: - case SyntaxKind.ConversionOperatorDeclaration: - case SyntaxKind.OperatorDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.ConstructorDeclaration: - case SyntaxKind.GetAccessorDeclaration: - case SyntaxKind.SetAccessorDeclaration: - case SyntaxKind.InitAccessorDeclaration: - case SyntaxKind.AddAccessorDeclaration: - case SyntaxKind.RemoveAccessorDeclaration: - case SyntaxKind.FieldDeclaration: - case SyntaxKind.EventFieldDeclaration: - case SyntaxKind.AccessorList: - case SyntaxKind.VariableDeclarator: - case SyntaxKind.VariableDeclaration: - return; - case SyntaxKind.ArrowExpressionClause: if (node.Parent.IsKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration)) { @@ -2324,26 +2343,6 @@ private void ClassifyInsert(SyntaxNode node) break; - case SyntaxKind.Parameter when !_classifyStatementSyntax: - // Parameter inserts are allowed for local functions - if (node.Parent?.Parent is RecordDeclarationSyntax) - { - ReportError(RudeEditKind.AddRecordPositionalParameter); - } - else - { - ReportError(RudeEditKind.Insert); - } - - return; - - case SyntaxKind.EnumMemberDeclaration: - case SyntaxKind.TypeParameter: - case SyntaxKind.TypeParameterConstraintClause: - case SyntaxKind.TypeParameterList: - ReportError(RudeEditKind.Insert); - return; - case SyntaxKind.Attribute: case SyntaxKind.AttributeList: // To allow inserting of attributes we need to check if the inserted attribute @@ -2358,12 +2357,6 @@ private void ClassifyInsert(SyntaxNode node) return; } - - // When classifying statement syntax we could see potentially any node as an edit - if (!_classifyStatementSyntax) - { - throw ExceptionUtilities.UnexpectedValue(node.Kind()); - } } #endregion @@ -2375,8 +2368,6 @@ private void ClassifyDelete(SyntaxNode oldNode) switch (oldNode.Kind()) { case SyntaxKind.GlobalStatement: - // TODO: - ReportError(RudeEditKind.Delete); return; case SyntaxKind.ExternAliasDirective: @@ -2386,47 +2377,6 @@ private void ClassifyDelete(SyntaxNode oldNode) ReportError(RudeEditKind.Delete); return; - case SyntaxKind.UsingDirective: - // We don't report rude edits for using directives though we also don't do any - // special processing, so deleting a using directive that changes semantics in - // unedited code will not issue edits for that code. Deleting code and then doing - // something like Remove Unused Usings will report edits for the changes code as - // normal. - return; - - case SyntaxKind.DestructorDeclaration: - case SyntaxKind.ConversionOperatorDeclaration: - case SyntaxKind.OperatorDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.StructDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.RecordDeclaration: - case SyntaxKind.RecordStructDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.IndexerDeclaration: - case SyntaxKind.EventDeclaration: - case SyntaxKind.FieldDeclaration: - case SyntaxKind.EventFieldDeclaration: - case SyntaxKind.VariableDeclarator: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.DelegateDeclaration: - case SyntaxKind.ConstructorDeclaration: - // We do not report member delete here since the member might be moving to a different part of a partial type declaration. - // If that is not the case the semantic analysis reports the rude edit. - return; - - case SyntaxKind.GetAccessorDeclaration: - case SyntaxKind.SetAccessorDeclaration: - case SyntaxKind.InitAccessorDeclaration: - case SyntaxKind.AddAccessorDeclaration: - case SyntaxKind.RemoveAccessorDeclaration: - case SyntaxKind.EnumMemberDeclaration: - case SyntaxKind.AccessorList: - // We do not report error here since it will be reported in semantic analysis. - return; - case SyntaxKind.ArrowExpressionClause: // We do not report error here since it will be reported in semantic analysis. if (oldNode.Parent.IsKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration)) @@ -2449,31 +2399,6 @@ private void ClassifyDelete(SyntaxNode oldNode) } return; - - case SyntaxKind.TypeParameter: - case SyntaxKind.TypeParameterList: - case SyntaxKind.TypeParameterConstraintClause: - ReportError(RudeEditKind.Delete); - return; - - case SyntaxKind.Parameter when !_classifyStatementSyntax: - case SyntaxKind.ParameterList when !_classifyStatementSyntax: - if (oldNode.Parent?.Parent is RecordDeclarationSyntax) - { - ReportError(RudeEditKind.DeleteRecordPositionalParameter); - } - else - { - ReportError(RudeEditKind.Delete); - } - - return; - } - - // When classifying statement syntax we could see potentially any node as an edit - if (!_classifyStatementSyntax) - { - throw ExceptionUtilities.UnexpectedValue(oldNode.Kind()); } } @@ -2486,48 +2411,24 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) switch (newNode.Kind()) { case SyntaxKind.GlobalStatement: - ReportError(RudeEditKind.Update); + ClassifyUpdate((GlobalStatementSyntax)newNode); return; case SyntaxKind.ExternAliasDirective: ReportError(RudeEditKind.Update); return; - case SyntaxKind.UsingDirective: - // We don't report rude edits for using directives though we also don't do any - // special processing, so updating a using directive that changes semantics in - // unedited code will not issue edits for that code. - return; - case SyntaxKind.NamespaceDeclaration: - ClassifyUpdate((NamespaceDeclarationSyntax)oldNode, (NamespaceDeclarationSyntax)newNode); - return; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.StructDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.RecordDeclaration: - case SyntaxKind.RecordStructDeclaration: - ClassifyUpdate((TypeDeclarationSyntax)oldNode, (TypeDeclarationSyntax)newNode); - return; - - case SyntaxKind.EnumDeclaration: - ClassifyUpdate((EnumDeclarationSyntax)oldNode, (EnumDeclarationSyntax)newNode); - return; - - case SyntaxKind.DelegateDeclaration: - ClassifyUpdate((DelegateDeclarationSyntax)oldNode, (DelegateDeclarationSyntax)newNode); - return; - - case SyntaxKind.FieldDeclaration: - ClassifyUpdate((BaseFieldDeclarationSyntax)oldNode, (BaseFieldDeclarationSyntax)newNode); - return; - - case SyntaxKind.EventFieldDeclaration: - ClassifyUpdate((BaseFieldDeclarationSyntax)oldNode, (BaseFieldDeclarationSyntax)newNode); + case SyntaxKind.FileScopedNamespaceDeclaration: + ClassifyUpdate((BaseNamespaceDeclarationSyntax)oldNode, (BaseNamespaceDeclarationSyntax)newNode); return; case SyntaxKind.VariableDeclaration: - ClassifyUpdate((VariableDeclarationSyntax)oldNode, (VariableDeclarationSyntax)newNode); + if (!oldNode.IsParentKind(SyntaxKind.FieldDeclaration, SyntaxKind.EventFieldDeclaration)) + { + ClassifyUpdate((VariableDeclarationSyntax)oldNode, (VariableDeclarationSyntax)newNode); + } + return; case SyntaxKind.VariableDeclarator: @@ -2562,9 +2463,6 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) ClassifyUpdate((IndexerDeclarationSyntax)oldNode, (IndexerDeclarationSyntax)newNode); return; - case SyntaxKind.EventDeclaration: - return; - case SyntaxKind.EnumMemberDeclaration: ClassifyUpdate((EnumMemberDeclarationSyntax)oldNode, (EnumMemberDeclarationSyntax)newNode); return; @@ -2577,23 +2475,6 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) ClassifyUpdate((AccessorDeclarationSyntax)oldNode, (AccessorDeclarationSyntax)newNode); return; - case SyntaxKind.TypeParameterConstraintClause: - ClassifyUpdate((TypeParameterConstraintClauseSyntax)oldNode, (TypeParameterConstraintClauseSyntax)newNode); - return; - - case SyntaxKind.TypeParameter: - ClassifyUpdate((TypeParameterSyntax)oldNode, (TypeParameterSyntax)newNode); - return; - - case SyntaxKind.Parameter when !_classifyStatementSyntax: - // Parameter updates are allowed for local functions - ClassifyUpdate((ParameterSyntax)oldNode, (ParameterSyntax)newNode); - return; - - case SyntaxKind.AttributeList: - ClassifyUpdate((AttributeListSyntax)oldNode, (AttributeListSyntax)newNode); - return; - case SyntaxKind.Attribute: // To allow update of attributes we need to check if the updated attribute // is a pseudo-custom attribute that CLR allows us to change, or if it is a compiler well-know attribute @@ -2606,81 +2487,18 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) } return; - - case SyntaxKind.TypeParameterList: - case SyntaxKind.ParameterList: - case SyntaxKind.BracketedParameterList: - case SyntaxKind.AccessorList: - return; - - } - - // When classifying statement syntax we could see potentially any node as an edit - if (!_classifyStatementSyntax) - { - throw ExceptionUtilities.UnexpectedValue(newNode.Kind()); } } - private void ClassifyUpdate(NamespaceDeclarationSyntax oldNode, NamespaceDeclarationSyntax newNode) + private void ClassifyUpdate(GlobalStatementSyntax node) { - Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.Name, newNode.Name)); - ReportError(RudeEditKind.Renamed); + ClassifyDeclarationBodyRudeUpdates(node.Statement); } - private void ClassifyUpdate(TypeDeclarationSyntax oldNode, TypeDeclarationSyntax newNode) + private void ClassifyUpdate(BaseNamespaceDeclarationSyntax oldNode, BaseNamespaceDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.BaseList, newNode.BaseList)) - { - ReportError(RudeEditKind.BaseTypeOrInterfaceUpdate); - } - } - - private void ClassifyUpdate(EnumDeclarationSyntax oldNode, EnumDeclarationSyntax newNode) - { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.BaseList, newNode.BaseList)) - { - ReportError(RudeEditKind.EnumUnderlyingTypeUpdate); - return; - } - - // The list of members has been updated (separators added). - // We report a Rude Edit for each updated member. - } - - private void ClassifyUpdate(DelegateDeclarationSyntax oldNode, DelegateDeclarationSyntax newNode) - { - if (!SyntaxFactory.AreEquivalent(oldNode.ReturnType, newNode.ReturnType)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - } - } - - private void ClassifyUpdate(BaseFieldDeclarationSyntax oldNode, BaseFieldDeclarationSyntax newNode) - { - if (oldNode.Kind() != newNode.Kind()) - { - ReportError(RudeEditKind.FieldKindUpdate); - return; - } + Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.Name, newNode.Name)); + ReportError(RudeEditKind.Renamed); } private void ClassifyUpdate(VariableDeclarationSyntax oldNode, VariableDeclarationSyntax newNode) @@ -2690,18 +2508,10 @@ private void ClassifyUpdate(VariableDeclarationSyntax oldNode, VariableDeclarati ReportError(RudeEditKind.TypeUpdate); return; } - - // separators may be added/removed: } private void ClassifyUpdate(VariableDeclaratorSyntax oldNode, VariableDeclaratorSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - // If the argument lists are mismatched the field must have mismatched "fixed" modifier, // which is reported by the field declaration. if (oldNode.ArgumentList is null == newNode.ArgumentList is null) @@ -2733,24 +2543,6 @@ private void ClassifyUpdate(VariableDeclaratorSyntax oldNode, VariableDeclarator private void ClassifyUpdate(MethodDeclarationSyntax oldNode, MethodDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.ReturnType, newNode.ReturnType)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.ExplicitInterfaceSpecifier, newNode.ExplicitInterfaceSpecifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - ClassifyMethodBodyRudeUpdate( (SyntaxNode?)oldNode.Body ?? oldNode.ExpressionBody?.Expression, (SyntaxNode?)newNode.Body ?? newNode.ExpressionBody?.Expression, @@ -2760,18 +2552,6 @@ private void ClassifyUpdate(MethodDeclarationSyntax oldNode, MethodDeclarationSy private void ClassifyUpdate(ConversionOperatorDeclarationSyntax oldNode, ConversionOperatorDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.ImplicitOrExplicitKeyword, newNode.ImplicitOrExplicitKeyword)) - { - ReportError(RudeEditKind.ModifiersUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - ClassifyMethodBodyRudeUpdate( (SyntaxNode?)oldNode.Body ?? oldNode.ExpressionBody?.Expression, (SyntaxNode?)newNode.Body ?? newNode.ExpressionBody?.Expression, @@ -2781,18 +2561,6 @@ private void ClassifyUpdate(ConversionOperatorDeclarationSyntax oldNode, Convers private void ClassifyUpdate(OperatorDeclarationSyntax oldNode, OperatorDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.OperatorToken, newNode.OperatorToken)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.ReturnType, newNode.ReturnType)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - ClassifyMethodBodyRudeUpdate( (SyntaxNode?)oldNode.Body ?? oldNode.ExpressionBody?.Expression, (SyntaxNode?)newNode.Body ?? newNode.ExpressionBody?.Expression, @@ -2804,7 +2572,6 @@ private void ClassifyUpdate(AccessorDeclarationSyntax oldNode, AccessorDeclarati { if (oldNode.Kind() != newNode.Kind()) { - ReportError(RudeEditKind.AccessorKindUpdate); return; } @@ -2820,12 +2587,6 @@ private void ClassifyUpdate(AccessorDeclarationSyntax oldNode, AccessorDeclarati private void ClassifyUpdate(EnumMemberDeclarationSyntax oldNode, EnumMemberDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - if (!SyntaxFactory.AreEquivalent(oldNode.EqualsValue, newNode.EqualsValue)) { ReportError(RudeEditKind.InitializerUpdate); @@ -2855,24 +2616,6 @@ private void ClassifyUpdate(DestructorDeclarationSyntax oldNode, DestructorDecla private void ClassifyUpdate(PropertyDeclarationSyntax oldNode, PropertyDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.ExplicitInterfaceSpecifier, newNode.ExplicitInterfaceSpecifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - var containingType = (TypeDeclarationSyntax)newNode.Parent!; // TODO: We currently don't support switching from auto-props to properties with accessors and vice versa. @@ -2909,18 +2652,6 @@ private void ClassifyUpdate(PropertyDeclarationSyntax oldNode, PropertyDeclarati private void ClassifyUpdate(IndexerDeclarationSyntax oldNode, IndexerDeclarationSyntax newNode) { - if (!SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.ExplicitInterfaceSpecifier, newNode.ExplicitInterfaceSpecifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - if (SyntaxFactory.AreEquivalent(oldNode.ExpressionBody, newNode.ExpressionBody)) { var oldBody = SyntaxUtilities.TryGetEffectiveGetterBody(oldNode.ExpressionBody, oldNode.AccessorList); @@ -2934,96 +2665,6 @@ private void ClassifyUpdate(IndexerDeclarationSyntax oldNode, IndexerDeclaration } } - private void ClassifyUpdate(TypeParameterSyntax oldNode, TypeParameterSyntax newNode) - { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.VarianceKeyword, newNode.VarianceKeyword)) - { - ReportError(RudeEditKind.VarianceUpdate); - return; - } - - // attribute changes are handled by semantics - } - - private void ClassifyUpdate(TypeParameterConstraintClauseSyntax oldNode, TypeParameterConstraintClauseSyntax newNode) - { - if (!SyntaxFactory.AreEquivalent(oldNode.Name, newNode.Name)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.Constraints, newNode.Constraints)); - ReportError(RudeEditKind.TypeUpdate); - } - - private void ClassifyUpdate(ParameterSyntax oldNode, ParameterSyntax newNode) - { - if (!SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - { - ReportError(RudeEditKind.Renamed); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers)) - { - ReportError(RudeEditKind.ModifiersUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type)) - { - ReportError(RudeEditKind.TypeUpdate); - return; - } - - if (!SyntaxFactory.AreEquivalent(oldNode.Default, newNode.Default)) - { - ReportError(RudeEditKind.InitializerUpdate); - return; - } - - // Attribute changes handled in semantics - } - - private void ClassifyUpdate(AttributeListSyntax oldNode, AttributeListSyntax newNode) - { - if (!SyntaxFactory.AreEquivalent(oldNode.Target, newNode.Target)) - { - var spanNode = ((SyntaxNode?)newNode.Target) ?? newNode; - ReportError(RudeEditKind.Update, spanNode: spanNode, displayNode: spanNode); - return; - } - - // changes in attribute separators are not interesting: - } - - private static bool AreModifiersEquivalent(SyntaxTokenList oldModifiers, SyntaxTokenList newModifiers, SyntaxKind ignore, SyntaxKind? ignore2 = null) - { - var oldIgnoredModifierIndex = oldModifiers.IndexOf(ignore); - var newIgnoredModifierIndex = newModifiers.IndexOf(ignore); - - if (oldIgnoredModifierIndex >= 0) - { - oldModifiers = oldModifiers.RemoveAt(oldIgnoredModifierIndex); - } - - if (newIgnoredModifierIndex >= 0) - { - newModifiers = newModifiers.RemoveAt(newIgnoredModifierIndex); - } - - return ignore2 is null - ? SyntaxFactory.AreEquivalent(oldModifiers, newModifiers) - : AreModifiersEquivalent(oldModifiers, newModifiers, ignore2.Value); - } - private void ClassifyMethodBodyRudeUpdate( SyntaxNode? oldBody, SyntaxNode? newBody, @@ -3160,6 +2801,12 @@ internal override void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder RudeEditKind.InsertIntoInterface, + // Inserting a field into an enum: +#pragma warning disable format // https://github.com/dotnet/roslyn/issues/54759 + IFieldSymbol { ContainingType.TypeKind: TypeKind.Enum } + => RudeEditKind.Insert, +#pragma warning restore format + _ => RudeEditKind.None }; @@ -3173,44 +2820,6 @@ internal override void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder diagnostics, INamedTypeSymbol oldType, INamedTypeSymbol newType, SyntaxNode newDeclaration, CancellationToken cancellationToken) - { - using var _1 = ArrayBuilder.GetInstance(out var oldNodes); - using var _2 = ArrayBuilder.GetInstance(out var newNodes); - - // Consider: better error messages - Report((b, t) => AddNodes(b, t.AttributeLists), RudeEditKind.Update); - Report((b, t) => AddNodes(b, t.TypeParameterList?.Parameters), RudeEditKind.Update); - Report((b, t) => AddNodes(b, t.ConstraintClauses), RudeEditKind.Update); - Report((b, t) => AddNodes(b, t.BaseList?.Types), RudeEditKind.BaseTypeOrInterfaceUpdate); - - void Report(Action, TypeDeclarationSyntax> addNodes, RudeEditKind rudeEditKind) - { - foreach (var syntaxRef in oldType.DeclaringSyntaxReferences) - { - addNodes(oldNodes, (TypeDeclarationSyntax)syntaxRef.GetSyntax(cancellationToken)); - } - - foreach (var syntaxRef in newType.DeclaringSyntaxReferences) - { - addNodes(newNodes, (TypeDeclarationSyntax)syntaxRef.GetSyntax(cancellationToken)); - } - - if (oldNodes.Count != newNodes.Count || - oldNodes.Zip(newNodes, (oldNode, newNode) => SyntaxFactory.AreEquivalent(oldNode, newNode)).Any(isEquivalent => !isEquivalent)) - { - diagnostics.Add(new RudeEditDiagnostic( - rudeEditKind, - GetDiagnosticSpan(newDeclaration, EditKind.Update), - newDeclaration, - arguments: new[] { GetDisplayName(newDeclaration, EditKind.Update) })); - } - - oldNodes.Clear(); - newNodes.Clear(); - } - } - #endregion #region Exception Handling Rude Edits diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 84384afef4d08..b48b918cb63bc 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -53,6 +53,8 @@ internal enum Label // Top level syntax kinds CompilationUnit, + GlobalStatement, + NamespaceDeclaration, ExternAliasDirective, // tied to parent UsingDirective, // tied to parent @@ -310,6 +312,17 @@ private static Label ClassifyStatementSyntax(SyntaxKind kind, SyntaxNode? node, // Expressions are ignored but they may contain nodes that should be matched by tree comparer. // (e.g. lambdas, declaration expressions). Descending to these nodes is handled in EnumerateChildren. + case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.RecordDeclaration: + case SyntaxKind.RecordStructDeclaration: + // These declarations can come after global statements so we want to stop statement matching + // because no global statements can come after them + isLeaf = true; + return Label.Ignored; + case SyntaxKind.LocalDeclarationStatement: return Label.LocalDeclarationStatement; @@ -541,9 +554,8 @@ private static Label ClassifyTopSyntax(SyntaxKind kind, SyntaxNode? node, out bo switch (kind) { case SyntaxKind.GlobalStatement: - // TODO: isLeaf = true; - return Label.Ignored; + return Label.GlobalStatement; case SyntaxKind.ExternAliasDirective: isLeaf = true; @@ -554,6 +566,7 @@ private static Label ClassifyTopSyntax(SyntaxKind kind, SyntaxNode? node, out bo return Label.UsingDirective; case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: return Label.NamespaceDeclaration; case SyntaxKind.ClassDeclaration: @@ -1355,7 +1368,8 @@ private static double CombineOptional( return ((UsingDirectiveSyntax)node).Name; case SyntaxKind.NamespaceDeclaration: - return ((NamespaceDeclarationSyntax)node).Name; + case SyntaxKind.FileScopedNamespaceDeclaration: + return ((BaseNamespaceDeclarationSyntax)node).Name; case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs index 333e146172c19..9ca36f86ba051 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs @@ -116,9 +116,25 @@ public static void AssertIsBody(SyntaxNode syntax, bool allowLambda) return; } + // special case for top level statements, which have no containing block other than the compilation unit + if (syntax is CompilationUnitSyntax unit && unit.ContainsTopLevelStatements()) + { + return; + } + Debug.Assert(false); } + public static bool ContainsTopLevelStatements(this CompilationUnitSyntax compilationUnit) + { + if (compilationUnit.Members.Count == 0) + { + return false; + } + + return compilationUnit.Members[0] is GlobalStatementSyntax; + } + public static void FindLeafNodeAndPartner(SyntaxNode leftRoot, int leftPosition, SyntaxNode rightRoot, out SyntaxNode leftNode, out SyntaxNode rightNodeOpt) { leftNode = leftRoot; diff --git a/src/Features/CSharp/Portable/ImplementInterface/AbstractChangeImplementionCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ImplementInterface/AbstractChangeImplementionCodeRefactoringProvider.cs index a8456785be94a..39469714791b0 100644 --- a/src/Features/CSharp/Portable/ImplementInterface/AbstractChangeImplementionCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ImplementInterface/AbstractChangeImplementionCodeRefactoringProvider.cs @@ -249,7 +249,7 @@ await UpdateReferencesAsync( private class MyCodeAction : CodeAction.SolutionChangeAction { public MyCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs index 2132e02303889..3a80dc9a83628 100644 --- a/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeRefactoringProvider.cs @@ -48,15 +48,14 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte if (MakeLocalFunctionStaticHelper.CanMakeLocalFunctionStaticByRefactoringCaptures(localFunction, semanticModel, out var captures)) { context.RegisterRefactoring(new MyCodeAction( - CSharpAnalyzersResources.Make_local_function_static, c => MakeLocalFunctionStaticCodeFixHelper.MakeLocalFunctionStaticAsync(document, localFunction, captures, c))); } } private class MyCodeAction : CustomCodeActions.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public MyCodeAction(Func> createChangedDocument) + : base(CSharpAnalyzersResources.Make_local_function_static, createChangedDocument, nameof(CSharpAnalyzersResources.Make_local_function_static)) { } } diff --git a/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs index fddaffe89bd56..f0049d8bbfe0a 100644 --- a/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs @@ -396,7 +396,7 @@ private static ExpressionSyntax InvertAfter(ExpressionSyntax after) private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(CSharpFeaturesResources.Reverse_for_statement, createChangedDocument) + : base(CSharpFeaturesResources.Reverse_for_statement, createChangedDocument, nameof(CSharpFeaturesResources.Reverse_for_statement)) { } } diff --git a/src/Features/CSharp/Portable/UseExplicitTypeForConst/UseExplicitTypeForConstCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExplicitTypeForConst/UseExplicitTypeForConstCodeFixProvider.cs index 8021d4ce2c266..5bd37b9bada53 100644 --- a/src/Features/CSharp/Portable/UseExplicitTypeForConst/UseExplicitTypeForConstCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExplicitTypeForConst/UseExplicitTypeForConstCodeFixProvider.cs @@ -72,7 +72,8 @@ private sealed class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) : base(CSharpAnalyzersResources.Use_explicit_type_instead_of_var, - createChangedDocument) + createChangedDocument, + nameof(CSharpAnalyzersResources.Use_explicit_type_instead_of_var)) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs index 63ca954f4ea1f..ddd030b16e87f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs @@ -155,7 +155,7 @@ private static async Task UpdateDocumentAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeStyleProvider.cs b/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeStyleProvider.cs index 9aedba0f55817..f62383a14a528 100644 --- a/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeStyleProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeStyleProvider.cs @@ -221,7 +221,7 @@ private static bool CreateReturnStatementForExpression( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs b/src/Features/Core/Portable/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs index f372459b33f1b..74461187ac38f 100644 --- a/src/Features/Core/Portable/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs @@ -141,7 +141,7 @@ private async Task FixOneAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Add_member_name, createChangedDocument) + : base(FeaturesResources.Add_member_name, createChangedDocument, nameof(FeaturesResources.Add_member_name)) { } } diff --git a/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs b/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs index 3abe551e65445..38327967a7173 100644 --- a/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/AddDebuggerDisplay/AbstractAddDebuggerDisplayCodeRefactoringProvider.cs @@ -58,7 +58,6 @@ await GetRelevantTypeFromHeaderAsync(context).ConfigureAwait(false) ?? context.RegisterRefactoring(new MyCodeAction( priority, - FeaturesResources.Add_DebuggerDisplay_attribute, c => ApplyAsync(document, type, debuggerAttributeTypeSymbol, c))); } @@ -180,8 +179,8 @@ private sealed class MyCodeAction : CodeAction.DocumentChangeAction { internal override CodeActionPriority Priority { get; } - public MyCodeAction(CodeActionPriority priority, string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public MyCodeAction(CodeActionPriority priority, Func> createChangedDocument) + : base(FeaturesResources.Add_DebuggerDisplay_attribute, createChangedDocument, nameof(FeaturesResources.Add_DebuggerDisplay_attribute)) { Priority = priority; } diff --git a/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs b/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs index 80312f1d5781f..71164fe40dd5d 100644 --- a/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerCodeRefactoringProvider.cs @@ -151,7 +151,7 @@ private async Task> TryGetBannerAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(CodeFixesResources.Add_file_header, createChangedDocument) + : base(CodeFixesResources.Add_file_header, createChangedDocument, nameof(CodeFixesResources.Add_file_header)) { } } diff --git a/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs index 6666a67176e85..3454be1c6a0a1 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs @@ -50,7 +50,7 @@ protected override async Task> ComputePreviewOp { // Make a SolutionChangeAction. This way we can let it generate the diff // preview appropriately. - var solutionChangeAction = new SolutionChangeAction("", c => GetUpdatedSolutionAsync(c)); + var solutionChangeAction = new SolutionChangeAction("", c => GetUpdatedSolutionAsync(c), ""); using var _ = ArrayBuilder.GetInstance(out var result); result.AddRange(await solutionChangeAction.GetPreviewOperationsAsync(cancellationToken).ConfigureAwait(false)); diff --git a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs index 775ea5fced93f..e7cbd9250a69e 100644 --- a/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddParameter/AbstractAddParameterCodeFixProvider.cs @@ -566,7 +566,7 @@ private static bool TypeInfoMatchesType( private class MyCodeAction : CodeAction.SolutionChangeAction { public MyCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index 80ddbceb3bc97..dabab4bd296c8 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -74,6 +75,13 @@ public abstract Task ChangeSignatureAsync( /// protected abstract bool SupportsOptionalAndParamsArrayParametersSimultaneously(); + protected abstract bool TryGetRecordPrimaryConstructor(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol? primaryConstructor); + + /// + /// A temporarily hack that should be removed once/if https://github.com/dotnet/roslyn/issues/53092 is fixed. + /// + protected abstract ImmutableArray GetParameters(ISymbol declarationSymbol); + protected abstract SyntaxGenerator Generator { get; } protected abstract ISyntaxFacts SyntaxFacts { get; } @@ -124,6 +132,10 @@ internal async Task GetChangeSignatureContextAsy { symbol = typeSymbol.DelegateInvokeMethod; } + else if (TryGetRecordPrimaryConstructor(typeSymbol, out var primaryConstructor)) + { + symbol = primaryConstructor; + } } if (!symbol.MatchesKind(SymbolKind.Method, SymbolKind.Property)) @@ -164,7 +176,7 @@ internal async Task GetChangeSignatureContextAsy } var parameterConfiguration = ParameterConfiguration.Create( - symbol.GetParameters().Select(p => new ExistingParameter(p)).ToImmutableArray(), + GetParameters(symbol).Select(p => new ExistingParameter(p)).ToImmutableArray(), symbol.IsExtensionMethod(), selectedIndex); return new ChangeSignatureAnalysisSucceededContext( @@ -245,7 +257,7 @@ private static async Task> FindChangeSignatureR var symbols = await FindChangeSignatureReferencesAsync( declaredSymbol, context.Solution, cancellationToken).ConfigureAwait(false); - var declaredSymbolParametersCount = declaredSymbol.GetParameters().Length; + var declaredSymbolParametersCount = GetParameters(declaredSymbol).Length; var telemetryNumberOfDeclarationsToUpdate = 0; var telemetryNumberOfReferencesToUpdate = 0; @@ -441,14 +453,14 @@ private static bool TryGetNodeWithEditableSignatureOrAttributes(Location locatio return nodeToUpdate != null; } - protected static ImmutableArray PermuteArguments( + protected ImmutableArray PermuteArguments( ISymbol declarationSymbol, ImmutableArray arguments, SignatureChange updatedSignature, bool isReducedExtensionMethod = false) { // 1. Determine which parameters are permutable - var declarationParameters = declarationSymbol.GetParameters(); + var declarationParameters = GetParameters(declarationSymbol); var declarationParametersToPermute = GetParametersToPermute(arguments, declarationParameters, isReducedExtensionMethod); var argumentsToPermute = arguments.Take(declarationParametersToPermute.Length).ToList(); @@ -552,14 +564,14 @@ protected static ImmutableArray PermuteArguments( /// delegate Invoke methods (m) and delegate BeginInvoke methods (n = m + 2). This method adds on those extra parameters /// to the base . /// - private static SignatureChange UpdateSignatureChangeToIncludeExtraParametersFromTheDeclarationSymbol(ISymbol declarationSymbol, SignatureChange updatedSignature) + private SignatureChange UpdateSignatureChangeToIncludeExtraParametersFromTheDeclarationSymbol(ISymbol declarationSymbol, SignatureChange updatedSignature) { - if (declarationSymbol.GetParameters().Length > updatedSignature.OriginalConfiguration.ToListOfParameters().Length) + var realParameters = GetParameters(declarationSymbol); + if (realParameters.Length > updatedSignature.OriginalConfiguration.ToListOfParameters().Length) { var originalConfigurationParameters = updatedSignature.OriginalConfiguration.ToListOfParameters(); var updatedConfigurationParameters = updatedSignature.UpdatedConfiguration.ToListOfParameters(); - var realParameters = declarationSymbol.GetParameters(); var bonusParameters = realParameters.Skip(originalConfigurationParameters.Length); var originalConfigurationParametersWithExtraParameters = originalConfigurationParameters.AddRange(bonusParameters.Select(p => new ExistingParameter(p))); @@ -759,13 +771,14 @@ protected virtual async Task> AddNewArgumentsToL if (updatedParameters[i] != signaturePermutation.UpdatedConfiguration.ThisParameter || !isReducedExtensionMethod) { + var parameters = GetParameters(declarationSymbol); if (updatedParameters[i] is AddedParameter addedParameter) { // Omitting an argument only works in some languages, depending on whether // there is a params array. We sometimes need to reinterpret an requested // omitted parameter as one with a TODO requested. var forcedCallsiteErrorDueToParamsArray = addedParameter.CallSiteKind == CallSiteKind.Omitted && - declarationSymbol.GetParameters().LastOrDefault()?.IsParams == true && + parameters.LastOrDefault()?.IsParams == true && !SupportsOptionalAndParamsArrayParametersSimultaneously(); var isCallsiteActuallyOmitted = addedParameter.CallSiteKind == CallSiteKind.Omitted && !forcedCallsiteErrorDueToParamsArray; @@ -808,7 +821,6 @@ protected virtual async Task> AddNewArgumentsToL } else { - var parameters = declarationSymbol.GetParameters(); if (indexInListOfPreexistingArguments == parameters.Length - 1 && parameters[indexInListOfPreexistingArguments].IsParams) { @@ -1001,7 +1013,6 @@ protected ImmutableArray GetPermutedDocCommentTrivia(Document docu node.GetTrailingTrivia(), lastWhiteSpaceTrivia, document.Project.Solution.Options.GetOption(FormattingOptions.NewLine, document.Project.Language)); - var newTrivia = Generator.Trivia(extraDocComments); updatedLeadingTrivia.Add(newTrivia); diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractConvertToAsyncCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractConvertToAsyncCodeFixProvider.cs index 78edf4241bf94..580708ca66e13 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractConvertToAsyncCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractConvertToAsyncCodeFixProvider.cs @@ -77,7 +77,7 @@ await GetDescriptionAsync(diagnostic, node, semanticModel, cancellationToken).Co private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs index 06891f6587da5..c71cff9b1b2ca 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixService.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixService.cs @@ -615,7 +615,7 @@ await diagnosticsWithSameSpan.OrderByDescending(d => d.Severity) document: document, codeFixProvider: codeFixProvider, scope: FixAllScope.Document, - codeActionEquivalenceKey: null, + codeActionEquivalenceKey: fixes[0].Action.EquivalenceKey, diagnosticIds: diagnosticIds, fixAllDiagnosticProvider: diagnosticProvider); diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs index cfb308643e621..70021dca9b330 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs @@ -164,7 +164,8 @@ void AddCodeActionWithOptionValue(ICodeStyleOption codeStyleOption, object newVa nestedActions.Add( new SolutionChangeAction( parts.optionValue, - solution => ConfigurationUpdater.ConfigureCodeStyleOptionAsync(parts.optionName, parts.optionValue, diagnostic, isPerLanguage, project, cancellationToken))); + solution => ConfigurationUpdater.ConfigureCodeStyleOptionAsync(parts.optionName, parts.optionValue, diagnostic, isPerLanguage, project, cancellationToken), + parts.optionValue)); } } } diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs index e8853534fbce2..0ec923094f0a3 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.cs @@ -61,7 +61,10 @@ private static ImmutableArray GetConfigurations(Project project, IEnume foreach (var (name, value) in s_editorConfigSeverityStrings) { nestedActions.Add( - new SolutionChangeAction(name, solution => ConfigurationUpdater.ConfigureSeverityAsync(value, diagnostic, project, cancellationToken))); + new SolutionChangeAction( + name, + solution => ConfigurationUpdater.ConfigureSeverityAsync(value, diagnostic, project, cancellationToken), + name)); } var codeAction = new TopLevelConfigureSeverityCodeAction(diagnostic, nestedActions.ToImmutableAndFree()); @@ -103,7 +106,8 @@ void AddBulkConfigurationCodeFixes(ImmutableArray diagnostics, strin name, solution => category != null ? ConfigurationUpdater.BulkConfigureSeverityAsync(value, category, project, cancellationToken) - : ConfigurationUpdater.BulkConfigureSeverityAsync(value, project, cancellationToken))); + : ConfigurationUpdater.BulkConfigureSeverityAsync(value, project, cancellationToken), + name)); } var codeAction = new TopLevelBulkConfigureSeverityCodeAction(nestedActions.ToImmutableAndFree(), category); diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs index bef9aa674245a..7f1aa3017b8d5 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionBatchFixAllProvider.cs @@ -242,7 +242,7 @@ protected virtual Task AddProjectFixesAsync( if (newSolution != null && newSolution != solution) { var title = GetFixAllTitle(fixAllState); - return new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(newSolution)); + return new CodeAction.SolutionChangeAction(title, _ => Task.FromResult(newSolution), title); } return null; diff --git a/src/Features/Core/Portable/CodeRefactorings/AddAwait/AbstractAddAwaitCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/AddAwait/AbstractAddAwaitCodeRefactoringProvider.cs index b072c25983350..a7c7c87ae31f5 100644 --- a/src/Features/Core/Portable/CodeRefactorings/AddAwait/AbstractAddAwaitCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/AddAwait/AbstractAddAwaitCodeRefactoringProvider.cs @@ -113,7 +113,7 @@ private static Task AddAwaitAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsRefactoringProvider.cs index 4db4b844c33d1..1917ba0a01f11 100644 --- a/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsRefactoringProvider.cs @@ -58,7 +58,7 @@ private static async Task AddMissingImportsAsync(Document document, IA private class AddMissingImportsCodeAction : CodeActions.CodeAction.SolutionChangeAction { public AddMissingImportsCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs index 4e7796e465264..0c681289f4cb4 100644 --- a/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs @@ -131,7 +131,7 @@ private static async Task AddRenameAnnotationAsync(Document document, private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs index 0a5d9cc0284bf..c76493f0ee874 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractSyncNamespaceCodeRefactoringProvider.cs @@ -98,7 +98,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte private class ChangeNamespaceCodeAction : SolutionChangeAction { public ChangeNamespaceCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs index 84866db580dd2..06471715b2b8b 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractDocCommentCompletionProvider.cs @@ -97,6 +97,13 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) protected abstract IEnumerable GetExistingTopLevelAttributeValues(TSyntax syntax, string tagName, string attributeName); + protected abstract IEnumerable GetKeywordNames(); + + /// + /// A temporarily hack that should be removed once/if https://github.com/dotnet/roslyn/issues/53092 is fixed. + /// + protected abstract ImmutableArray GetParameters(ISymbol symbol); + private CompletionItem GetItem(string name) { if (s_tagMap.TryGetValue(name, out var values)) @@ -152,7 +159,7 @@ protected IEnumerable GetNestedItems(ISymbol symbol, bool includ private IEnumerable GetParamRefItems(ISymbol symbol) { - var names = symbol.GetParameters().Select(p => p.Name); + var names = GetParameters(symbol).Select(p => p.Name); return names.Select(p => CreateCompletionItem( displayText: FormatParameter(ParameterReferenceElementName, p), @@ -176,7 +183,7 @@ protected IEnumerable GetAttributeValueItems(ISymbol symbol, str { if (tagName is ParameterElementName or ParameterReferenceElementName) { - return symbol.GetParameters() + return GetParameters(symbol) .Select(parameter => CreateCompletionItem(parameter.Name)); } else if (tagName == TypeParameterElementName) @@ -202,8 +209,6 @@ protected IEnumerable GetAttributeValueItems(ISymbol symbol, str return SpecializedCollections.EmptyEnumerable(); } - protected abstract IEnumerable GetKeywordNames(); - protected ImmutableArray GetTopLevelItems(ISymbol symbol, TSyntax syntax) { using var _1 = ArrayBuilder.GetInstance(out var items); @@ -216,7 +221,7 @@ protected ImmutableArray GetTopLevelItems(ISymbol symbol, TSynta if (symbol != null) { - items.AddRange(GetParameterItems(symbol.GetParameters(), syntax, ParameterElementName)); + items.AddRange(GetParameterItems(GetParameters(symbol), syntax, ParameterElementName)); items.AddRange(GetParameterItems(symbol.GetTypeParameters(), syntax, TypeParameterElementName)); if (symbol is IPropertySymbol && !existingTopLevelTags.Contains(ValueElementName)) diff --git a/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs index 59bad4dfc00e8..25d85844d11af 100644 --- a/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAnonymousTypeToClass/AbstractConvertAnonymousTypeToClassCodeRefactoringProvider.cs @@ -423,7 +423,7 @@ private static IMethodSymbol CreateClassConstructor( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs index f0b130b1fa028..d5cc1f5d54cf0 100644 --- a/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertAutoPropertyToFullProperty/AbstractConvertAutoPropertyToFullPropertyCodeRefactoringProvider.cs @@ -55,7 +55,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( new ConvertAutoPropertyToFullPropertyCodeAction( - FeaturesResources.Convert_to_full_property, c => ExpandToFullPropertyAsync(document, property, propertySymbol, root, c)), property.Span); } @@ -129,9 +128,8 @@ private async Task ExpandToFullPropertyAsync( private class ConvertAutoPropertyToFullPropertyCodeAction : CodeAction.DocumentChangeAction { - public ConvertAutoPropertyToFullPropertyCodeAction( - string title, - Func> createChangedDocument) : base(title, createChangedDocument) + public ConvertAutoPropertyToFullPropertyCodeAction(Func> createChangedDocument) + : base(FeaturesResources.Convert_to_full_property, createChangedDocument, nameof(FeaturesResources.Convert_to_full_property)) { } } diff --git a/src/Features/Core/Portable/ConvertCast/AbstractConvertCastCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertCast/AbstractConvertCastCodeRefactoringProvider.cs index 68a29d8eba9f0..1b54800fb112a 100644 --- a/src/Features/Core/Portable/ConvertCast/AbstractConvertCastCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertCast/AbstractConvertCastCodeRefactoringProvider.cs @@ -71,7 +71,7 @@ protected async Task ConvertAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs index 1176331d907d3..1086407b4c04e 100644 --- a/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertForEachToFor/AbstractConvertForEachToForCodeRefactoringProvider.cs @@ -468,7 +468,7 @@ private class ForEachToForCodeAction : CodeAction.DocumentChangeAction { public ForEachToForCodeAction( string title, - Func> createChangedDocument) : base(title, createChangedDocument) + Func> createChangedDocument) : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/ConvertLinq/AbstractConvertLinqQueryToForEachProvider.cs b/src/Features/Core/Portable/ConvertLinq/AbstractConvertLinqQueryToForEachProvider.cs index c62dd394b592a..65e19fa3c2312 100644 --- a/src/Features/Core/Portable/ConvertLinq/AbstractConvertLinqQueryToForEachProvider.cs +++ b/src/Features/Core/Portable/ConvertLinq/AbstractConvertLinqQueryToForEachProvider.cs @@ -92,7 +92,7 @@ public SyntaxNode UpdateRoot(SyntaxNode root) protected sealed class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConvertForEachToLinqQueryProvider.cs b/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConvertForEachToLinqQueryProvider.cs index 2f65ad3522780..bd8c418eac6f0 100644 --- a/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConvertForEachToLinqQueryProvider.cs +++ b/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractConvertForEachToLinqQueryProvider.cs @@ -187,7 +187,7 @@ private bool TryBuildConverter( private class ForEachToLinqQueryCodeAction : CodeAction.DocumentChangeAction { public ForEachToLinqQueryCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs index d69aa81454d41..e23aaa5ef077e 100644 --- a/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertNumericLiteral/AbstractConvertNumericLiteralCodeRefactoringProvider.cs @@ -171,7 +171,7 @@ private enum NumericKind { Unknown, Decimal, Binary, Hexadecimal } private sealed class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) : base(title, createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs index d1d055584013b..150926ef90023 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs @@ -256,7 +256,7 @@ private static bool IsStringConcat( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Convert_to_interpolated_string, createChangedDocument) + : base(FeaturesResources.Convert_to_interpolated_string, createChangedDocument, nameof(FeaturesResources.Convert_to_interpolated_string)) { } } diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs index 83ae44cfc373d..0b9023f1750a1 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/AbstractConvertPlaceholderToInterpolatedStringRefactoringProvider.cs @@ -63,7 +63,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte { context.RegisterRefactoring( new ConvertToInterpolatedStringCodeAction( - FeaturesResources.Convert_to_interpolated_string, c => CreateInterpolatedStringAsync(invocation, document, syntaxFactsService, c)), invocation.Span); } @@ -297,8 +296,8 @@ private static bool IsArgumentListNotPassingArrayToParams( private class ConvertToInterpolatedStringCodeAction : CodeAction.DocumentChangeAction { - public ConvertToInterpolatedStringCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public ConvertToInterpolatedStringCodeAction(Func> createChangedDocument) + : base(FeaturesResources.Convert_to_interpolated_string, createChangedDocument, nameof(FeaturesResources.Convert_to_interpolated_string)) { } } diff --git a/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs b/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs index 95e94fc74f7b6..771efdf94562a 100644 --- a/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertToInterpolatedString/ConvertRegularStringToInterpolatedStringRefactoringProvider.cs @@ -112,7 +112,7 @@ private class MyCodeAction : CodeAction.DocumentChangeAction internal override CodeActionPriority Priority => CodeActionPriority.Low; public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Convert_to_interpolated_string, createChangedDocument) + : base(FeaturesResources.Convert_to_interpolated_string, createChangedDocument, nameof(FeaturesResources.Convert_to_interpolated_string)) { } } diff --git a/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractAddDocCommentNodesCodeFixProvider.cs b/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractAddDocCommentNodesCodeFixProvider.cs index f8c0abb338db5..856b2964af1ae 100644 --- a/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractAddDocCommentNodesCodeFixProvider.cs +++ b/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractAddDocCommentNodesCodeFixProvider.cs @@ -178,7 +178,7 @@ protected TXmlElementSyntax GetParamNodeForParamName( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Add_missing_param_nodes, createChangedDocument) + : base(FeaturesResources.Add_missing_param_nodes, createChangedDocument, nameof(FeaturesResources.Add_missing_param_nodes)) { } } diff --git a/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractRemoveDocCommentNodeCodeFixProvider.cs b/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractRemoveDocCommentNodeCodeFixProvider.cs index b544edd6a6e85..a399ddc35d411 100644 --- a/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractRemoveDocCommentNodeCodeFixProvider.cs +++ b/src/Features/Core/Portable/DocumentationComments/CodeFixes/AbstractRemoveDocCommentNodeCodeFixProvider.cs @@ -126,7 +126,7 @@ private bool ShouldRemovePreviousSibling(List paramNodeSiblings, int private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Remove_tag, createChangedDocument) + : base(FeaturesResources.Remove_tag, createChangedDocument, nameof(FeaturesResources.Remove_tag)) { } } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 2e20695da1d9a..8fe1d00a96ea2 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -44,7 +44,8 @@ internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyz parameterOptions: SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeExtensionThis | - SymbolDisplayParameterOptions.IncludeType, + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); @@ -260,11 +261,13 @@ protected virtual bool StateMachineSuspensionPointKindEquals(SyntaxNode suspensi /// protected abstract bool AreEquivalentActiveStatements(SyntaxNode oldStatement, SyntaxNode newStatement, int statementPart); + protected abstract TextSpan GetGlobalStatementDiagnosticSpan(SyntaxNode node); + /// - /// Returns all symbols associated with an edit. + /// Returns all symbols associated with an edit and an actual edit kind, which may be different then the specified one. /// Returns an empty set if the edit is not associated with any symbols. /// - protected abstract OneOrMany<(ISymbol? oldSymbol, ISymbol? newSymbol)> GetSymbolsForEdit( + protected abstract OneOrMany<(ISymbol? oldSymbol, ISymbol? newSymbol, EditKind editKind)> GetSymbolEdits( EditKind editKind, SyntaxNode? oldNode, SyntaxNode? newNode, @@ -316,6 +319,47 @@ protected virtual TextSpan GetBodyDiagnosticSpan(SyntaxNode node, EditKind editK internal string GetDisplayName(SyntaxNode node, EditKind editKind = EditKind.Update) => TryGetDisplayName(node, editKind) ?? throw ExceptionUtilities.UnexpectedValue(node.GetType().Name); + internal string GetDisplayName(ISymbol symbol) + => symbol.Kind switch + { + SymbolKind.Event => FeaturesResources.event_, + SymbolKind.Field => GetDisplayName((IFieldSymbol)symbol), + SymbolKind.Method => GetDisplayName((IMethodSymbol)symbol), + SymbolKind.NamedType => GetDisplayName((INamedTypeSymbol)symbol), + SymbolKind.Parameter => FeaturesResources.parameter, + SymbolKind.Property => GetDisplayName((IPropertySymbol)symbol), + SymbolKind.TypeParameter => FeaturesResources.type_parameter, + _ => throw ExceptionUtilities.UnexpectedValue(symbol.Kind) + }; + + internal virtual string GetDisplayName(IPropertySymbol symbol) + => FeaturesResources.property_; + + internal virtual string GetDisplayName(INamedTypeSymbol symbol) + => symbol.TypeKind switch + { + TypeKind.Class => FeaturesResources.class_, + TypeKind.Interface => FeaturesResources.interface_, + TypeKind.Delegate => FeaturesResources.delegate_, + TypeKind.Enum => FeaturesResources.enum_, + TypeKind.TypeParameter => FeaturesResources.type_parameter, + _ => FeaturesResources.type, + }; + + internal virtual string GetDisplayName(IFieldSymbol symbol) + => symbol.IsConst ? ((symbol.ContainingType.TypeKind == TypeKind.Enum) ? FeaturesResources.enum_value : FeaturesResources.const_field) : + FeaturesResources.field; + + internal virtual string GetDisplayName(IMethodSymbol symbol) + => symbol.MethodKind switch + { + MethodKind.Constructor => FeaturesResources.constructor, + MethodKind.PropertyGet or MethodKind.PropertySet => FeaturesResources.property_accessor, + MethodKind.EventAdd or MethodKind.EventRaise or MethodKind.EventRemove => FeaturesResources.event_accessor, + MethodKind.BuiltinOperator or MethodKind.UserDefinedOperator or MethodKind.Conversion => FeaturesResources.operator_, + _ => FeaturesResources.method, + }; + /// /// Returns the display name of an ancestor node that contains the specified node and has a display name. /// @@ -359,7 +403,6 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind internal abstract void ReportMemberUpdateRudeEdits(ArrayBuilder diagnostics, SyntaxNode newMember, TextSpan? span); internal abstract void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder diagnostics, ISymbol newSymbol, SyntaxNode newNode, bool insertingIntoExistingContainingType); internal abstract void ReportStateMachineSuspensionPointRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode); - internal abstract void ReportTypeDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, INamedTypeSymbol oldSymbol, INamedTypeSymbol newSymbol, SyntaxNode newDeclaration, CancellationToken cancellationToken); internal abstract bool IsLambda(SyntaxNode node); internal abstract bool IsInterfaceDeclaration(SyntaxNode node); @@ -404,10 +447,12 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind internal abstract SyntaxNode? TryGetContainingTypeDeclaration(SyntaxNode node); /// - /// Returns a property, indexer or event declaration whose accessor is the specified , - /// or null if is not an accessor. + /// Returns the declaration of + /// - a property, indexer or event declaration whose accessor is the specified , + /// - a method, an indexer or a type (delegate) if the is a parameter, + /// - a method or an type if the is a type parameter. /// - internal abstract SyntaxNode? TryGetAssociatedMemberDeclaration(SyntaxNode node); + internal abstract bool TryGetAssociatedMemberDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? declaration); internal abstract bool HasBackingField(SyntaxNode propertyDeclaration); @@ -658,8 +703,35 @@ private void ReportTopLevelSyntacticRudeEdits(ArrayBuilder d /// or . /// The scenarios include moving a type declaration from one file to another and moving a member of a partial type from one partial declaration to another. /// - internal virtual void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode, ISymbol oldSymbol, ISymbol newSymbol) + internal virtual void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode, ISymbol oldSymbol, ISymbol newSymbol, CancellationToken cancellationToken) { + if (oldSymbol is ITypeParameterSymbol or IParameterSymbol) + { + return; + } + + // When a method is moved to a different declaration and its parameters are changed at the same time + // the new method symbol key will not resolve to the old one since the parameters are different. + // As a result we will report separate delete and insert rude edits. + // + // For delegates, however, the symbol key will resolve to the old type so we need to report + // rude edits here. + if (oldSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null and var oldDelegateInvoke } && + newSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null and var newDelegateInvoke }) + { + if (!ParametersEquivalent(oldDelegateInvoke.Parameters, newDelegateInvoke.Parameters, exact: false)) + { + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingParameterTypes, newSymbol, newNode, cancellationToken); + } + + return; + } + + if (oldSymbol is INamedTypeSymbol) + { + return; + } + // Consider replacing following syntax analysis with semantic analysis of the corresponding symbols, // or a combination of semantic and syntax analysis (e.g. primarily analyze symbols but fall back // to syntax analysis for comparisons of attribute values, optional parameter values, etc.). @@ -669,7 +741,6 @@ internal virtual void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder obj?.Identity.GetHashCode() ?? 0; } - protected static readonly SymbolEquivalenceComparer s_assemblyEqualityComparer = new( - AssemblyEqualityComparer.Instance, distinguishRefFromOut: true, tupleNamesMustMatch: false); + // Ignore tuple element changes, nullability and dynamic. These type changes do not affect runtime type. + // They only affect custom attributes emitted on the members - all runtimes are expected to accept + // custom attribute updates in metadata deltas, even if they do not have any observable effect. + private static readonly SymbolEquivalenceComparer s_runtimeSymbolEqualityComparer = new( + AssemblyEqualityComparer.Instance, distinguishRefFromOut: true, tupleNamesMustMatch: false, ignoreNullableAnnotations: true); + + private static readonly SymbolEquivalenceComparer s_exactSymbolEqualityComparer = new( + AssemblyEqualityComparer.Instance, distinguishRefFromOut: true, tupleNamesMustMatch: true, ignoreNullableAnnotations: false); protected static bool SignaturesEquivalent(ImmutableArray oldParameters, ITypeSymbol oldReturnType, ImmutableArray newParameters, ITypeSymbol newReturnType) - { - return oldParameters.SequenceEqual(newParameters, s_assemblyEqualityComparer.ParameterEquivalenceComparer) && - s_assemblyEqualityComparer.Equals(oldReturnType, newReturnType); - } + => ParametersEquivalent(oldParameters, newParameters, exact: false) && + s_runtimeSymbolEqualityComparer.Equals(oldReturnType, newReturnType); // TODO: should check ref, ref readonly, custom mods + + protected static bool ParametersEquivalent(ImmutableArray oldParameters, ImmutableArray newParameters, bool exact) + => oldParameters.SequenceEqual(newParameters, exact, (oldParameter, newParameter, exact) => ParameterTypesEquivalent(oldParameter, newParameter, exact)); + + protected static bool CustomModifiersEquivalent(CustomModifier oldModifier, CustomModifier newModifier, bool exact) + => oldModifier.IsOptional == newModifier.IsOptional && + TypesEquivalent(oldModifier.Modifier, newModifier.Modifier, exact); + + protected static bool ReturnTypesEquivalent(IMethodSymbol oldMethod, IMethodSymbol newMethod, bool exact) + => oldMethod.ReturnsByRef == newMethod.ReturnsByRef && + oldMethod.ReturnsByRefReadonly == newMethod.ReturnsByRefReadonly && + oldMethod.ReturnTypeCustomModifiers.SequenceEqual(newMethod.ReturnTypeCustomModifiers, exact, (x, y, exact) => CustomModifiersEquivalent(x, y, exact)) && + oldMethod.RefCustomModifiers.SequenceEqual(newMethod.RefCustomModifiers, exact, (x, y, exact) => CustomModifiersEquivalent(x, y, exact)) && + TypesEquivalent(oldMethod.ReturnType, newMethod.ReturnType, exact); + + protected static bool ReturnTypesEquivalent(IPropertySymbol oldProperty, IPropertySymbol newProperty, bool exact) + => oldProperty.ReturnsByRef == newProperty.ReturnsByRef && + oldProperty.ReturnsByRefReadonly == newProperty.ReturnsByRefReadonly && + oldProperty.TypeCustomModifiers.SequenceEqual(newProperty.TypeCustomModifiers, exact, (x, y, exact) => CustomModifiersEquivalent(x, y, exact)) && + oldProperty.RefCustomModifiers.SequenceEqual(newProperty.RefCustomModifiers, exact, (x, y, exact) => CustomModifiersEquivalent(x, y, exact)) && + TypesEquivalent(oldProperty.Type, newProperty.Type, exact); + + protected static bool ReturnTypesEquivalent(IEventSymbol oldEvent, IEventSymbol newEvent, bool exact) + => TypesEquivalent(oldEvent.Type, newEvent.Type, exact); + + // Note: SignatureTypeEquivalenceComparer compares dynamic and object the same. + protected static bool TypesEquivalent(ITypeSymbol? oldType, ITypeSymbol? newType, bool exact) + => (exact ? s_exactSymbolEqualityComparer : (IEqualityComparer)s_runtimeSymbolEqualityComparer.SignatureTypeEquivalenceComparer).Equals(oldType, newType); + + protected static bool ParameterTypesEquivalent(IParameterSymbol oldParameter, IParameterSymbol newParameter, bool exact) + => (exact ? s_exactSymbolEqualityComparer : s_runtimeSymbolEqualityComparer).ParameterEquivalenceComparer.Equals(oldParameter, newParameter); + + protected static bool TypeParameterConstraintsEquivalent(ITypeParameterSymbol oldParameter, ITypeParameterSymbol newParameter, bool exact) + => oldParameter.ConstraintTypes.SequenceEqual(newParameter.ConstraintTypes, exact, (x, y, exact) => TypesEquivalent(x, y, exact)) && + oldParameter.HasReferenceTypeConstraint == newParameter.HasReferenceTypeConstraint && + oldParameter.HasValueTypeConstraint == newParameter.HasValueTypeConstraint && + oldParameter.HasConstructorConstraint == newParameter.HasConstructorConstraint && + oldParameter.HasNotNullConstraint == newParameter.HasNotNullConstraint && + oldParameter.HasUnmanagedTypeConstraint == newParameter.HasUnmanagedTypeConstraint && + oldParameter.Variance == newParameter.Variance; + + protected static bool TypeParametersEquivalent(ImmutableArray oldParameters, ImmutableArray newParameters, bool exact) + => oldParameters.SequenceEqual(newParameters, exact, (oldParameter, newParameter, exact) => oldParameter.Name == newParameter.Name && TypeParameterConstraintsEquivalent(oldParameter, newParameter, exact)); + + protected static bool BaseTypesEquivalent(INamedTypeSymbol oldType, INamedTypeSymbol newType, bool exact) + => TypesEquivalent(oldType.BaseType, newType.BaseType, exact) && + oldType.AllInterfaces.SequenceEqual(newType.AllInterfaces, exact, (x, y, exact) => TypesEquivalent(x, y, exact)); protected static bool MemberSignaturesEquivalent( ISymbol? oldMember, @@ -2405,31 +2527,54 @@ private async Task> AnalyzeSemanticsAsync( continue; } - foreach (var symbols in GetSymbolsForEdit(edit.Kind, edit.OldNode, edit.NewNode, oldModel, newModel, editMap, cancellationToken)) + foreach (var symbolEdits in GetSymbolEdits(edit.Kind, edit.OldNode, edit.NewNode, oldModel, newModel, editMap, cancellationToken)) { - SymbolKey? lazySymbolKey = null; Func? syntaxMap; SemanticEditKind editKind; - var (oldSymbol, newSymbol) = symbols; + var (oldSymbol, newSymbol, syntacticEditKind) = symbolEdits; + var symbol = newSymbol ?? oldSymbol; + Contract.ThrowIfNull(symbol); + + if (!processedSymbols.Add(symbol)) + { + continue; + } + + var symbolKey = SymbolKey.Create(symbol, cancellationToken); + + // Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation. + oldSymbol ??= symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + newSymbol ??= symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + var (oldDeclaration, newDeclaration) = GetSymbolDeclarationNodes(oldSymbol, newSymbol, edit.OldNode, edit.NewNode); - switch (edit.Kind) + // The syntax change implies an update of the associated symbol but the old/new symbol does not actually exist. + // Treat the edit as Insert/Delete. This may happen e.g. when all C# global statements are removed, the first one is added or they are moved to another file. + if (syntacticEditKind == EditKind.Update) + { + if (oldSymbol == null) + { + syntacticEditKind = EditKind.Insert; + } + else if (newSymbol == null) + { + syntacticEditKind = EditKind.Delete; + } + else if (oldDeclaration != null && oldModel != null && oldDeclaration.SyntaxTree != oldModel.SyntaxTree || + newDeclaration != null && newDeclaration.SyntaxTree != newModel.SyntaxTree) + { + syntacticEditKind = edit.Kind; + } + } + + switch (syntacticEditKind) { case EditKind.Delete: { Contract.ThrowIfNull(oldModel); Contract.ThrowIfNull(oldSymbol); Contract.ThrowIfNull(oldDeclaration); - Contract.ThrowIfFalse(newSymbol == null); - Contract.ThrowIfFalse(newDeclaration == null); - - if (!processedSymbols.Add(oldSymbol)) - { - // Node doesn't represent a symbol or it represents multiple symbols and the semantic delete - // will be issued for node that represents the specific symbol. - continue; - } var activeStatementIndices = GetOverlappingActiveStatements(oldDeclaration, oldActiveStatements); var hasActiveStatement = activeStatementIndices.Any(); @@ -2455,11 +2600,6 @@ private async Task> AnalyzeSemanticsAsync( editKind = SemanticEditKind.Delete; // Check if the declaration has been moved from one document to another. - var symbolKey = SymbolKey.Create(oldSymbol, cancellationToken); - lazySymbolKey = symbolKey; - - // Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation. - newSymbol = symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; if (newSymbol != null && !(newSymbol is IMethodSymbol newMethod && newMethod.IsPartialDefinition)) { // Symbol has actually not been deleted but rather moved to another document, another partial type declaration @@ -2474,7 +2614,7 @@ private async Task> AnalyzeSemanticsAsync( // 3) The debugger's ManagedActiveStatementUpdate might need another field indicating the source file path. if (hasActiveStatement) { - ReportDeletedMemberRudeEdit(diagnostics, editScript, oldDeclaration, oldSymbol, RudeEditKind.DeleteActiveStatement); + ReportDeletedMemberRudeEdit(diagnostics, oldSymbol, newCompilation, RudeEditKind.DeleteActiveStatement, cancellationToken); continue; } @@ -2502,18 +2642,10 @@ private async Task> AnalyzeSemanticsAsync( // We only need to do this once though. if (isFirst) { - AddEditsForSynthesizedRecordMembers(newCompilation, newSymbol.ContainingType, semanticEdits); + AddEditsForSynthesizedRecordMembers(newCompilation, newSymbol.ContainingType, semanticEdits, cancellationToken); } } - // The new symbol is implicitly declared and thus has implied accessibility that needs to be the same - // as the accessibility of the deleted explicit symbol. - if (newSymbol.DeclaredAccessibility != oldSymbol.DeclaredAccessibility) - { - ReportDeletedMemberRudeEdit(diagnostics, editScript, oldDeclaration, oldSymbol, RudeEditKind.ChangingAccessibility); - continue; - } - // If a constructor is deleted and replaced by an implicit one the update needs to aggregate updates to all data member initializers, // or if a property is deleted that is part of a records primary constructor, which is effectivelly moving from an explicit to implicit // initializer. @@ -2529,12 +2661,33 @@ private async Task> AnalyzeSemanticsAsync( } else { - // Check if the symbol being deleted is a member of a type or associated with a property or event that's also being deleted. - // If so, skip the member deletion and only report the containing symbol deletion. - var oldContainingSymbol = (oldSymbol as IMethodSymbol)?.AssociatedSymbol ?? oldSymbol.ContainingType; - if (oldContainingSymbol != null) + var diagnosticSpan = GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, oldDeclaration); + + // If we got here for a global statement then the actual edit is a delete of the synthesized Main method + if (IsGlobalMain(oldSymbol)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.Delete, diagnosticSpan, edit.OldNode, new[] { GetDisplayName(edit.OldNode, EditKind.Delete) })); + continue; + } + + // If the associated member declaration (accessor -> property/indexer/event, parameter -> method) has also been deleted skip + // the delete of the symbol as it will be deleted by the delete of the associated member. + // + // Associated member declarations must be in the same document as the symbol, so we don't need to resolve their symbol. + // In some cases the symbol even can't be resolved unambiguously. Consider e.g. resolving a method with its parameter deleted - + // we wouldn't know which overload to resolve to. + if (TryGetAssociatedMemberDeclaration(oldDeclaration, out var oldAssociatedMemberDeclaration)) + { + if (HasEdit(editMap, oldAssociatedMemberDeclaration, EditKind.Delete)) + { + continue; + } + } + else if (oldSymbol.ContainingType != null) { - var containingSymbolKey = SymbolKey.Create(oldContainingSymbol, cancellationToken); + // Check if the symbol being deleted is a member of a type that's also being deleted. + // If so, skip the member deletion and only report the containing symbol deletion. + var containingSymbolKey = SymbolKey.Create(oldSymbol.ContainingType, cancellationToken); var newContatiningSymbol = containingSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; if (newContatiningSymbol == null) { @@ -2543,7 +2696,6 @@ private async Task> AnalyzeSemanticsAsync( } // deleting symbol is not allowed - var diagnosticSpan = GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, oldDeclaration); diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.Delete, @@ -2551,9 +2703,9 @@ private async Task> AnalyzeSemanticsAsync( oldDeclaration, new[] { - string.Format(FeaturesResources.member_kind_and_name, - GetDisplayName(oldDeclaration, EditKind.Delete), - oldSymbol.ToDisplayString(diagnosticSpan.IsEmpty ? s_fullyQualifiedMemberDisplayFormat : s_unqualifiedMemberDisplayFormat)) + string.Format(FeaturesResources.member_kind_and_name, + GetDisplayName(oldDeclaration, EditKind.Delete), + oldSymbol.ToDisplayString(diagnosticSpan.IsEmpty ? s_fullyQualifiedMemberDisplayFormat : s_unqualifiedMemberDisplayFormat)) })); continue; @@ -2565,29 +2717,16 @@ private async Task> AnalyzeSemanticsAsync( case EditKind.Insert: { Contract.ThrowIfNull(newModel); - Contract.ThrowIfFalse(oldSymbol == null); - Contract.ThrowIfFalse(oldDeclaration == null); Contract.ThrowIfNull(newSymbol); Contract.ThrowIfNull(newDeclaration); syntaxMap = null; - if (!processedSymbols.Add(newSymbol)) - { - // Node doesn't represent a symbol or it represents multiple symbols and the semantic insert - // will be issued for node that represents the specific symbol. - continue; - } editKind = SemanticEditKind.Insert; INamedTypeSymbol? oldContainingType; var newContainingType = newSymbol.ContainingType; // Check if the declaration has been moved from one document to another. - var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); - lazySymbolKey = symbolKey; - - // Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation. - oldSymbol = symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; if (oldSymbol != null) { // Symbol has actually not been inserted but rather moved between documents or partial type declarations, @@ -2599,12 +2738,10 @@ private async Task> AnalyzeSemanticsAsync( // If a user explicitly implements a member of a record then we want to issue an update, not an insert. if (oldSymbol.DeclaringSyntaxReferences.Length == 1) { - Contract.ThrowIfFalse(oldDeclaration == null); - oldDeclaration = GetSymbolDeclarationSyntax(oldSymbol.DeclaringSyntaxReferences[0], cancellationToken); - - ReportDeclarationInsertDeleteRudeEdits(diagnostics, oldDeclaration, newDeclaration, oldSymbol, newSymbol); + Contract.ThrowIfNull(oldDeclaration); + ReportDeclarationInsertDeleteRudeEdits(diagnostics, oldDeclaration, newDeclaration, oldSymbol, newSymbol, cancellationToken); - if (IsPropertyAccessorDeclarationMatchingPrimaryConstructorParameter(newDeclaration, newSymbol.ContainingType, out var isFirst)) + if (IsPropertyAccessorDeclarationMatchingPrimaryConstructorParameter(newDeclaration, newContainingType, out var isFirst)) { // If there is no body declared we can skip it entirely because for a property accessor // it matches what the compiler would have previously implicitly implemented. @@ -2617,33 +2754,17 @@ private async Task> AnalyzeSemanticsAsync( // need to issue edits for the synthezied members. Only need to do it once. if (isFirst) { - AddEditsForSynthesizedRecordMembers(newCompilation, newSymbol.ContainingType, semanticEdits); + AddEditsForSynthesizedRecordMembers(newCompilation, newContainingType, semanticEdits, cancellationToken); } } editKind = SemanticEditKind.Update; } } - else if (newSymbol is IFieldSymbol { ContainingType: { TypeKind: TypeKind.Enum } }) - { - // Skip enum field declarations. Enums can't be partial their fields must be inserted at the same time as the enum itself. - continue; - } - else if (newSymbol is INamedTypeSymbol { TypeKind: not (TypeKind.Delegate or TypeKind.Enum) } newTypeSymbol) - { - // The old symbol must be named type as well since we resolved it via symbol key above. - var oldTypeSymbol = (INamedTypeSymbol)oldSymbol; - - // The types have multiple partial declaration parts, each can contribute attributes and base types. - // All have to declare the same type parameters, but each can add different attributes to them. - // Only one can contribute generic type parameter constraints. - // We collect all these entities and require them to be unchanged. - ReportTypeDeclarationInsertDeleteRudeEdits(diagnostics, oldTypeSymbol, newTypeSymbol, newDeclaration, cancellationToken); - - continue; - } else if (oldSymbol.DeclaringSyntaxReferences.Length == 1 && newSymbol.DeclaringSyntaxReferences.Length == 1) { + Contract.ThrowIfNull(oldDeclaration); + // Handles partial methods and explicitly implemented properties that implement positional parameters of records // We ignore partial method definition parts when processing edits (GetSymbolForEdit). @@ -2651,12 +2772,9 @@ private async Task> AnalyzeSemanticsAsync( // We can therefore ignore any symbols that have more than one declaration. ReportTypeLayoutUpdateRudeEdits(diagnostics, newSymbol, newDeclaration, newModel, ref lazyLayoutAttribute); - Contract.ThrowIfFalse(oldDeclaration == null); - oldDeclaration = GetSymbolDeclarationSyntax(oldSymbol.DeclaringSyntaxReferences[0], cancellationToken); - // Compare the old declaration syntax of the symbol with its new declaration and report rude edits // if it changed in any way that's not allowed. - ReportDeclarationInsertDeleteRudeEdits(diagnostics, oldDeclaration, newDeclaration, oldSymbol, newSymbol); + ReportDeclarationInsertDeleteRudeEdits(diagnostics, oldDeclaration, newDeclaration, oldSymbol, newSymbol, cancellationToken); var oldBody = TryGetDeclarationBody(oldDeclaration); if (oldBody != null) @@ -2704,10 +2822,10 @@ private async Task> AnalyzeSemanticsAsync( if (isDeclarationWithInitializer) { - AnalyzeSymbolUpdate(oldSymbol, newSymbol, newDeclaration, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); } - DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + DeferConstructorEdit(oldSymbol.ContainingType, newContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); // Don't add a separate semantic edit. // Updates of data members with initializers and constructors that emit initializers will be aggregated and added later. @@ -2716,18 +2834,31 @@ private async Task> AnalyzeSemanticsAsync( editKind = SemanticEditKind.Update; } + else + { + editKind = SemanticEditKind.Update; + } } - else if (newSymbol.ContainingType != null) + else if (TryGetAssociatedMemberDeclaration(newDeclaration, out var newAssociatedMemberDeclaration) && + HasEdit(editMap, newAssociatedMemberDeclaration, EditKind.Insert)) { - // The edit actually adds a new symbol into an existing or a new type. - // If the symbol is an accessor and the containing property/indexer/event declaration has also been inserted skip // the insert of the accessor as it will be inserted by the property/indexer/event. - var newAssociatedMemberDeclaration = TryGetAssociatedMemberDeclaration(newDeclaration); - if (newAssociatedMemberDeclaration != null && HasEdit(editMap, newAssociatedMemberDeclaration, EditKind.Insert)) - { - continue; - } + continue; + } + else if (newSymbol is IParameterSymbol || newSymbol is ITypeParameterSymbol) + { + diagnostics.Add(new RudeEditDiagnostic( + RudeEditKind.Insert, + GetDiagnosticSpan(newDeclaration, EditKind.Insert), + newDeclaration, + arguments: new[] { GetDisplayName(newDeclaration, EditKind.Insert) })); + + continue; + } + else if (newContainingType != null && !IsGlobalMain(newSymbol)) + { + // The edit actually adds a new symbol into an existing or a new type. var containingSymbolKey = SymbolKey.Create(newContainingType, cancellationToken); oldContainingType = containingSymbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol as INamedTypeSymbol; @@ -2758,17 +2889,18 @@ private async Task> AnalyzeSemanticsAsync( // If a property or field is added to a record then the implicit constructors change, // and we need to mark a number of other synthesized members as having changed. - if (newSymbol is IPropertySymbol or IFieldSymbol && newSymbol.ContainingType.IsRecord) + if (newSymbol is IPropertySymbol or IFieldSymbol && newContainingType.IsRecord) { - DeferConstructorEdit(oldContainingType, newSymbol.ContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + DeferConstructorEdit(oldContainingType, newContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); - AddEditsForSynthesizedRecordMembers(newCompilation, newContainingType, semanticEdits); + AddEditsForSynthesizedRecordMembers(newCompilation, newContainingType, semanticEdits, cancellationToken); } } else { - // adds a new top-level type - Contract.ThrowIfFalse(newSymbol is INamedTypeSymbol); + // adds a new top-level type, or a global statement where none existed before, which is + // therefore inserting the $ type + Contract.ThrowIfFalse(newSymbol is INamedTypeSymbol || IsGlobalMain(newSymbol)); if (!capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) { @@ -2823,28 +2955,22 @@ private async Task> AnalyzeSemanticsAsync( case EditKind.Update: { - if (oldSymbol == null) - { - // May happen when the old node represents partial method changed from a definition to an implementation (adding a body). - // This is already reported as rude edit. - continue; - } - Contract.ThrowIfNull(oldModel); Contract.ThrowIfNull(newModel); Contract.ThrowIfNull(oldSymbol); Contract.ThrowIfNull(newSymbol); - Contract.ThrowIfNull(oldDeclaration); - Contract.ThrowIfNull(newDeclaration); - if (!processedSymbols.Add(newSymbol)) + editKind = SemanticEditKind.Update; + syntaxMap = null; + + // Partial type declarations and their type parameters. + if (oldSymbol.DeclaringSyntaxReferences.Length != 1 && newSymbol.DeclaringSyntaxReferences.Length != 1) { - // node doesn't represent a symbol or the symbol has already been processed - continue; + break; } - editKind = SemanticEditKind.Update; - syntaxMap = null; + Contract.ThrowIfNull(oldDeclaration); + Contract.ThrowIfNull(newDeclaration); var oldBody = TryGetDeclarationBody(oldDeclaration); if (oldBody != null) @@ -2885,7 +3011,7 @@ private async Task> AnalyzeSemanticsAsync( if (isDeclarationWithInitializer) { - AnalyzeSymbolUpdate(oldSymbol, newSymbol, newDeclaration, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); } DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); @@ -2908,7 +3034,7 @@ private async Task> AnalyzeSemanticsAsync( { Contract.ThrowIfNull(oldSymbol); - AnalyzeSymbolUpdate(oldSymbol, newSymbol, newDeclaration, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); // The only update to the type itself that's supported is an addition or removal of the partial modifier, // which does not have impact on the emitted type metadata. @@ -2932,11 +3058,9 @@ private async Task> AnalyzeSemanticsAsync( } } - lazySymbolKey ??= SymbolKey.Create(newSymbol, cancellationToken); - // Edits in data member initializers and constructors are deferred, edits of other members (even on partial types) // do not need merging accross partial type declarations. - semanticEdits.Add(new SemanticEditInfo(editKind, lazySymbolKey.Value, syntaxMap, syntaxMapTree: null, partialType: null)); + semanticEdits.Add(new SemanticEditInfo(editKind, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); } } @@ -2945,7 +3069,7 @@ private async Task> AnalyzeSemanticsAsync( Contract.ThrowIfNull(oldModel); Contract.ThrowIfNull(newModel); - foreach (var (oldSymbol, newSymbol) in GetSymbolsForEdit(EditKind.Update, oldEditNode, newEditNode, oldModel, newModel, editMap, cancellationToken)) + foreach (var (oldSymbol, newSymbol, editKind) in GetSymbolEdits(EditKind.Update, oldEditNode, newEditNode, oldModel, newModel, editMap, cancellationToken)) { // Trivia edits are only calculated for member bodies and each member has a symbol. Contract.ThrowIfNull(newSymbol); @@ -3010,6 +3134,7 @@ private async Task> AnalyzeSemanticsAsync( editScript.Match, oldModel, oldCompilation, + newCompilation, processedSymbols, capabilities, isStatic: false, @@ -3025,6 +3150,7 @@ private async Task> AnalyzeSemanticsAsync( editScript.Match, oldModel, oldCompilation, + newCompilation, processedSymbols, capabilities, isStatic: true, @@ -3039,7 +3165,7 @@ private async Task> AnalyzeSemanticsAsync( staticConstructorEdits?.Free(); } - return semanticEdits.ToImmutable(); + return semanticEdits.Distinct(SemanticEditInfoComparer.Instance).ToImmutableArray(); // If the symbol has a single declaring reference use its syntax node for further analysis. // Some syntax edits may not be directly associated with the declarations. @@ -3055,10 +3181,64 @@ private async Task> AnalyzeSemanticsAsync( } } - private void ReportUpdatedSymbolDeclarationRudeEdits(ArrayBuilder diagnostics, ISymbol oldSymbol, ISymbol newSymbol, SyntaxNode newDeclaration) + private sealed class SemanticEditInfoComparer : IEqualityComparer + { + public static SemanticEditInfoComparer Instance = new(); + + private static readonly IEqualityComparer s_symbolKeyComparer = SymbolKey.GetComparer(); + + public bool Equals([AllowNull] SemanticEditInfo x, [AllowNull] SemanticEditInfo y) + => s_symbolKeyComparer.Equals(x.Symbol, y.Symbol); + + public int GetHashCode([DisallowNull] SemanticEditInfo obj) + => obj.Symbol.GetHashCode(); + } + + private void ReportUpdatedSymbolDeclarationRudeEdits( + ArrayBuilder diagnostics, + ISymbol oldSymbol, + ISymbol newSymbol, + SyntaxNode? newNode, + Compilation newCompilation, + out bool hasGeneratedAttributeChange, + out bool hasGeneratedReturnTypeAttributeChange, + CancellationToken cancellationToken) { var rudeEdit = RudeEditKind.None; + hasGeneratedAttributeChange = false; + hasGeneratedReturnTypeAttributeChange = false; + + if (oldSymbol.Kind != newSymbol.Kind) + { + rudeEdit = (oldSymbol.Kind == SymbolKind.Field || newSymbol.Kind == SymbolKind.Field) ? RudeEditKind.FieldKindUpdate : RudeEditKind.Update; + } + else if (oldSymbol.Name != newSymbol.Name) + { + rudeEdit = RudeEditKind.Renamed; + + // specialize rude edit for accessors and conversion operators: + if (oldSymbol is IMethodSymbol oldMethod && newSymbol is IMethodSymbol newMethod) + { + if (oldMethod.AssociatedSymbol != null && newMethod.AssociatedSymbol != null) + { + if (oldMethod.MethodKind != newMethod.MethodKind) + { + rudeEdit = RudeEditKind.AccessorKindUpdate; + } + else + { + // rude edit will be reported by the associated symbol + rudeEdit = RudeEditKind.None; + } + } + else if (oldMethod.MethodKind == MethodKind.Conversion) + { + rudeEdit = RudeEditKind.ModifiersUpdate; + } + } + } + if (oldSymbol.DeclaredAccessibility != newSymbol.DeclaredAccessibility) { rudeEdit = RudeEditKind.ChangingAccessibility; @@ -3084,6 +3264,8 @@ private void ReportUpdatedSymbolDeclarationRudeEdits(ArrayBuilder Array.Empty(), - _ => new[] { GetDisplayName(newDeclaration, EditKind.Update) } - }; + hasGeneratedAttributeChange = true; + } + else + { + rudeEdit = rudeEditKind; + } + } + } + + private static void AnalyzeBaseTypes(INamedTypeSymbol oldType, INamedTypeSymbol newType, ref RudeEditKind rudeEdit, ref bool hasGeneratedAttributeChange) + { + if (oldType.EnumUnderlyingType != null && newType.EnumUnderlyingType != null) + { + if (!TypesEquivalent(oldType.EnumUnderlyingType, newType.EnumUnderlyingType, exact: true)) + { + if (TypesEquivalent(oldType.EnumUnderlyingType, newType.EnumUnderlyingType, exact: false)) + { + hasGeneratedAttributeChange = true; + } + else + { + rudeEdit = RudeEditKind.EnumUnderlyingTypeUpdate; + } + } + } + else if (!BaseTypesEquivalent(oldType, newType, exact: true)) + { + if (BaseTypesEquivalent(oldType, newType, exact: false)) + { + hasGeneratedAttributeChange = true; + } + else + { + rudeEdit = RudeEditKind.BaseTypeOrInterfaceUpdate; + } + } + } + + private static void AnalyzeParameterType(IParameterSymbol oldParameter, IParameterSymbol newParameter, ref RudeEditKind rudeEdit, ref bool hasGeneratedAttributeChange) + { + if (!ParameterTypesEquivalent(oldParameter, newParameter, exact: true)) + { + if (ParameterTypesEquivalent(oldParameter, newParameter, exact: false)) + { + hasGeneratedAttributeChange = true; + } + else + { + rudeEdit = RudeEditKind.TypeUpdate; + } + } + } - diagnostics.Add(new RudeEditDiagnostic(rudeEdit, GetDiagnosticSpan(newDeclaration, EditKind.Update), arguments: arguments)); + private static void AnalyzeTypeParameter(ITypeParameterSymbol oldParameter, ITypeParameterSymbol newParameter, ref RudeEditKind rudeEdit, ref bool hasGeneratedAttributeChange) + { + if (!TypeParameterConstraintsEquivalent(oldParameter, newParameter, exact: true)) + { + if (TypeParameterConstraintsEquivalent(oldParameter, newParameter, exact: false)) + { + hasGeneratedAttributeChange = true; + } + else + { + rudeEdit = (oldParameter.Variance != newParameter.Variance) ? RudeEditKind.VarianceUpdate : RudeEditKind.ChangingConstraints; + } } } + private static void AnalyzeReturnType(IMethodSymbol oldMethod, IMethodSymbol newMethod, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange) + { + if (!ReturnTypesEquivalent(oldMethod, newMethod, exact: true)) + { + if (ReturnTypesEquivalent(oldMethod, newMethod, exact: false)) + { + hasGeneratedReturnTypeAttributeChange = true; + } + else if (IsGlobalMain(oldMethod) || IsGlobalMain(newMethod)) + { + rudeEdit = RudeEditKind.ChangeImplicitMainReturnType; + } + else + { + rudeEdit = RudeEditKind.TypeUpdate; + } + } + } + + private static void AnalyzeReturnType(IEventSymbol oldEvent, IEventSymbol newEvent, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange) + { + if (!ReturnTypesEquivalent(oldEvent, newEvent, exact: true)) + { + if (ReturnTypesEquivalent(oldEvent, newEvent, exact: false)) + { + hasGeneratedReturnTypeAttributeChange = true; + } + else + { + rudeEdit = RudeEditKind.TypeUpdate; + } + } + } + + private static void AnalyzeReturnType(IPropertySymbol oldProperty, IPropertySymbol newProperty, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange) + { + if (!ReturnTypesEquivalent(oldProperty, newProperty, exact: true)) + { + if (ReturnTypesEquivalent(oldProperty, newProperty, exact: false)) + { + hasGeneratedReturnTypeAttributeChange = true; + } + else + { + rudeEdit = RudeEditKind.TypeUpdate; + } + } + } + + private static bool IsExtensionMethodThisParameter(IParameterSymbol parameter) + => parameter is { Ordinal: 0, ContainingSymbol: IMethodSymbol { IsExtensionMethod: true } }; + private void AnalyzeSymbolUpdate( ISymbol oldSymbol, ISymbol newSymbol, - SyntaxNode? newDeclaration, + SyntaxNode? newNode, + Compilation newCompilation, EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, - ArrayBuilder? semanticEdits, + ArrayBuilder semanticEdits, Func? syntaxMap, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(newSymbol.IsImplicitlyDeclared == newDeclaration is null); + // TODO: fails in VB on delegate parameter https://github.com/dotnet/roslyn/issues/53337 + // Contract.ThrowIfFalse(newSymbol.IsImplicitlyDeclared == newDeclaration is null); + + ReportCustomAttributeRudeEdits(diagnostics, oldSymbol, newSymbol, newNode, newCompilation, capabilities, out var hasAttributeChange, out var hasReturnTypeAttributeChange, cancellationToken); - AnalyzeCustomAttributes(oldSymbol, newSymbol, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + ReportUpdatedSymbolDeclarationRudeEdits(diagnostics, oldSymbol, newSymbol, newNode, newCompilation, out var hasGeneratedAttributeChange, out var hasGeneratedReturnTypeAttributeChange, cancellationToken); + hasAttributeChange |= hasGeneratedAttributeChange; + hasReturnTypeAttributeChange |= hasGeneratedReturnTypeAttributeChange; - // We might be updating an explicit old declaration to an implicit new declaration. - if (newDeclaration != null) + if (hasAttributeChange || hasReturnTypeAttributeChange) { - ReportUpdatedSymbolDeclarationRudeEdits(diagnostics, oldSymbol, newSymbol, newDeclaration); + AddCustomAttributeSemanticEdits(semanticEdits, newSymbol, syntaxMap, hasAttributeChange, hasReturnTypeAttributeChange, cancellationToken); } } - private void AnalyzeCustomAttributes(ISymbol oldSymbol, ISymbol newSymbol, EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, ArrayBuilder? semanticEdits, Func? syntaxMap, CancellationToken cancellationToken) + private static void AddCustomAttributeSemanticEdits( + ArrayBuilder semanticEdits, + ISymbol newSymbol, + Func? syntaxMap, + bool hasAttributeChange, + bool hasReturnTypeAttributeChange, + CancellationToken cancellationToken) { - var needsEdit = false; - - if (newSymbol is IMethodSymbol newMethod) + // Most symbol types will automatically have an edit added, so we just need to handle a few + if (newSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null and var newDelegateInvokeMethod } newDelegateType) { - if (oldSymbol is not IMethodSymbol oldMethod) + if (hasAttributeChange) { - return; + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newDelegateType, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); } - needsEdit |= HasCustomAttributeChanges(oldMethod.GetReturnTypeAttributes(), newMethod.GetReturnTypeAttributes(), newMethod, capabilities, diagnostics); + if (hasReturnTypeAttributeChange) + { + // attributes applied on return type of a delegate are applied to both Invoke and BeginInvoke methods + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newDelegateInvokeMethod, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + AddDelegateBeginInvokeEdit(newDelegateType); + } + } + else if (newSymbol is INamedTypeSymbol or IFieldSymbol or IPropertySymbol or IEventSymbol) + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + } + else if (newSymbol is ITypeParameterSymbol) + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newSymbol.ContainingSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); } - else if (newSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null } newType) + else if (newSymbol is IParameterSymbol) { - if (oldSymbol is not INamedTypeSymbol { DelegateInvokeMethod: not null } oldType) + var newContainingSymbol = newSymbol.ContainingSymbol; + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newContainingSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + + // attributes applied on parameters of a delegate are applied to both Invoke and BeginInvoke methods + if (newContainingSymbol.ContainingSymbol is INamedTypeSymbol { TypeKind: TypeKind.Delegate } newContainingDelegateType) { - return; + AddDelegateBeginInvokeEdit(newContainingDelegateType); } - - // If this is a delegate with attributes on its return type for example, they are found on the DelegateInvokeMethod - AnalyzeCustomAttributes(oldType.DelegateInvokeMethod, newType.DelegateInvokeMethod, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); } - foreach (var parameter in newSymbol.GetParameters()) + // attribute applied on parameters or return value of a delegate are applied to both Invoke and BeginInvoke methods + void AddDelegateBeginInvokeEdit(INamedTypeSymbol delegateType) { - var oldParameter = oldSymbol.GetParameters().FirstOrDefault(p => p.Name.Equals(parameter.Name)); - needsEdit |= HasCustomAttributeChanges(oldParameter?.GetAttributes(), parameter.GetAttributes(), parameter, capabilities, diagnostics); - } + Debug.Assert(semanticEdits != null); - foreach (var typeParam in newSymbol.GetTypeParameters()) - { - var oldParameter = oldSymbol.GetTypeParameters().FirstOrDefault(p => p.Name.Equals(typeParam.Name)); - needsEdit |= HasCustomAttributeChanges(oldParameter?.GetAttributes(), typeParam.GetAttributes(), typeParam, capabilities, diagnostics); + var beginInvokeMethod = delegateType.GetMembers("BeginInvoke").FirstOrDefault(); + if (beginInvokeMethod != null) + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(beginInvokeMethod, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + } } + } + private void ReportCustomAttributeRudeEdits( + ArrayBuilder diagnostics, + ISymbol oldSymbol, + ISymbol newSymbol, + SyntaxNode? newNode, + Compilation newCompilation, + EditAndContinueCapabilities capabilities, + out bool hasAttributeChange, + out bool hasReturnTypeAttributeChange, + CancellationToken cancellationToken) + { // This is the only case we care about whether to issue an edit or not, because this is the only case where types have their attributes checked // and types are the only things that would otherwise not have edits reported. - needsEdit |= HasCustomAttributeChanges(oldSymbol.GetAttributes(), newSymbol.GetAttributes(), newSymbol, capabilities, diagnostics); + hasAttributeChange = ReportCustomAttributeRudeEdits(diagnostics, oldSymbol.GetAttributes(), newSymbol.GetAttributes(), oldSymbol, newSymbol, newNode, newCompilation, capabilities, cancellationToken); - // If we don't need to add an edit, then we're done - if (!needsEdit || semanticEdits is null) - { - return; - } + hasReturnTypeAttributeChange = false; - // Most symbol types will automatically have an edit added, so we just need to handle a few - if (newSymbol is INamedTypeSymbol or IFieldSymbol or IPropertySymbol or IEventSymbol) + if (oldSymbol is IMethodSymbol oldMethod && + newSymbol is IMethodSymbol newMethod) { - var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); - semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); + hasReturnTypeAttributeChange |= ReportCustomAttributeRudeEdits(diagnostics, oldMethod.GetReturnTypeAttributes(), newMethod.GetReturnTypeAttributes(), oldSymbol, newSymbol, newNode, newCompilation, capabilities, cancellationToken); } - else if (newSymbol is ITypeParameterSymbol or IMethodSymbol { MethodKind: MethodKind.DelegateInvoke }) + else if (oldSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null and var oldInvokeMethod } && + newSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null and var newInvokeMethod }) { - var symbolKey = SymbolKey.Create(newSymbol.ContainingSymbol, cancellationToken); - semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); + hasReturnTypeAttributeChange |= ReportCustomAttributeRudeEdits(diagnostics, oldInvokeMethod.GetReturnTypeAttributes(), newInvokeMethod.GetReturnTypeAttributes(), oldSymbol, newSymbol, newNode, newCompilation, capabilities, cancellationToken); } } - private bool HasCustomAttributeChanges(ImmutableArray? oldAttributes, ImmutableArray newAttributes, ISymbol newSymbol, EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics) + private bool ReportCustomAttributeRudeEdits( + ArrayBuilder diagnostics, + ImmutableArray? oldAttributes, + ImmutableArray newAttributes, + ISymbol oldSymbol, + ISymbol newSymbol, + SyntaxNode? newNode, + Compilation newCompilation, + EditAndContinueCapabilities capabilities, + CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var changedAttributes); @@ -3238,8 +3666,7 @@ private bool HasCustomAttributeChanges(ImmutableArray? oldAttribu if (!capabilities.HasFlag(EditAndContinueCapabilities.ChangeCustomAttributes) || changedAttributes.Any(IsNonCustomAttribute)) { - var newNode = FindSyntaxNode(newSymbol); - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, GetDiagnosticSpan(newNode, EditKind.Update), newNode, new[] { GetDisplayName(newNode, EditKind.Update) })); + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingAttributesNotSupportedByRuntime, oldSymbol, newSymbol, newNode, newCompilation, cancellationToken); // If the runtime doesn't support edits then pretend there weren't changes, so no edits are produced return false; @@ -3284,18 +3711,6 @@ static void FindChangedAttributes(ImmutableArray? oldAttributes, return null; } - static SyntaxNode FindSyntaxNode(ISymbol symbol) - { - // In VB parameters of delegates don't have declaring syntax references so we have to go all the way up to the delegate - // See: https://github.com/dotnet/roslyn/issues/53337 - if (symbol.DeclaringSyntaxReferences.Length == 0) - { - return FindSyntaxNode(symbol.ContainingSymbol); - } - - return symbol.DeclaringSyntaxReferences.First().GetSyntax(); - } - static bool IsNonCustomAttribute(AttributeData attribute) { // TODO: Use a compiler API to get this information rather than hard coding a list: https://github.com/dotnet/roslyn/issues/53410 @@ -3374,11 +3789,11 @@ private static bool CanAddNewMember(ISymbol newSymbol, EditAndContinueCapabiliti return true; } - private static void AddEditsForSynthesizedRecordMembers(Compilation compilation, INamedTypeSymbol recordType, ArrayBuilder semanticEdits) + private static void AddEditsForSynthesizedRecordMembers(Compilation compilation, INamedTypeSymbol recordType, ArrayBuilder semanticEdits, CancellationToken cancellationToken) { foreach (var member in GetRecordUpdatedSynthesizedMembers(compilation, recordType)) { - var symbolKey = SymbolKey.Create(member); + var symbolKey = SymbolKey.Create(member, cancellationToken); semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap: null, syntaxMapTree: null, partialType: null)); } } @@ -3430,20 +3845,86 @@ private static IEnumerable GetRecordUpdatedSynthesizedMembers(Compilati private void ReportDeletedMemberRudeEdit( ArrayBuilder diagnostics, - EditScript editScript, - SyntaxNode oldNode, ISymbol oldSymbol, - RudeEditKind rudeEditKind) + Compilation newCompilation, + RudeEditKind rudeEditKind, + CancellationToken cancellationToken) { + var newNode = GetDeleteRudeEditDiagnosticNode(oldSymbol, newCompilation, cancellationToken); + diagnostics.Add(new RudeEditDiagnostic( rudeEditKind, - GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, oldNode), + GetDiagnosticSpan(newNode, EditKind.Delete), arguments: new[] { - string.Format(FeaturesResources.member_kind_and_name, GetDisplayName(oldNode, EditKind.Delete), oldSymbol.ToDisplayString(s_unqualifiedMemberDisplayFormat)) + string.Format(FeaturesResources.member_kind_and_name, GetDisplayName(oldSymbol), oldSymbol.ToDisplayString(s_unqualifiedMemberDisplayFormat)) })); } + private void ReportUpdateRudeEdit(ArrayBuilder diagnostics, RudeEditKind rudeEdit, SyntaxNode newNode) + { + diagnostics.Add(new RudeEditDiagnostic( + rudeEdit, + GetDiagnosticSpan(newNode, EditKind.Update), + newNode, + new[] { GetDisplayName(newNode) })); + } + + private void ReportUpdateRudeEdit(ArrayBuilder diagnostics, RudeEditKind rudeEdit, ISymbol newSymbol, SyntaxNode? newNode, CancellationToken cancellationToken) + { + var node = newNode ?? GetRudeEditDiagnosticNode(newSymbol, cancellationToken); + var span = (rudeEdit == RudeEditKind.ChangeImplicitMainReturnType) ? GetGlobalStatementDiagnosticSpan(node) : GetDiagnosticSpan(node, EditKind.Update); + var arguments = (rudeEdit is RudeEditKind.TypeKindUpdate or RudeEditKind.ChangeImplicitMainReturnType) ? Array.Empty() : new[] { GetDisplayName(newSymbol) }; + + diagnostics.Add(new RudeEditDiagnostic(rudeEdit, span, node, arguments)); + } + + private void ReportUpdateRudeEdit(ArrayBuilder diagnostics, RudeEditKind rudeEdit, ISymbol oldSymbol, ISymbol newSymbol, SyntaxNode? newNode, Compilation newCompilation, CancellationToken cancellationToken) + { + if (newSymbol.IsImplicitlyDeclared) + { + ReportDeletedMemberRudeEdit(diagnostics, oldSymbol, newCompilation, rudeEdit, cancellationToken); + } + else + { + ReportUpdateRudeEdit(diagnostics, rudeEdit, newSymbol, newNode, cancellationToken); + } + } + + private static SyntaxNode GetRudeEditDiagnosticNode(ISymbol symbol, CancellationToken cancellationToken) + { + var container = symbol; + while (container != null) + { + if (container.DeclaringSyntaxReferences.Length > 0) + { + return container.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + } + + container = container.ContainingSymbol; + } + + throw ExceptionUtilities.Unreachable; + } + + private static SyntaxNode GetDeleteRudeEditDiagnosticNode(ISymbol oldSymbol, Compilation newCompilation, CancellationToken cancellationToken) + { + var oldContainer = oldSymbol.ContainingSymbol; + while (oldContainer != null) + { + var containerKey = SymbolKey.Create(oldContainer, cancellationToken); + var newContainer = containerKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + if (newContainer != null) + { + return GetRudeEditDiagnosticNode(newContainer, cancellationToken); + } + + oldContainer = oldContainer.ContainingSymbol; + } + + throw ExceptionUtilities.Unreachable; + } + #region Type Layout Update Validation internal void ReportTypeLayoutUpdateRudeEdits( @@ -3634,6 +4115,7 @@ private void AddConstructorEdits( Match topMatch, SemanticModel? oldModel, Compilation oldCompilation, + Compilation newCompilation, IReadOnlySet processedSymbols, EditAndContinueCapabilities capabilities, bool isStatic, @@ -3817,7 +4299,7 @@ static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) if (oldCtor != null) { - AnalyzeSymbolUpdate(oldCtor, newCtor, newDeclaration, capabilities, diagnostics, semanticEdits: null, syntaxMap: null, cancellationToken); + AnalyzeSymbolUpdate(oldCtor, newCtor, newDeclaration, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMapToUse, cancellationToken); semanticEdits.Add(new SemanticEditInfo( SemanticEditKind.Update, @@ -3915,7 +4397,7 @@ private void ReportLambdaAndClosureRudeEdits( return; } - ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaInfo.NewBody, capabilities, diagnostics, out var hasErrors, cancellationToken); + ReportLambdaSignatureRudeEdits(diagnostics, oldModel, oldLambdaBody, newModel, newLambdaInfo.NewBody, capabilities, out var hasErrors, cancellationToken); anySignatureErrors |= hasErrors; } @@ -4472,7 +4954,7 @@ private void CalculateCapturedVariablesMaps( var oldTypeOpt = GetType(oldCapture); var newTypeOpt = GetType(newCapture); - if (!s_assemblyEqualityComparer.Equals(oldTypeOpt, newTypeOpt)) + if (!TypesEquivalent(oldTypeOpt, newTypeOpt, exact: false)) { diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.ChangingCapturedVariableType, @@ -4579,16 +5061,18 @@ private void CalculateCapturedVariablesMaps( } } - protected virtual void ReportLambdaSignatureRudeEdits( + private void ReportLambdaSignatureRudeEdits( + ArrayBuilder diagnostics, SemanticModel oldModel, SyntaxNode oldLambdaBody, SemanticModel newModel, SyntaxNode newLambdaBody, EditAndContinueCapabilities capabilities, - ArrayBuilder diagnostics, - out bool hasErrors, + out bool hasSignatureErrors, CancellationToken cancellationToken) { + hasSignatureErrors = false; + var newLambda = GetLambda(newLambdaBody); var oldLambda = GetLambda(oldLambdaBody); @@ -4597,38 +5081,54 @@ protected virtual void ReportLambdaSignatureRudeEdits( // queries are analyzed separately if (!IsNestedFunction(newLambda)) { - hasErrors = false; + return; + } + + if (IsLocalFunction(oldLambda) != IsLocalFunction(newLambda)) + { + ReportUpdateRudeEdit(diagnostics, RudeEditKind.SwitchBetweenLambdaAndLocalFunction, newLambda); + hasSignatureErrors = true; return; } var oldLambdaSymbol = GetLambdaExpressionSymbol(oldModel, oldLambda, cancellationToken); var newLambdaSymbol = GetLambdaExpressionSymbol(newModel, newLambda, cancellationToken); - AnalyzeCustomAttributes(oldLambdaSymbol, newLambdaSymbol, capabilities, diagnostics, semanticEdits: null, syntaxMap: null, cancellationToken); - - RudeEditKind rudeEdit; - - if (!oldLambdaSymbol.Parameters.SequenceEqual(newLambdaSymbol.Parameters, s_assemblyEqualityComparer.ParameterEquivalenceComparer)) + // signature validation: + if (!ParametersEquivalent(oldLambdaSymbol.Parameters, newLambdaSymbol.Parameters, exact: false)) { - rudeEdit = RudeEditKind.ChangingLambdaParameters; + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingLambdaParameters, newLambda); + hasSignatureErrors = true; } - else if (!s_assemblyEqualityComparer.ReturnTypeEquals(oldLambdaSymbol, newLambdaSymbol)) + else if (!ReturnTypesEquivalent(oldLambdaSymbol, newLambdaSymbol, exact: false)) { - rudeEdit = RudeEditKind.ChangingLambdaReturnType; + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingLambdaReturnType, newLambda); + hasSignatureErrors = true; } - else + else if (!TypeParametersEquivalent(oldLambdaSymbol.TypeParameters, newLambdaSymbol.TypeParameters, exact: false)) + { + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingTypeParameters, newLambda); + hasSignatureErrors = true; + } + + if (hasSignatureErrors) { - hasErrors = false; return; } - diagnostics.Add(new RudeEditDiagnostic( - rudeEdit, - GetDiagnosticSpan(newLambda, EditKind.Update), - newLambda, - new[] { GetDisplayName(newLambda) })); + // custom attributes + + ReportCustomAttributeRudeEdits(diagnostics, oldLambdaSymbol, newLambdaSymbol, newLambda, newModel.Compilation, capabilities, out _, out _, cancellationToken); - hasErrors = true; + for (var i = 0; i < oldLambdaSymbol.Parameters.Length; i++) + { + ReportCustomAttributeRudeEdits(diagnostics, oldLambdaSymbol.Parameters[i], newLambdaSymbol.Parameters[i], newLambda, newModel.Compilation, capabilities, out _, out _, cancellationToken); + } + + for (var i = 0; i < oldLambdaSymbol.TypeParameters.Length; i++) + { + ReportCustomAttributeRudeEdits(diagnostics, oldLambdaSymbol.TypeParameters[i], newLambdaSymbol.TypeParameters[i], newLambda, newModel.Compilation, capabilities, out _, out _, cancellationToken); + } } private static ITypeSymbol GetType(ISymbol localOrParameter) @@ -4782,6 +5282,11 @@ public int GetHashCode(KeyValuePair obj) => obj.GetHashCode(); } +#pragma warning disable format + private static bool IsGlobalMain(ISymbol symbol) + => symbol is IMethodSymbol { Name: WellKnownMemberNames.TopLevelStatementsEntryPointMethodName, ContainingType.Name: WellKnownMemberNames.TopLevelStatementsEntryPointTypeName }; +#pragma warning restore format + #endregion #region Testing diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index 1029bfd4204c2..bbc3e647a839d 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -81,7 +81,6 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.BaseTypeOrInterfaceUpdate, nameof(FeaturesResources.Updating_the_base_class_and_or_base_interface_s_of_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.TypeKindUpdate, nameof(FeaturesResources.Updating_the_kind_of_a_type_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.AccessorKindUpdate, nameof(FeaturesResources.Updating_the_kind_of_an_property_event_accessor_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.MethodKindUpdate, nameof(FeaturesResources.Updating_the_kind_of_a_method_Sub_Function_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.DeclareAliasUpdate, nameof(FeaturesResources.Updating_the_alias_of_Declare_Statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.DeclareLibraryUpdate, nameof(FeaturesResources.Updating_the_library_name_of_Declare_Statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.FieldKindUpdate, nameof(FeaturesResources.Updating_a_field_to_an_event_or_vice_versa_will_prevent_the_debug_session_from_continuing)); @@ -145,8 +144,6 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.ImplementRecordParameterAsReadOnly, nameof(FeaturesResources.Implementing_a_record_positional_parameter_0_as_read_only_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ImplementRecordParameterWithSet, nameof(FeaturesResources.Implementing_a_record_positional_parameter_0_with_a_set_accessor_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.AddRecordPositionalParameter, nameof(FeaturesResources.Adding_a_positional_parameter_to_a_record_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.DeleteRecordPositionalParameter, nameof(FeaturesResources.Deleting_a_positional_parameter_from_a_record_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, nameof(FeaturesResources.Explicitly_implemented_methods_of_records_must_have_parameter_names_that_match_the_compiler_generated_equivalent_0)); AddRudeEdit(RudeEditKind.NotSupportedByRuntime, nameof(FeaturesResources.Edit_and_continue_is_not_supported_by_the_runtime)); @@ -154,11 +151,15 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.MakeMethodIterator, nameof(FeaturesResources.Making_a_method_an_iterator_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.InsertNotSupportedByRuntime, nameof(FeaturesResources.Adding_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ChangingAttributesNotSupportedByRuntime, nameof(FeaturesResources.Updating_the_attributes_of_0_is_not_supported_by_the_runtime)); + AddRudeEdit(RudeEditKind.ChangingParameterTypes, nameof(FeaturesResources.Changing_parameter_types_of_0_will_prevent_the_debug_session_from_continuing)); + AddRudeEdit(RudeEditKind.ChangingTypeParameters, nameof(FeaturesResources.Changing_type_parameters_of_0_will_prevent_the_debug_session_from_continuing)); + AddRudeEdit(RudeEditKind.ChangingConstraints, nameof(FeaturesResources.Changing_constraints_of_0_will_prevent_the_debug_session_from_continuing)); + + AddRudeEdit(RudeEditKind.ChangeImplicitMainReturnType, FeaturesResources.An_update_that_causes_the_return_type_of_implicit_main_to_change_will_prevent_the_debug_session_from_continuing); // VB specific AddRudeEdit(RudeEditKind.HandlesClauseUpdate, nameof(FeaturesResources.Updating_the_Handles_clause_of_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ImplementsClauseUpdate, nameof(FeaturesResources.Updating_the_Implements_clause_of_a_0_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.ConstraintKindUpdate, nameof(FeaturesResources.Changing_the_constraint_from_0_to_1_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.InsertHandlesClause, nameof(FeaturesResources.Adding_0_with_the_Handles_clause_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.UpdateStaticLocal, nameof(FeaturesResources.Modifying_0_which_contains_a_static_variable_will_prevent_the_debug_session_from_continuing)); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 06459d0085077..a2970db04abf6 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -97,7 +97,7 @@ public void OnSourceFileUpdated(Document document) foreach (var debuggingSession in GetActiveDebuggingSessions()) { // fire and forget - _ = Task.Run(() => debuggingSession.OnSourceFileUpdatedAsync(document)); + _ = Task.Run(() => debuggingSession.OnSourceFileUpdatedAsync(document)).ReportNonFatalErrorAsync(); } } diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index bd26ddb8f6edd..6a641e23d4379 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -23,14 +23,14 @@ internal enum RudeEditKind : ushort VarianceUpdate = 7, FieldKindUpdate = 8, TypeUpdate = 9, - ConstraintKindUpdate = 10, + //ConstraintKindUpdate = 10, InitializerUpdate = 11, FixedSizeFieldUpdate = 12, EnumUnderlyingTypeUpdate = 13, BaseTypeOrInterfaceUpdate = 14, TypeKindUpdate = 15, AccessorKindUpdate = 16, - MethodKindUpdate = 17, + //MethodKindUpdate = 17, DeclareLibraryUpdate = 18, DeclareAliasUpdate = 19, Renamed = 20, @@ -120,14 +120,19 @@ internal enum RudeEditKind : ushort ImplementRecordParameterAsReadOnly = 92, ImplementRecordParameterWithSet = 93, - AddRecordPositionalParameter = 94, - DeleteRecordPositionalParameter = 95, + //AddRecordPositionalParameter = 94, + //DeleteRecordPositionalParameter = 95, ExplicitRecordMethodParameterNamesMustMatch = 96, NotSupportedByRuntime = 97, MakeMethodAsync = 98, MakeMethodIterator = 99, InsertNotSupportedByRuntime = 100, - ChangingAttributesNotSupportedByRuntime = 101 + ChangingAttributesNotSupportedByRuntime = 101, + + ChangeImplicitMainReturnType = 102, + ChangingParameterTypes = 103, + ChangingTypeParameters = 104, + ChangingConstraints = 105, } } diff --git a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs index 1c371668e3b5f..f33a07c2d04ee 100644 --- a/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/AbstractEncapsulateFieldService.cs @@ -437,7 +437,7 @@ protected static string GeneratePropertyName(string fieldName) private class MyCodeAction : CodeAction.SolutionChangeAction { public MyCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index a1e7d9653e735..97bd53da30702 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -141,6 +141,12 @@ overloads + + type + + + property accessor + {0} Keyword @@ -432,9 +438,6 @@ Updating the Implements clause of a '{0}' will prevent the debug session from continuing. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Updating the variance of '{0}' will prevent the debug session from continuing. @@ -462,9 +465,6 @@ Updating the kind of an property/event accessor will prevent the debug session from continuing. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Updating the library name of Declare Statement will prevent the debug session from continuing. @@ -2864,12 +2864,6 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Implementing a record positional parameter '{0}' with a set accessor will prevent the debug session from continuing. - - Adding a positional parameter to a record will prevent the debug session from continuing. - - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Explicitly implemented methods of records must have parameter names that match the compiler generated equivalent '{0}' @@ -2888,4 +2882,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Updating the attributes of '{0}' is not supported by the runtime. + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + + + Changing constraints of '{0}' will prevent the debug session from continuing. + \ No newline at end of file diff --git a/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs b/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs index d4ec02f107915..1bd75a40c181e 100644 --- a/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs +++ b/src/Features/Core/Portable/InlineMethod/AbstractInlineMethodRefactoringProvider.cs @@ -595,7 +595,7 @@ private class MySolutionChangeAction : CodeAction.SolutionChangeAction public MySolutionChangeAction( string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/IntroduceUsingStatement/AbstractIntroduceUsingStatementCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceUsingStatement/AbstractIntroduceUsingStatementCodeRefactoringProvider.cs index e2d10efc3af21..d877104f3c395 100644 --- a/src/Features/Core/Portable/IntroduceUsingStatement/AbstractIntroduceUsingStatementCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceUsingStatement/AbstractIntroduceUsingStatementCodeRefactoringProvider.cs @@ -334,7 +334,7 @@ private static void AddReferencedLocalVariables( private sealed class MyCodeAction : DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceParameterService.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceParameterService.cs index 42d8243ae3363..58eca8d5cb290 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceParameterService.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceParameterService.cs @@ -307,7 +307,7 @@ await SymbolFinder.FindReferencesAsync( private class MyCodeAction : SolutionChangeAction { public MyCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/IntroduceVariable/IntroduceLocalForExpressionCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceVariable/IntroduceLocalForExpressionCodeRefactoringProvider.cs index 49cc2c996491a..b7724a4a7adf8 100644 --- a/src/Features/Core/Portable/IntroduceVariable/IntroduceLocalForExpressionCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceVariable/IntroduceLocalForExpressionCodeRefactoringProvider.cs @@ -111,7 +111,7 @@ protected static async Task GenerateUniqueNameAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs b/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs index 2907b80212202..5c455fd9be1c2 100644 --- a/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InvertConditional/AbstractInvertConditionalCodeRefactoringProvider.cs @@ -31,7 +31,6 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } context.RegisterRefactoring(new MyCodeAction( - FeaturesResources.Invert_conditional, c => InvertConditionalAsync(document, span, c)), conditional.Span); } @@ -63,8 +62,8 @@ private static async Task InvertConditionalAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + public MyCodeAction(Func> createChangedDocument) + : base(FeaturesResources.Invert_conditional, createChangedDocument, nameof(FeaturesResources.Invert_conditional)) { } } diff --git a/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs b/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs index 9c913c0905026..4b8f208629a32 100644 --- a/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InvertIf/AbstractInvertIfCodeRefactoringProvider.cs @@ -594,7 +594,7 @@ private SyntaxNode GetRootWithInvertIfStatement( private sealed class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/InvertLogical/AbstractInvertLogicalCodeRefactoringProvider.cs b/src/Features/Core/Portable/InvertLogical/AbstractInvertLogicalCodeRefactoringProvider.cs index 004ef95c610fa..a8addb9f1a230 100644 --- a/src/Features/Core/Portable/InvertLogical/AbstractInvertLogicalCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InvertLogical/AbstractInvertLogicalCodeRefactoringProvider.cs @@ -148,7 +148,7 @@ private static int InvertedKind(ISyntaxKindsService syntaxKinds, int binaryExprK private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs index 4d7b9aa1b6c88..58f64598b9903 100644 --- a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -57,7 +57,7 @@ private static async Task MoveDeclarationNearReferenceAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(FeaturesResources.Move_declaration_near_reference, createChangedDocument) + : base(FeaturesResources.Move_declaration_near_reference, createChangedDocument, nameof(FeaturesResources.Move_declaration_near_reference)) { } diff --git a/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs b/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs index 5d02c5c973f23..1392b09d085fc 100644 --- a/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/NameTupleElement/AbstractNameTupleElementCodeRefactoringProvider.cs @@ -91,7 +91,7 @@ private async Task AddNamedElementAsync(Document document, TextSpan sp private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/PullMemberUp/MembersPuller.cs b/src/Features/Core/Portable/PullMemberUp/MembersPuller.cs index 847bca5a94c3d..c6b7b681661c1 100644 --- a/src/Features/Core/Portable/PullMemberUp/MembersPuller.cs +++ b/src/Features/Core/Portable/PullMemberUp/MembersPuller.cs @@ -37,9 +37,11 @@ public static CodeAction TryComputeCodeAction( return null; } + var title = string.Format(FeaturesResources.Pull_0_up_to_1, selectedMember.Name, result.Destination.Name); return new SolutionChangeAction( - string.Format(FeaturesResources.Pull_0_up_to_1, selectedMember.Name, result.Destination.Name), - cancellationToken => PullMembersUpAsync(document, result, cancellationToken)); + title, + cancellationToken => PullMembersUpAsync(document, result, cancellationToken), + title); } /// diff --git a/src/Features/Core/Portable/ReplaceDocCommentTextWithTag/AbstractReplaceDocCommentTextWithTagCodeRefactoringProvider.cs b/src/Features/Core/Portable/ReplaceDocCommentTextWithTag/AbstractReplaceDocCommentTextWithTagCodeRefactoringProvider.cs index 4265120872d55..76b8728c79add 100644 --- a/src/Features/Core/Portable/ReplaceDocCommentTextWithTag/AbstractReplaceDocCommentTextWithTagCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ReplaceDocCommentTextWithTag/AbstractReplaceDocCommentTextWithTagCodeRefactoringProvider.cs @@ -238,7 +238,7 @@ private static bool ShouldExpandSpanBackwardOneCharacter( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractMergeConsecutiveIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractMergeConsecutiveIfStatementsCodeRefactoringProvider.cs index 834c264c18044..417fa4eaad791 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractMergeConsecutiveIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractMergeConsecutiveIfStatementsCodeRefactoringProvider.cs @@ -46,7 +46,10 @@ internal abstract class AbstractMergeConsecutiveIfStatementsCodeRefactoringProvi // The body statements need to be equivalent. In the second case, control flow must quit from inside the body. protected sealed override CodeAction CreateCodeAction(Func> createChangedDocument, MergeDirection direction, string ifKeywordText) - => new MyCodeAction(createChangedDocument, direction, ifKeywordText); + { + var resourceText = direction == MergeDirection.Up ? FeaturesResources.Merge_with_previous_0_statement : FeaturesResources.Merge_with_next_0_statement; + return new MyCodeAction(string.Format(resourceText, ifKeywordText), createChangedDocument); + } protected sealed override Task CanBeMergedUpAsync( Document document, SyntaxNode ifOrElseIf, CancellationToken cancellationToken, out SyntaxNode firstIfOrElseIf) @@ -243,13 +246,10 @@ private static bool ContainEquivalentStatements( private sealed class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(Func> createChangedDocument, MergeDirection direction, string ifKeywordText) - : base(string.Format(GetResourceText(direction), ifKeywordText), createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) + : base(title, createChangedDocument, title) { } - - private static string GetResourceText(MergeDirection direction) - => direction == MergeDirection.Up ? FeaturesResources.Merge_with_previous_0_statement : FeaturesResources.Merge_with_next_0_statement; } } } diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs index bfdf42a26cbb0..58571f6ba0b0f 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs @@ -46,7 +46,7 @@ protected sealed override int GetLogicalExpressionKind(ISyntaxKindsService synta => syntaxKinds.LogicalOrExpression; protected sealed override CodeAction CreateCodeAction(Func> createChangedDocument, string ifKeywordText) - => new MyCodeAction(createChangedDocument, ifKeywordText); + => new MyCodeAction(string.Format(FeaturesResources.Split_into_consecutive_0_statements, ifKeywordText), createChangedDocument); protected sealed override async Task GetChangedRootAsync( Document document, @@ -153,8 +153,8 @@ private static async Task CanBeSeparateStatementsAsync( private sealed class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(Func> createChangedDocument, string ifKeywordText) - : base(string.Format(FeaturesResources.Split_into_consecutive_0_statements, ifKeywordText), createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractMergeNestedIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractMergeNestedIfStatementsCodeRefactoringProvider.cs index 9fec2ebaaba5c..e11c835396f03 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractMergeNestedIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractMergeNestedIfStatementsCodeRefactoringProvider.cs @@ -34,7 +34,10 @@ internal abstract class AbstractMergeNestedIfStatementsCodeRefactoringProvider // Console.WriteLine(); protected sealed override CodeAction CreateCodeAction(Func> createChangedDocument, MergeDirection direction, string ifKeywordText) - => new MyCodeAction(createChangedDocument, direction, ifKeywordText); + { + var resourceText = direction == MergeDirection.Up ? FeaturesResources.Merge_with_outer_0_statement : FeaturesResources.Merge_with_nested_0_statement; + return new MyCodeAction(string.Format(resourceText, ifKeywordText), createChangedDocument); + } protected sealed override Task CanBeMergedUpAsync( Document document, SyntaxNode ifOrElseIf, CancellationToken cancellationToken, out SyntaxNode outerIfOrElseIf) @@ -254,13 +257,10 @@ private static bool IsElseIfOrElseClauseEquivalent( private sealed class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(Func> createChangedDocument, MergeDirection direction, string ifKeywordText) - : base(string.Format(GetResourceText(direction), ifKeywordText), createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) + : base(title, createChangedDocument, title) { } - - private static string GetResourceText(MergeDirection direction) - => direction == MergeDirection.Up ? FeaturesResources.Merge_with_outer_0_statement : FeaturesResources.Merge_with_nested_0_statement; } } } diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractSplitIntoNestedIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractSplitIntoNestedIfStatementsCodeRefactoringProvider.cs index 96683be83747b..f7b3c45295fae 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractSplitIntoNestedIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/Nested/AbstractSplitIntoNestedIfStatementsCodeRefactoringProvider.cs @@ -32,7 +32,7 @@ protected sealed override int GetLogicalExpressionKind(ISyntaxKindsService synta => syntaxKinds.LogicalAndExpression; protected sealed override CodeAction CreateCodeAction(Func> createChangedDocument, string ifKeywordText) - => new MyCodeAction(createChangedDocument, ifKeywordText); + => new MyCodeAction(string.Format(FeaturesResources.Split_into_nested_0_statements, ifKeywordText), createChangedDocument); protected sealed override Task GetChangedRootAsync( Document document, @@ -56,8 +56,8 @@ protected sealed override Task GetChangedRootAsync( private sealed class MyCodeAction : CodeAction.DocumentChangeAction { - public MyCodeAction(Func> createChangedDocument, string ifKeywordText) - : base(string.Format(FeaturesResources.Split_into_nested_0_statements, ifKeywordText), createChangedDocument) + public MyCodeAction(string title, Func> createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs index 5ae92fc11cad4..617735ffc952a 100644 --- a/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs +++ b/src/Features/Core/Portable/UnifiedSuggestions/UnifiedSuggestedActionsSource.cs @@ -270,7 +270,7 @@ private static ImmutableArray PrioritizeFixGroups( { // Wrap the suppression/configuration actions within another top level suggested action // to avoid clutter in the light bulb menu. - var suppressOrConfigureCodeAction = new NoChangeAction(CodeFixesResources.Suppress_or_Configure_issues); + var suppressOrConfigureCodeAction = new NoChangeAction(CodeFixesResources.Suppress_or_Configure_issues, nameof(CodeFixesResources.Suppress_or_Configure_issues)); var wrappingSuggestedAction = new UnifiedSuggestedActionWithNestedActions( workspace, codeAction: suppressOrConfigureCodeAction, codeActionPriority: suppressOrConfigureCodeAction.Priority, provider: null, diff --git a/src/Features/Core/Portable/UnsealClass/AbstractUnsealClassCodeFixProvider.cs b/src/Features/Core/Portable/UnsealClass/AbstractUnsealClassCodeFixProvider.cs index 7c1fce8f3c4cb..15ddf33896e86 100644 --- a/src/Features/Core/Portable/UnsealClass/AbstractUnsealClassCodeFixProvider.cs +++ b/src/Features/Core/Portable/UnsealClass/AbstractUnsealClassCodeFixProvider.cs @@ -87,7 +87,7 @@ private static async Task UnsealDeclarationsAsync( private sealed class MyCodeAction : CodeAction.SolutionChangeAction { public MyCodeAction(string title, Func> createChangedSolution) - : base(title, createChangedSolution) + : base(title, createChangedSolution, title) { } } diff --git a/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs b/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs index 03adc9af14c93..d00ebc9956317 100644 --- a/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/UseNamedArguments/AbstractUseNamedArgumentsCodeRefactoringProvider.cs @@ -214,7 +214,7 @@ public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContex private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(string title, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { } } diff --git a/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs b/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs index 5b3532db94d8d..caed7435cddc7 100644 --- a/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs +++ b/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs @@ -42,7 +42,7 @@ internal class WrapItemsAction : DocumentChangeAction internal override CodeActionPriority Priority => CodeActionPriority.Low; public WrapItemsAction(string title, string parentTitle, Func> createChangedDocument) - : base(title, createChangedDocument) + : base(title, createChangedDocument, title) { ParentTitle = parentTitle; SortTitle = parentTitle + "_" + title; diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 1df883b850441..7d0d239fdc2d9 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -110,11 +110,6 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Přidání prvku {0} do rozhraní zabrání v pokračování relace ladění. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Když se do záznamu přidá parametr pozice, ladicí relace nebude moct pokračovat. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Přidání metody s explicitním specifikátorem rozhraní zabrání v pokračování relace ladění. @@ -140,6 +135,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Podmínky alternativního výrazu nezachytávají a nejde je pojmenovat. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Použít předvolby hlaviček souborů @@ -235,6 +235,21 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Pokud se změní {0} na {1}, relace ladění nebude moct pokračovat, protože dojde ke změně tvaru stavového počítače. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Když se změní viditelnost pro {0}, ladicí relace nebude moct pokračovat. @@ -320,11 +335,6 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Vytvořit a přiřadit zbývající položky jako vlastnosti - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Když se ze záznamu odstraní parametr pozice, ladicí relace nebude moct pokračovat. - - Do not change this code. Put cleanup code in '{0}' method Neměňte tento kód. Kód pro vyčištění vložte do metody {0}. @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastního formátu, interpretuje se jako standardní specifikátor formátu data a času g. + + property accessor + property accessor + + range variable proměnná rozsahu @@ -3174,11 +3189,6 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn Aktualizace klauzule Implements pro {0} zabrání v pokračování relace ladění. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Změna omezení z {0} na {1} zabrání v pokračování relace ladění. - - Updating the variance of '{0}' will prevent the debug session from continuing. Aktualizace odchylky pro {0} zabrání v pokračování relace ladění. @@ -3224,11 +3234,6 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn Aktualizace druhu přistupujícího objektu pro vlastnost/událost zabrání v pokračování relace ladění. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Aktualizace tohoto druhu metody (Sub/Function) zabrání v pokračování relace ladění. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Aktualizace názvu knihovny pro deklaraci příkazu zabrání v pokračování relace ladění. @@ -3843,6 +3848,11 @@ Pro hodnoty DateTimeOffset je specifikátor formátu K ekvivalentní specifikát Pokud se specifikátor formátu K použije bez dalších specifikátorů vlastního formátu, interpretuje se jako standardní specifikátor formátu data a času a vyvolá FormatException. + + type + type + + type constraint omezení typu diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index fb78e23e5bce2..339902abb3b37 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -110,11 +110,6 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Das Hinzufügen von "{0}" zu einer Schnittstelle verhindert das Fortsetzen der Debugsitzung. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Das Hinzufügen eines Positionsparameters zu einem Datensatz verhindert die Fortsetzung der Debugsitzung. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Das Hinzufügen einer Methode mit einem expliziten Schnittstellenbezeichner verhindert das Fortsetzen der Debugsitzung. @@ -140,6 +135,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Wechselbedingungen werden nicht erfasst und können nicht benannt werden. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Dateiheadereinstellungen anwenden @@ -235,6 +235,21 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Die Änderung von "{0}" in "{1}" verhindert, dass die Debugsitzung fortgesetzt wird, weil sich dadurch die Form des Zustandsautomaten verändert. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Durch Änderungen an der Sichtbarkeit von "{0}" wird die Fortsetzung der Debugsitzung verhindert. @@ -320,11 +335,6 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Verbleibende Elemente als Eigenschaften erstellen und zuweisen - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Das Löschen eines Positionsparameters aus einem Datensatz verhindert die Fortsetzung der Debugsitzung. - - Do not change this code. Put cleanup code in '{0}' method Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in der Methode "{0}" ein. @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatbezeichner wird er als Standardformatbezeichner "g" für Datum und Uhrzeit interpretiert. + + property accessor + property accessor + + range variable Bereichsvariable @@ -3174,11 +3189,6 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb Durch Aktualisieren der Implements-Klausel eines "{0}" wird verhindert, dass die Debuggingsitzung fortgesetzt wird. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Durch Ändern der Einschränkung von "{0}" in "{1}" wird verhindert, dass die Debuggingsitzung fortgesetzt wird. - - Updating the variance of '{0}' will prevent the debug session from continuing. Durch Aktualisieren der Varianz von "{0}" wird verhindert, dass die Debuggingsitzung fortgesetzt wird. @@ -3224,11 +3234,6 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb Durch Aktualisieren der Art einer Eigenschaft/eines Ereignissaccessors wird verhindert, dass die Debuggingsitzung fortgesetzt wird. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Durch Aktualisieren der Art einer Methode (Sub/Function) wird verhindert, dass die Debuggingsitzung fortgesetzt wird. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Durch Aktualisieren des Bibliotheknamens der Declare-Anweisung wird verhindert, dass die Debuggingsitzung fortgesetzt wird. @@ -3843,6 +3848,11 @@ Für DateTimeOffset-Werte ist der Formatbezeichner "K" äquivalent zum Formatbez Bei Verwendung des Formatbezeichners "K" ohne weitere benutzerdefinierte Formatbezeichner wird er als Standardformatbezeichner für Datums- und Uhrzeitwerte interpretiert und löst eine FormatException aus. + + type + type + + type constraint Typeinschränkung diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 50a5fd50630e5..bdc894f0c2e1a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -110,11 +110,6 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Agregar '{0}' en una interfaz impedirá que continúe la sesión de depuración. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Al agregar un parámetro posicional a un registro impedirá que continúe la sesión de depuración. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Agregar un método con un especificador de interfaz explícita evitará que la sesión de depuración continúe. @@ -140,6 +135,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Las condiciones de alternancia no se captan y se les puede poner un nombre This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Aplicar preferencias de encabezado de archivo @@ -235,6 +235,21 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Si se cambia "{0}" a "{1}", la sesión de depuración no podrá continuar porque cambia la forma de la máquina de estados. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Si se cambia la visibilidad de {0}, la sesión de depuración no podrá continuar. @@ -320,11 +335,6 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Crear y asignar el resto como propiedades - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Eliminar un parámetro posicional desde un registro impedirá que continúe la sesión de depuración. - - Do not change this code. Put cleanup code in '{0}' method No cambie este código. Coloque el código de limpieza en el método "{0}". @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Si el especificador de formato "g" se usa sin otros especificadores de formato personalizado, se interpreta como el especificador de formato de fecha y hora estándar "g". + + property accessor + property accessor + + range variable variable de rango @@ -3174,11 +3189,6 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p Actualizar la cláusula Implements de un '{0}' impedirá que continúe la sesión de depuración. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Cambiar la restricción de '{0}' a '{1}' impedirá que continúe la sesión de depuración. - - Updating the variance of '{0}' will prevent the debug session from continuing. Actualizar la varianza de '{0}' impedirá que continúe la sesión de depuración. @@ -3224,11 +3234,6 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p Actualizar el tipo de un descriptor de acceso de propiedad/evento impedirá que continúe la sesión de depuración. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Actualizar el tipo de un método (Sub/Function) impedirá que continúe la sesión de depuración. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Actualizar el nombre de biblioteca de Declarar instrucción impedirá que continúe la sesión de depuración. @@ -3843,6 +3848,11 @@ Para los valores de DateTimeOffset, el especificador de formato "K" es equivalen Si el especificador de formato "K" se usa sin otros especificadores de formato personalizado, se interpreta como un especificador de formato de fecha y hora estándar y produce una FormatException. + + type + type + + type constraint restricción de tipo diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index f88bebc705fb0..f92ccdedc18bf 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -110,11 +110,6 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai L'ajout de '{0}' à une interface empêche la session de débogage de se poursuivre. - - Adding a positional parameter to a record will prevent the debug session from continuing. - L’ajout d’un paramètre positionnel à un enregistrement empêche la session de débogage de se poursuivre. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. L'ajout d'une méthode avec un spécificateur d'interface explicite empêche la session de débogage de continuer. @@ -140,6 +135,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Les conditions d'alternance n'effectuent pas de capture et ne peuvent pas être nommées This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Appliquer les préférences d'en-tête de fichier @@ -235,6 +235,21 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Le remplacement de '{0}' par '{1}' va empêcher la session de débogage de se poursuivre, car cela change la forme de la machine à états. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Le changement de visibilité de {0} empêche la session de débogage de se poursuivre. @@ -320,11 +335,6 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Créer et affecter ce qui reste en tant que propriétés - - Deleting a positional parameter from a record will prevent the debug session from continuing. - La suppression d’un paramètre positionnel d’un enregistrement empêche la session de débogage de se poursuivre. - - Do not change this code. Put cleanup code in '{0}' method Ne changez pas ce code. Placez le code de nettoyage dans la méthode '{0}' @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Si le spécificateur de format "g" est utilisé sans autres spécificateurs de format personnalisés, il est interprété en tant que spécificateur de format de date et d'heure standard : "g". + + property accessor + property accessor + + range variable variable de plage @@ -3174,11 +3189,6 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f La mise à jour de la clause Implements de '{0}' empêche la session de débogage de se poursuivre. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Le remplacement de la contrainte '{0}' en '{1}' empêche la session de débogage de se poursuivre. - - Updating the variance of '{0}' will prevent the debug session from continuing. La mise à jour de la variance de '{0}' empêche la session de débogage de se poursuivre. @@ -3224,11 +3234,6 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f La mise à jour du genre d'un accesseur de propriété/événement empêche la session de débogage de se poursuivre. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - La mise à jour du genre d'une méthode (Sub/Function) empêche la session de débogage de se poursuivre. - - Updating the library name of Declare Statement will prevent the debug session from continuing. La mise à jour du nom de la bibliothèque de l'instruction Declare empêche la session de débogage de se poursuivre. @@ -3843,6 +3848,11 @@ Pour les valeurs DateTimeOffset, le spécificateur de format "K" est équivalent Si le spécificateur de format "K" est utilisé sans autres spécificateurs de format personnalisés, il est interprété en tant que spécificateur de format de date et d'heure standard, et lève une exception FormatException. + + type + type + + type constraint contrainte de type diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index b3debbd02411a..9d13a938e5066 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -110,11 +110,6 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se si aggiunge '{0}' in un'interfaccia, la sessione di debug non potrà continuare. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Se si aggiunge un parametro posizionale a un record, la sessione di debug non potrà continuare. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Se si aggiunge un metodo con identificatore di interfaccia esplicita, la sessione di debug non potrà continuare. @@ -140,6 +135,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Le condizioni di alternanza non consentono l'acquisizione e non possono essere denominate This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Applica le preferenze relative alle intestazioni di file @@ -235,6 +235,21 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se si modifica '{0}' in '{1}', la sessione di debug non potrà continuare perché modifica la forma della macchina a stati. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Se si modifica la visibilità di {0}, la sessione di debug non potrà continuare. @@ -320,11 +335,6 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Crea e assegna rimanenti come proprietà - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Se si elimina un parametro posizionale da un record, la sessione di debug non potrà continuare. - - Do not change this code. Put cleanup code in '{0}' method Non modificare questo codice. Inserire il codice di pulizia nel metodo '{0}' @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Se l'identificatore di formato "g" viene usato senza altri identificatori di formato personalizzati, viene interpretato come l'identificatore di formato di data e ora standard "g". + + property accessor + property accessor + + range variable variabile di intervallo @@ -3174,11 +3189,6 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for Se si aggiorna la clausola Implements di un elemento '{0}', la sessione di debug non potrà continuare. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Se si modifica il vincolo da '{0}' a '{1}', la sessione di debug non potrà continuare. - - Updating the variance of '{0}' will prevent the debug session from continuing. Se si aggiorna la varianza di '{0}', la sessione di debug non potrà continuare. @@ -3224,11 +3234,6 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for Se si aggiorna il tipo di una funzione di accesso a proprietà/eventi, la sessione di debug non potrà continuare. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Se si aggiorna il tipo di un metodo (Sub/Function), la sessione di debug non potrà continuare. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Se si aggiorna il nome della libreria dell'istruzione Declare, la sessione di debug non potrà continuare. @@ -3843,6 +3848,11 @@ Per i valori DateTimeOffset, l'identificatore di formato "K" è equivalente all' Se l'identificatore di formato "K" viene usato senza altri identificatori di formato personalizzati, viene interpretato come l'identificatore di formato di data e ora standard e viene generato un evento FormatException. + + type + type + + type constraint vincolo di tipo diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 670e4fa05740a..66d41c2fd01fe 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -110,11 +110,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma '{0}' をインターフェイスに追加すると、デバッグ セッションは続行されません。 - - Adding a positional parameter to a record will prevent the debug session from continuing. - レコードに位置パラメーターを追加すると、デバッグ セッションが続行されなくなります。 - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. 明示的なインターフェイス指定子が含まれるメソッドを追加すると、デバッグ セッションを続行できなくなります。 @@ -140,6 +135,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 選択条件が捕捉されないため、名前を指定できません This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences ファイル ヘッダーの基本設定を適用する @@ -235,6 +235,21 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma '{0}' から '{1}' に変更すると、ステート マシンの形状が変わるため、デバッグ セッションは続行されません。 + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. {0} の表示範囲を変更すると、デバッグ セッションを続行できなくなります。 @@ -320,11 +335,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 残りをプロパティとして作成して割り当てる - - Deleting a positional parameter from a record will prevent the debug session from continuing. - レコードから位置パラメーターを削除すると、デバッグ セッションが続行されなくなります。 - - Do not change this code. Put cleanup code in '{0}' method このコードを変更しないでください。クリーンアップ コードを '{0}' メソッドに記述します @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's 他のカスタム書式指定子が使用されていない場合、"g" は標準の日時書式指定子として解釈されます。 + + property accessor + property accessor + + range variable 範囲変数 @@ -3174,11 +3189,6 @@ If the "g" format specifier is used without other custom format specifiers, it's '{0}' の Implements 句を更新すると、デバッグ セッションは続行されません。 - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - 制約を '{0}' から '{1}' に変更すると、デバッグ セッションは続行されません。 - - Updating the variance of '{0}' will prevent the debug session from continuing. '{0}' の変性を更新すると、デバッグ セッションは続行されません。 @@ -3224,11 +3234,6 @@ If the "g" format specifier is used without other custom format specifiers, it's プロパティ/イベント アクセサーの種類を更新すると、デバッグ セッションは続行されません。 - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - メソッドの種類 (Sub/Function) を更新すると、デバッグ セッションは続行されません。 - - Updating the library name of Declare Statement will prevent the debug session from continuing. Declare ステートメントのライブラリ名を更新すると、デバッグ セッションは続行されません。 @@ -3843,6 +3848,11 @@ UTC 時刻の場合 (DateTime.Kind プロパティ値が DateTimeKind.Utc)、書 他のカスタム書式指定子が使用されていない場合、"K" は標準の日時書式指定子として解釈され、FormatException をスローします。 + + type + type + + type constraint 型制約 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 66b63e7857464..4a5730dd76c44 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -110,11 +110,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 인터페이스에 '{0}'을(를) 추가하면 디버그 세션을 계속할 수 없습니다. - - Adding a positional parameter to a record will prevent the debug session from continuing. - 레코드에 위치 매개 변수를 추가하면 디버그 세션이 계속 진행되지 않습니다. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. 명시적 인터페이스 지정자를 사용하여 메서드를 추가하면 디버그 세션을 계속할 수 없습니다. @@ -140,6 +135,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 교체 조건은 캡처하지 않고 이름을 지정할 수 없습니다. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences 파일 헤더 기본 설정 적용 @@ -235,6 +235,21 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma '{0}'을(를) '{1}'(으)로 변경하면 상태 시스템의 셰이프가 변경되므로 디버그 세션을 계속할 수 없습니다. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. {0} 표시 여부를 변경하면 디버그 세션을 계속할 수 없습니다. @@ -320,11 +335,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 나머지를 만들고 속성으로 할당합니다. - - Deleting a positional parameter from a record will prevent the debug session from continuing. - 레코드에서 위치 매개 변수를 삭제하면 디버그 세션이 계속 진행되지 않습니다. - - Do not change this code. Put cleanup code in '{0}' method 이 코드를 변경하지 마세요. '{0}' 메서드에 정리 코드를 입력합니다. @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's 다른 사용자 지정 형식 지정자 없이 "g" 형식 지정자를 사용하면 "g" 표준 날짜 및 시간 형식 지정자로 해석됩니다. + + property accessor + property accessor + + range variable 범위 변수 @@ -3174,11 +3189,6 @@ If the "g" format specifier is used without other custom format specifiers, it's '{0}'의 Implements 절을 업데이트하면 디버그 세션을 계속할 수 없습니다. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - '{0}'에서 '{1}'(으)로 제약 조건을 변경하면 디버그 세션을 계속할 수 없습니다. - - Updating the variance of '{0}' will prevent the debug session from continuing. '{0}'의 가변성(variance)을 업데이트하면 디버그 세션을 계속할 수 없습니다. @@ -3224,11 +3234,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 속성/이벤트 접근자 종류를 업데이트하면 디버그 세션을 계속할 수 없습니다. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - 메서드(Sub/Function) 종류를 업데이트하면 디버그 세션을 계속할 수 없습니다. - - Updating the library name of Declare Statement will prevent the debug session from continuing. 선언 문의 라이브러리 이름을 업데이트하면 디버그 세션을 계속할 수 없습니다. @@ -3843,6 +3848,11 @@ DateTimeOffset 값의 경우, "K" 형식 지정자는 "zzz" 형식 지정자와 다른 사용자 지정 형식 지정자 없이 "K" 형식 지정자를 사용하면 표준 날짜 및 시간 형식 지정자로 해석되어 FormatException을 throw합니다. + + type + type + + type constraint 형식 제약 조건 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 53e1ce23a705a..66f37d7e2207f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -110,11 +110,6 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Dodanie elementu „{0}” do interfejsu uniemożliwi kontynuowanie sesji debugowania. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Dodanie parametru pozycyjnego do rekordu uniemożliwi kontynuowanie sesji debugowania. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Dodanie metody z jawnym specyfikatorem interfejsu uniemożliwi kontynuowanie sesji debugowania. @@ -140,6 +135,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Warunki alternatywne nie przechwytują i nie mogą być nazywane This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Zastosuj preferencje nagłówka pliku @@ -235,6 +235,21 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Zmiana elementu „{0}” na „{1}” uniemożliwi kontynuowanie sesji debugowania, ponieważ powoduje zmianę kształtu automatu stanów. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Zmiana widoczności elementu {0} uniemożliwi kontynuowanie sesji debugowania. @@ -320,11 +335,6 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Utwórz i przypisz pozostałe jako właściwości - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Usunięcie parametru pozycyjnego z rekordu uniemożliwi kontynuowanie sesji debugowania. - - Do not change this code. Put cleanup code in '{0}' method Nie zmieniaj tego kodu. Umieść kod czyszczący w metodzie „{0}”. @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych specyfikatorów formatu, jest on interpretowany jako standardowy specyfikator formatu daty i godziny „g”. + + property accessor + property accessor + + range variable zmienna zakresu @@ -3174,11 +3189,6 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp Aktualizacja klauzuli Implements elementu „{0}” uniemożliwi kontynuowanie sesji debugowania. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Zmiana ograniczenia z „{0}” na „{1}” uniemożliwi kontynuowanie sesji debugowania. - - Updating the variance of '{0}' will prevent the debug session from continuing. Aktualizacja wariancji elementu „{0}” uniemożliwi kontynuowanie sesji debugowania. @@ -3224,11 +3234,6 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp Aktualizacja rodzaju metody dostępu do właściwości lub zdarzenia uniemożliwi kontynuowanie sesji debugowania. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Aktualizacja rodzaju metody (Sub/Function) uniemożliwi kontynuowanie sesji debugowania. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Aktualizacja nazwy biblioteki instrukcji Declare uniemożliwi kontynuowanie sesji debugowania. @@ -3843,6 +3848,11 @@ W przypadku wartości typu DateTimeOffset specyfikator formatu „K” jest rów Jeśli specyfikator formatu „K” jest używany bez innych niestandardowych specyfikatorów formatu, jest on interpretowany jako standardowy specyfikator formatu daty i godziny i powoduje zgłoszenie wyjątku FormatException. + + type + type + + type constraint ograniczenie typu diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 379852007521a..abb54b4699b88 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -110,11 +110,6 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Adicionar '{0}' a uma interface impedirá que a sessão de depuração continue. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Adicionar um parâmetro de posição a um registro impedirá que a sessão de depuração continue. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Adicionar um método com um especificador explícito da interface impedirá a sessão de depuração de continuar. @@ -140,6 +135,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Condições de alternância não capturam e não podem ser nomeadas This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Aplicar as preferências de cabeçalho de arquivo @@ -235,6 +235,21 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Alterar '{0}' para '{1}' impedirá a continuação da sessão de depuração porque altera a forma da máquina de estado. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. A alteração da visibilidade de {0} impedirá que a sessão de depuração continue. @@ -320,11 +335,6 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Criar e atribuir os restantes como propriedades - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Excluir um parâmetro de posição de um registro impedirá que a sessão de depuração continue. - - Do not change this code. Put cleanup code in '{0}' method Não altere este código. Coloque o código de limpeza no método '{0}' @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Se o especificador de formato "g" for usado sem outros especificadores de formato personalizado, ele será interpretado como o especificador de formato padrão de data e hora "g". + + property accessor + property accessor + + range variable variável de intervalo @@ -3174,11 +3189,6 @@ Se o especificador de formato "g" for usado sem outros especificadores de format Atualizar a cláusula Implements "{0}" impedirá que a sessão de depuração continue. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Alterar a restrição de "{0}" para "{1}" impedirá que a sessão de depuração continue. - - Updating the variance of '{0}' will prevent the debug session from continuing. Atualizar a variância de "{0}" impedirá que a sessão de depuração continue. @@ -3224,11 +3234,6 @@ Se o especificador de formato "g" for usado sem outros especificadores de format Atualizar o tipo de um acessador de propriedades/eventos impedirá que a sessão de depuração continue. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Atualizar o tipo de um método (Sub/Função) impedirá que a sessão de depuração continue. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Atualizar o nome da biblioteca de Instrução Declare impedirá que a sessão de depuração continue. @@ -3843,6 +3848,11 @@ Para os valores de DateTimeOffset, o especificador de formato "K" é equivalente Se o especificador de formato "K" for usado sem outros especificadores de formato personalizado, ele será interpretado como um especificador de formato padrão de data e hora e gerará uma FormatException. + + type + type + + type constraint restrição de tipo diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 7692686a359ca..11e679e57d815 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -110,11 +110,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Добавление "{0}" в интерфейс сделает продолжение сеанса отладки невозможным. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Добавление позиционного параметра в запись остановит сеанс отладки. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. При добавлении метода с явным спецификатором интерфейса вы не сможете продолжить сеанс отладки. @@ -140,6 +135,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Условия чередования не выполняют запись, и им невозможно присвоить имя This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Применить параметры заголовка файла @@ -235,6 +235,21 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Изменение "{0}" на "{1}" сделает продолжение сеанса отладки невозможным, так как меняет форму конечной машины. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Изменение видимости {0} сделает продолжение сеанса отладки невозможным. @@ -320,11 +335,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Создать и назначить оставшиеся как свойства - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Удаление позиционного параметра из записи остановит сеанс отладки. - - Do not change this code. Put cleanup code in '{0}' method Не изменяйте этот код. Разместите код очистки в методе "{0}". @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's Если описатель формата "g" используется без других описателей пользовательского формата, он интерпретируется как описатель "g" стандартного формата даты и времени. + + property accessor + property accessor + + range variable переменная диапазона @@ -3174,11 +3189,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Обновление предложения Implements объекта "{0}" сделает продолжение сеанса отладки невозможным. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Изменение ограничения с "{0}" на "{1}" сделает продолжение сеанса отладки невозможным. - - Updating the variance of '{0}' will prevent the debug session from continuing. Обновление вариантности объекта "{0}" сделает продолжение сеанса отладки невозможным. @@ -3224,11 +3234,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Обновление типа метода доступа для свойства или события сделает продолжение сеанса отладки невозможным. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Обновление типа метода (Sub/Function) сделает продолжение сеанса отладки невозможным. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Обновление имени библиотеки оператора Declare сделает продолжение сеанса отладки невозможным. @@ -3843,6 +3848,11 @@ If the "K" format specifier is used without other custom format specifiers, it's Если описатель формата "K" используется без других описателей пользовательского формата, он интерпретируется как описатель стандартного формата даты и времени и вызывает исключение FormatException. + + type + type + + type constraint ограничение типа diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 77600d5869695..f640b1efd5fb7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -110,11 +110,6 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Arabirime '{0}' eklenmesi, hata ayıklama oturumunun devam etmesini engeller. - - Adding a positional parameter to a record will prevent the debug session from continuing. - Bir kayda konumsal bir parametre eklenmesi hata ayıklama oturumunun devam etmesini engeller. - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. Bir yöntem ile bir açık arabirim belirleyici ekleme hata ayıklama oturumu devam etmesini engeller. @@ -140,6 +135,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Değişim koşulları yakalamak değil ve adlandırılamaz This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences Dosya üst bilgisi tercihlerini uygula @@ -235,6 +235,21 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be '{0}' öğesini '{1}' olarak değiştirmek, durum makinesinin şeklini değiştirdiğinden hata ayıklama oturumunun devam etmesini engeller. + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. {0} öğesinin görünürlüğünü değiştirmek, hata ayıklama oturumunun devam etmesini engeller. @@ -320,11 +335,6 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Kalanları özellik olarak oluştur ve ata - - Deleting a positional parameter from a record will prevent the debug session from continuing. - Bir konumsal parametrenin bir kayıttan silinmesi hata ayıklama oturumunun devam etmesini engeller. - - Do not change this code. Put cleanup code in '{0}' method Bu kodu değiştirmeyin. Temizleme kodunu '{0}' metodunun içine yerleştirin. @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's "g" biçim belirticisi başka özel biçim belirticileri olmadan kullanılırsa, "g" standart tarih ve saat biçimi belirticisi olarak yorumlanır. + + property accessor + property accessor + + range variable aralık değişkeni @@ -3174,11 +3189,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Bir '{0}' öğesinin Implements yan tümcesini güncelleştirme, hata ayıklama oturumunun devam etmesini engelleyecek. - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - Kısıtlamayı '{0}' yerine '{1}' olarak değiştirmek hata ayıklama oturumunun devam etmesini engelleyecek. - - Updating the variance of '{0}' will prevent the debug session from continuing. '{0}' öğesinin farkını güncelleştirme, hata ayıklama oturumunun devam etmesini engelleyecek. @@ -3224,11 +3234,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Özellik/olay erişimcisinin türünü güncelleştirme, hata ayıklama oturumunun devam etmesini engelleyecek. - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - Bir yöntemin (Sub/Function) türünü güncelleştirme, hata ayıklama oturumunun devam etmesini engelleyecek. - - Updating the library name of Declare Statement will prevent the debug session from continuing. Declare Deyimi'nin kitaplık adını güncelleştirme, hata ayıklama oturumunun devam etmesini engelleyecek. @@ -3843,6 +3848,11 @@ DateTimeOffset değerleri için "K" biçim belirticisi "zzz" biçim belirticisin "K" biçim belirticisi başka özel biçim belirticileri olmadan kullanılırsa, standart bir tarih ve saat biçimi belirticisi olarak yorumlanır ve bir FormatException oluşturur. + + type + type + + type constraint tür kısıtlaması diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index cfefe8521ca2e..eeb8e3f0073fe 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -110,11 +110,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 将 "{0}" 添加进接口将阻止调试会话继续。 - - Adding a positional parameter to a record will prevent the debug session from continuing. - 将位置参数添加到记录将阻止调试会话继续。 - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. 添加具有显式接口说明符的方法将阻止调试会话继续。 @@ -140,6 +135,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替换条件不捕获且不能命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences 应用文件头首选项 @@ -235,6 +235,21 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 将“{0}”更改为“{1}”会阻止调试会话继续执行,因为它会更改状态机的形状。 + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. 更改 {0} 的可见性将会中止调试会话。 @@ -320,11 +335,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 创建其余部分并赋值为属性 - - Deleting a positional parameter from a record will prevent the debug session from continuing. - 从记录中删除位置参数将阻止调试会话继续。 - - Do not change this code. Put cleanup code in '{0}' method 不要更改此代码。请将清理代码放入“{0}”方法中 @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's 如果使用 "g" 格式说明符而没有其他自定义格式说明符,则该说明符将被解释为 "g" 标准日期和时间格式说明符。 + + property accessor + property accessor + + range variable 范围变量 @@ -3174,11 +3189,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 更新“{0}”的 Implements 子句将阻止调试会话继续。 - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - 将约束从“{0}”更改为“{1}” 将阻止调试会话继续。 - - Updating the variance of '{0}' will prevent the debug session from continuing. 更新“{0}”的变型将阻止调试会话继续。 @@ -3224,11 +3234,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 更新一个属性/事件访问器的种类将阻止调试会话继续。 - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - 更新方法 (Sub/Function) 的种类将阻止调试会话继续。 - - Updating the library name of Declare Statement will prevent the debug session from continuing. 更新 Declare 语句的库名将阻止调试会话继续。 @@ -3843,6 +3848,11 @@ If the "K" format specifier is used without other custom format specifiers, it's 如果使用 "K" 格式说明符而没有其他自定义格式说明符,则该说明符将被解释为标准日期和时间格式说明符,并引发 FormatException。 + + type + type + + type constraint 类型约束 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 01fba5962147e..7bfbd66b92237 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -110,11 +110,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 將 '{0}' 新增至介面,會造成偵錯工作階段無法繼續。 - - Adding a positional parameter to a record will prevent the debug session from continuing. - 若將位置參數新增至記錄,將會使偵錯工作階段無法繼續。 - - Adding a method with an explicit interface specifier will prevent the debug session from continuing. 新增具有明確介面指定名稱的方法會導致偵錯工作階段無法繼續。 @@ -140,6 +135,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替代條件不會擷取,也無法命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + An update that causes the return type of the implicit Main method to change will prevent the debug session from continuing. + + Apply file header preferences 套用檔案標題的喜好設定 @@ -235,6 +235,21 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 將 '{0}' 變更為 '{1}' 會變更狀態機器的圖形,進而使偵錯工作階段無法繼續。 + + Changing constraints of '{0}' will prevent the debug session from continuing. + Changing constraints of '{0}' will prevent the debug session from continuing. + + + + Changing parameter types of '{0}' will prevent the debug session from continuing. + Changing parameter types of '{0}' will prevent the debug session from continuing. + + + + Changing type parameters of '{0}' will prevent the debug session from continuing. + Changing type parameters of '{0}' will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. 變更 {0} 的可見度會造成偵錯工作階段無法繼續。 @@ -320,11 +335,6 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 建立其餘項目並將其指派為屬性 - - Deleting a positional parameter from a record will prevent the debug session from continuing. - 若從記錄刪除位置參數,將會使偵錯工作階段無法繼續。 - - Do not change this code. Put cleanup code in '{0}' method 請勿變更此程式碼。請將清除程式碼放入 '{0}' 方法 @@ -3014,6 +3024,11 @@ If the "g" format specifier is used without other custom format specifiers, it's 如果使用 "g" 格式規範,而且沒有其他自訂格式規範,則會將其解譯為 "g" 標準日期和時間格式規範。 + + property accessor + property accessor + + range variable 範圍變數 @@ -3174,11 +3189,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 更新 '{0}' 的 Implements 子句,會造成偵錯工作階段無法繼續。 - - Changing the constraint from '{0}' to '{1}' will prevent the debug session from continuing. - 將條件約束從 '{0}' 變更為 '{1}',會造成偵錯工作階段無法繼續。 - - Updating the variance of '{0}' will prevent the debug session from continuing. 更新 '{0}' 的變異數,會造成偵錯工作階段無法繼續。 @@ -3224,11 +3234,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 更新屬性/事件存取子的種類,會造成偵錯工作階段無法繼續。 - - Updating the kind of a method (Sub/Function) will prevent the debug session from continuing. - 更新方法種類 (Sub/Function),會造成偵錯工作階段無法繼續。 - - Updating the library name of Declare Statement will prevent the debug session from continuing. 更新 Declare 陳述式的程式庫名稱,會造成偵錯工作階段無法繼續。 @@ -3843,6 +3848,11 @@ If the "K" format specifier is used without other custom format specifiers, it's 如果使用 "K" 格式規範,而且沒有其他自訂格式規範,則會將其解譯為標準日期和時間格式規範,並擲回 FormatException。 + + type + type + + type constraint 類型條件約束 diff --git a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs index 95cc255a83d45..001b1d57d3e47 100644 --- a/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs +++ b/src/Features/LanguageServer/Protocol/CSharpVisualBasicLanguageServerFactory.cs @@ -14,14 +14,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer [Export(typeof(ILanguageServerFactory)), Shared] internal class CSharpVisualBasicLanguageServerFactory : ILanguageServerFactory { - public const string UserVisibleName = "C#/Visual Basic Language Server Client"; + public const string UserVisibleName = "Roslyn Language Server Client"; - private readonly CSharpVisualBasicRequestDispatcherFactory _dispatcherFactory; + private readonly RequestDispatcherFactory _dispatcherFactory; private readonly IAsynchronousOperationListenerProvider _listenerProvider; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpVisualBasicLanguageServerFactory(CSharpVisualBasicRequestDispatcherFactory dispatcherFactory, + public CSharpVisualBasicLanguageServerFactory(RequestDispatcherFactory dispatcherFactory, IAsynchronousOperationListenerProvider listenerProvider) { _dispatcherFactory = dispatcherFactory; diff --git a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs index e694e67dabf45..6d6893c65a813 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Diagnostics/AbstractPullDiagnosticHandler.cs @@ -308,9 +308,12 @@ private VSDiagnostic ConvertDiagnostic(Document document, SourceText text, Diagn var project = document.Project; - // Razor wants to handle all span mapping themselves. So if we are in razor, return the raw doc spans, and - // do not map them. - var useMappedSpan = !document.IsRazorDocument(); + // We currently do not map diagnostics spans as + // 1. Razor handles span mapping for razor files on their side. + // 2. LSP does not allow us to report document pull diagnostics for a different file path. + // 3. The VS LSP client does not support document pull diagnostics for files outside our content type. + // 4. This matches classic behavior where we only squiggle the original location anyway. + var useMappedSpan = false; return new VSDiagnostic { Source = GetType().Name, diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs index c269ebb7446a2..135274386d842 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestContext.cs @@ -26,6 +26,7 @@ public static RequestContext Create( TextDocumentIdentifier? textDocument, string? clientName, ILspLogger _logger, + RequestTelemetryLogger telemetryLogger, ClientCapabilities clientCapabilities, ILspWorkspaceRegistrationService lspWorkspaceRegistrationService, Dictionary? solutionCache, @@ -50,7 +51,7 @@ public static RequestContext Create( // There are multiple possible solutions that we could be interested in, so we need to find the document // first and then get the solution from there. If we're not given a document, this will return the default // solution - document = FindDocument(_logger, lspWorkspaceRegistrationService, textDocument, clientName); + document = FindDocument(_logger, telemetryLogger, lspWorkspaceRegistrationService, textDocument, clientName); if (document is not null) { @@ -84,7 +85,12 @@ public static RequestContext Create( return new RequestContext(lspSolution, _logger.TraceInformation, clientCapabilities, clientName, document, documentChangeTracker); } - private static Document? FindDocument(ILspLogger logger, ILspWorkspaceRegistrationService lspWorkspaceRegistrationService, TextDocumentIdentifier textDocument, string? clientName) + private static Document? FindDocument( + ILspLogger logger, + RequestTelemetryLogger telemetryLogger, + ILspWorkspaceRegistrationService lspWorkspaceRegistrationService, + TextDocumentIdentifier textDocument, + string? clientName) { logger.TraceInformation($"Finding document corresponding to {textDocument.Uri}"); @@ -98,14 +104,7 @@ public static RequestContext Create( { var document = documents.FindDocumentInProjectContext(textDocument); logger.TraceInformation($"Found document in workspace {workspace.Kind}: {document.FilePath}"); - - Logger.Log(FunctionId.FindDocumentInWorkspace, KeyValueLogMessage.Create(LogType.Trace, m => - { - m["WorkspaceKind"] = workspace.Kind; - m["FoundInWorkspace"] = true; - m["DocumentUriHashCode"] = textDocument.Uri.GetHashCode(); - })); - + telemetryLogger.UpdateFindDocumentTelemetryData(success: true, workspace.Kind); return document; } } @@ -113,12 +112,7 @@ public static RequestContext Create( var searchedWorkspaceKinds = string.Join(";", workspaceKinds.ToImmutableAndClear()); logger.TraceWarning($"No document found after looking in {searchedWorkspaceKinds} workspaces, but request did contain a document uri"); - Logger.Log(FunctionId.FindDocumentInWorkspace, KeyValueLogMessage.Create(LogType.Trace, m => - { - m["AvailableWorkspaceKinds"] = searchedWorkspaceKinds; - m["FoundInWorkspace"] = false; - m["DocumentUriHashCode"] = textDocument.Uri.GetHashCode(); - })); + telemetryLogger.UpdateFindDocumentTelemetryData(success: false, workspaceKind: null); return null; } diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs index ac02a539e0d4a..e7eab26087ea7 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.RequestTelemetryLogger.cs @@ -1,9 +1,11 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Internal.Log; @@ -42,10 +44,13 @@ internal class RequestTelemetryLogger : IDisposable /// private readonly ConcurrentDictionary _requestCounters; + private readonly LogAggregator _findDocumentResults; + public RequestTelemetryLogger(string serverTypeName) { _serverTypeName = serverTypeName; - _requestCounters = new ConcurrentDictionary(); + _requestCounters = new(); + _findDocumentResults = new(); // Buckets queued duration into 10ms buckets with the last bucket starting at 1000ms. // Queue times are relatively short and fall under 50ms, so tracking past 1000ms is not useful. @@ -56,7 +61,21 @@ public RequestTelemetryLogger(string serverTypeName) _requestDurationLogAggregator = new HistogramLogAggregator(bucketSize: 1, maxBucketValue: 40); } - public void UpdateTelemetryData(string methodName, TimeSpan queuedDuration, TimeSpan requestDuration, Result result) + public void UpdateFindDocumentTelemetryData(bool success, string? workspaceKind) + { + var workspaceKindTelemetryProperty = success ? workspaceKind : "Failed"; + + if (workspaceKindTelemetryProperty != null) + { + _findDocumentResults.IncreaseCount(workspaceKindTelemetryProperty); + } + } + + public void UpdateTelemetryData( + string methodName, + TimeSpan queuedDuration, + TimeSpan requestDuration, + Result result) { // Find the bucket corresponding to the queued duration and update the count of durations in that bucket. // This is not broken down per method as time in queue is not specific to an LSP method. @@ -121,6 +140,16 @@ public void Dispose() })); } + Logger.Log(FunctionId.LSP_FindDocumentInWorkspace, KeyValueLogMessage.Create(LogType.Trace, m => + { + m["server"] = _serverTypeName; + foreach (var kvp in _findDocumentResults) + { + var info = kvp.Key.ToString(); + m[info] = kvp.Value.GetCount(); + } + })); + // Clear telemetry we've published in case dispose is called multiple times. _requestCounters.Clear(); _queuedDurationLogAggregator = null; diff --git a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs index a0bb3496df8ec..ac32aae98a1ad 100644 --- a/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs +++ b/src/Features/LanguageServer/Protocol/Handler/RequestExecutionQueue.cs @@ -288,6 +288,7 @@ private RequestContext CreateRequestContext(QueueItem queueItem, out Workspace w queueItem.TextDocument, queueItem.ClientName, _logger, + _requestTelemetryLogger, queueItem.ClientCapabilities, _workspaceRegistrationService, _lspSolutionCache, diff --git a/src/Features/LanguageServer/Protocol/CSharpVisualBasicRequestDispatcherFactory.cs b/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs similarity index 64% rename from src/Features/LanguageServer/Protocol/CSharpVisualBasicRequestDispatcherFactory.cs rename to src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs index 7d409d74762e0..e4829d282094e 100644 --- a/src/Features/LanguageServer/Protocol/CSharpVisualBasicRequestDispatcherFactory.cs +++ b/src/Features/LanguageServer/Protocol/RequestDispatcherFactory.cs @@ -11,12 +11,12 @@ namespace Microsoft.CodeAnalysis.LanguageServer { [Shared] - [Export(typeof(CSharpVisualBasicRequestDispatcherFactory))] - internal sealed class CSharpVisualBasicRequestDispatcherFactory : AbstractRequestDispatcherFactory + [Export(typeof(RequestDispatcherFactory))] + internal sealed class RequestDispatcherFactory : AbstractRequestDispatcherFactory { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpVisualBasicRequestDispatcherFactory([ImportMany] IEnumerable> requestHandlerProviders) + public RequestDispatcherFactory([ImportMany] IEnumerable> requestHandlerProviders) : base(requestHandlerProviders) { } diff --git a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 4e031801aadc8..b8cc8b5072383 100644 --- a/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/Features/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -245,6 +245,28 @@ public async Task TestDocumentDiagnosticsRemainAfterErrorIsNotFixed() Assert.Equal(new Position { Line = 0, Character = 10 }, results[0].Diagnostics.Single().Range.Start); } + [Fact] + public async Task TestDocumentDiagnosticsAreNotMapped() + { + var markup = +@"#line 1 ""test.txt"" +class A {"; + using var testLspServer = CreateTestWorkspaceWithDiagnostics(markup, BackgroundAnalysisScope.OpenFilesAndProjects); + + // Calling GetTextBuffer will effectively open the file. + testLspServer.TestWorkspace.Documents.Single().GetTextBuffer(); + + var document = testLspServer.GetCurrentSolution().Projects.Single().Documents.Single(); + + await OpenDocumentAsync(testLspServer, document); + + var results = await RunGetDocumentPullDiagnosticsAsync( + testLspServer, document.GetURI()); + + Assert.Equal("CS1513", results.Single().Diagnostics.Single().Code); + Assert.Equal(1, results.Single().Diagnostics.Single().Range.Start.Line); + } + private static async Task InsertTextAsync( TestLspServer testLspServer, Document document, @@ -522,6 +544,25 @@ public async Task TestStreamingWorkspaceDiagnostics() Assert.Equal("CS1513", progress.GetValues()![0].Diagnostics![0].Code); } + [Fact] + public async Task TestWorkspaceDiagnosticsAreNotMapped() + { + var markup1 = +@"#line 1 ""test.txt"" +class A {"; + var markup2 = ""; + using var testLspServer = CreateTestWorkspaceWithDiagnostics( + new[] { markup1, markup2 }, BackgroundAnalysisScope.FullSolution); + + var results = await RunGetWorkspacePullDiagnosticsAsync(testLspServer); + + Assert.Equal(2, results.Length); + Assert.Equal(new Uri("C:/test1.cs"), results[0].TextDocument!.Uri); + Assert.Equal("CS1513", results[0].Diagnostics.Single().Code); + Assert.Equal(1, results[0].Diagnostics.Single().Range.Start.Line); + Assert.Empty(results[1].Diagnostics); + } + #endregion private static async Task RunGetDocumentPullDiagnosticsAsync( diff --git a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb index da9d187d8a6a3..ce65e2060dbc7 100644 --- a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb +++ b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb @@ -604,13 +604,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Return GetPermutedDocCommentTrivia(document, node, permutedParamNodes) End Function - Private Shared Function VerifyAndPermuteParamNodes(paramNodes As ImmutableArray(Of XmlElementSyntax), declarationSymbol As ISymbol, updatedSignature As SignatureChange) As ImmutableArray(Of SyntaxNode) + Private Function VerifyAndPermuteParamNodes(paramNodes As ImmutableArray(Of XmlElementSyntax), declarationSymbol As ISymbol, updatedSignature As SignatureChange) As ImmutableArray(Of SyntaxNode) ' Only reorder if count and order match originally. Dim originalParameters = updatedSignature.OriginalConfiguration.ToListOfParameters() Dim reorderedParameters = updatedSignature.UpdatedConfiguration.ToListOfParameters() - Dim declaredParameters = declarationSymbol.GetParameters() + Dim declaredParameters = GetParameters(declarationSymbol) If paramNodes.Length <> declaredParameters.Length Then Return ImmutableArray(Of SyntaxNode).Empty End If @@ -755,5 +755,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Protected Overrides Function CommaTokenWithElasticSpace() As SyntaxToken Return Token(SyntaxKind.CommaToken).WithTrailingTrivia(ElasticSpace) End Function + + Protected Overrides Function TryGetRecordPrimaryConstructor(typeSymbol As INamedTypeSymbol, ByRef primaryConstructor As IMethodSymbol) As Boolean + Return False + End Function + + Protected Overrides Function GetParameters(declarationSymbol As ISymbol) As ImmutableArray(Of IParameterSymbol) + Return declarationSymbol.GetParameters() + End Function End Class End Namespace diff --git a/src/Features/VisualBasic/Portable/CodeFixes/GenerateEndConstruct/GenerateEndConstructCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/GenerateEndConstruct/GenerateEndConstructCodeFixProvider.vb index f9de9afa552f9..6ce7418cb3269 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/GenerateEndConstruct/GenerateEndConstructCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/GenerateEndConstruct/GenerateEndConstructCodeFixProvider.vb @@ -259,7 +259,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEndConstruct Inherits CodeAction.DocumentChangeAction Public Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document))) - MyBase.New(title, createChangedDocument) + MyBase.New(title, createChangedDocument, title) End Sub End Class End Class diff --git a/src/Features/VisualBasic/Portable/CodeFixes/IncorrectFunctionReturnType/IncorrectFunctionReturnTypeCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/IncorrectFunctionReturnType/IncorrectFunctionReturnTypeCodeFixProvider.vb index 41b0f61092384..65154b371977c 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/IncorrectFunctionReturnType/IncorrectFunctionReturnTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/IncorrectFunctionReturnType/IncorrectFunctionReturnTypeCodeFixProvider.vb @@ -80,7 +80,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectFunctionReturnTy Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) Dim newRoot = root.ReplaceNode(node, rewrittenNode) Dim newDocument = document.WithSyntaxRoot(newRoot) - Return {New MyCodeAction(VBFeaturesResources.Fix_Incorrect_Function_Return_Type, newDocument)} + Return {New MyCodeAction(newDocument)} End If Return SpecializedCollections.EmptyEnumerable(Of CodeAction)() @@ -89,8 +89,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectFunctionReturnTy Private Class MyCodeAction Inherits CodeAction.DocumentChangeAction - Public Sub New(title As String, newDocument As Document) - MyBase.New(title, Function(c) Task.FromResult(newDocument)) + Public Sub New(newDocument As Document) + MyBase.New(VBFeaturesResources.Fix_Incorrect_Function_Return_Type, Function(c) Task.FromResult(newDocument), NameOf(VBFeaturesResources.Fix_Incorrect_Function_Return_Type)) End Sub End Class End Class diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicChangeToYieldCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicChangeToYieldCodeFixProvider.vb index a1e2bff553774..6e6f5bca36e8a 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicChangeToYieldCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicChangeToYieldCodeFixProvider.vb @@ -43,14 +43,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Iterator root = root.ReplaceNode(returnStatement, yieldStatement) - Return Task.FromResult(Of CodeAction)(New MyCodeAction(VBFeaturesResources.Replace_Return_with_Yield, document.WithSyntaxRoot(root))) + Return Task.FromResult(Of CodeAction)(New MyCodeAction(document.WithSyntaxRoot(root))) End Function Private Class MyCodeAction Inherits CodeAction.DocumentChangeAction - Public Sub New(title As String, newDocument As Document) - MyBase.New(title, Function(c) Task.FromResult(newDocument)) + Public Sub New(newDocument As Document) + MyBase.New(VBFeaturesResources.Replace_Return_with_Yield, Function(c) Task.FromResult(newDocument), NameOf(VBFeaturesResources.Replace_Return_with_Yield)) End Sub End Class End Class diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicConvertToIteratorCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicConvertToIteratorCodeFixProvider.vb index b08cd72878277..38b0605e9848b 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicConvertToIteratorCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Iterator/VisualBasicConvertToIteratorCodeFixProvider.vb @@ -140,7 +140,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Iterator Inherits CodeAction.DocumentChangeAction Public Sub New(title As String, newDocument As Document) - MyBase.New(title, Function(c) Task.FromResult(newDocument)) + MyBase.New(title, Function(c) Task.FromResult(newDocument), title) End Sub End Class End Class diff --git a/src/Features/VisualBasic/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.vb b/src/Features/VisualBasic/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.vb index 58aa2cf2e696f..3aaf1d30cc7cb 100644 --- a/src/Features/VisualBasic/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.vb @@ -60,7 +60,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary End If context.RegisterRefactoring( - New MyCodeAction(VBFeaturesResources.Inline_temporary_variable, Function(c) InlineTemporaryAsync(document, modifiedIdentifier, c)), variableDeclarator.Span) + New MyCodeAction(Function(c) InlineTemporaryAsync(document, modifiedIdentifier, c)), variableDeclarator.Span) End Function Private Shared Function HasConflict( @@ -470,8 +470,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary Private Class MyCodeAction Inherits CodeAction.DocumentChangeAction - Public Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document))) - MyBase.New(title, createChangedDocument) + Public Sub New(createChangedDocument As Func(Of CancellationToken, Task(Of Document))) + MyBase.New(VBFeaturesResources.Inline_temporary_variable, createChangedDocument, NameOf(VBFeaturesResources.Inline_temporary_variable)) End Sub End Class End Class diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb index 29c5befe0a147..192e23a5d194a 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb @@ -338,6 +338,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return nameSyntax?.LocalName.ValueText End Function + Protected Overrides Function GetParameters(symbol As ISymbol) As ImmutableArray(Of IParameterSymbol) + Return symbol.GetParameters() + End Function + Private Shared ReadOnly s_defaultRules As CompletionItemRules = CompletionItemRules.Create( filterCharacterRules:=FilterRules, diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb index 8eaf50ee137bf..19f0ee7dd1a21 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb @@ -72,15 +72,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue TypeParameterList ' tied to parent TypeParameter ' tied to parent - TypeParameterConstraintClause ' tied to parent - TypeConstraint ' tied to parent - TypeKindConstraint ' tied to parent - NewConstraint ' tied to parent ParameterList ' tied to parent Parameter ' tied to parent FieldOrParameterName ' tied to grand-grandparent (type or method declaration) - SimpleAsClause ' tied to parent AttributeList ' tied to parent Attribute ' tied to parent @@ -235,13 +230,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Label.EventStatement, Label.TypeParameterList, Label.TypeParameter, - Label.TypeParameterConstraintClause, - Label.TypeConstraint, - Label.TypeKindConstraint, - Label.NewConstraint, Label.ParameterList, Label.Parameter, - Label.SimpleAsClause, Label.AttributeList, Label.Attribute Return 1 @@ -769,10 +759,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue isLeaf = True Return If(ignoreVariableDeclarations, Label.Ignored, Label.FieldOrParameterName) - Case SyntaxKind.SimpleAsClause - isLeaf = ignoreVariableDeclarations - Return If(ignoreVariableDeclarations, Label.Ignored, Label.SimpleAsClause) - Case SyntaxKind.SubBlock, SyntaxKind.FunctionBlock isLeaf = False Return Label.MethodDeclaration @@ -840,24 +826,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue isLeaf = False Return Label.TypeParameter - Case SyntaxKind.TypeParameterSingleConstraintClause, - SyntaxKind.TypeParameterMultipleConstraintClause - isLeaf = False - Return Label.TypeParameterConstraintClause - - Case SyntaxKind.StructureConstraint, - SyntaxKind.ClassConstraint - isLeaf = True - Return Label.TypeKindConstraint - - Case SyntaxKind.NewConstraint - isLeaf = True - Return Label.NewConstraint - - Case SyntaxKind.TypeConstraint - isLeaf = True - Return Label.TypeConstraint - Case SyntaxKind.ParameterList isLeaf = False Return Label.ParameterList diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 75eab22cc8d3b..48c816486ac67 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -798,6 +798,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue #End Region #Region "Syntax And Semantic Utils" + Protected Overrides Function GetGlobalStatementDiagnosticSpan(node As SyntaxNode) As TextSpan + Return Nothing + End Function Protected Overrides ReadOnly Property LineDirectiveKeyword As String Get @@ -1001,8 +1004,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return node.Parent.FirstAncestorOrSelf(Of TypeBlockSyntax)() ' TODO: EnbumBlock? End Function - Friend Overrides Function TryGetAssociatedMemberDeclaration(node As SyntaxNode) As SyntaxNode - Return If(node.IsParentKind(SyntaxKind.PropertyBlock, SyntaxKind.EventBlock), node.Parent, Nothing) + Friend Overrides Function TryGetAssociatedMemberDeclaration(node As SyntaxNode, ByRef declaration As SyntaxNode) As Boolean + If node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter) Then + Contract.ThrowIfFalse(node.IsParentKind(SyntaxKind.ParameterList, SyntaxKind.TypeParameterList)) + declaration = node.Parent.Parent + Return True + End If + + If node.IsParentKind(SyntaxKind.PropertyBlock, SyntaxKind.EventBlock) Then + declaration = node.Parent + Return True + End If + + declaration = Nothing + Return False End Function Friend Overrides Function HasBackingField(propertyDeclaration As SyntaxNode) As Boolean @@ -1013,7 +1028,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Select Case declaration.Kind Case SyntaxKind.VariableDeclarator Dim declarator = DirectCast(declaration, VariableDeclaratorSyntax) - Return GetInitializerExpression(declarator.Initializer, declarator.AsClause) IsNot Nothing + Return GetInitializerExpression(declarator.Initializer, declarator.AsClause) IsNot Nothing OrElse + declarator.Names.Any(Function(n) n.ArrayBounds IsNot Nothing) Case SyntaxKind.ModifiedIdentifier Debug.Assert(declaration.Parent.IsKind(SyntaxKind.VariableDeclarator) OrElse @@ -1201,52 +1217,52 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue DirectCast(syntaxRefs.Single().GetSyntax(), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword) End Function - Protected Overrides Function GetSymbolsForEdit( + Protected Overrides Function GetSymbolEdits( editKind As EditKind, oldNode As SyntaxNode, newNode As SyntaxNode, oldModel As SemanticModel, newModel As SemanticModel, editMap As IReadOnlyDictionary(Of SyntaxNode, EditKind), - cancellationToken As CancellationToken) As OneOrMany(Of (oldSymbol As ISymbol, newSymbol As ISymbol)) + cancellationToken As CancellationToken) As OneOrMany(Of (oldSymbol As ISymbol, newSymbol As ISymbol, editKind As EditKind)) Dim oldSymbols As OneOrMany(Of ISymbol) = Nothing Dim newSymbols As OneOrMany(Of ISymbol) = Nothing If editKind = EditKind.Delete Then If Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) Then - Return OneOrMany(Of (ISymbol, ISymbol)).Empty + Return OneOrMany(Of (ISymbol, ISymbol, EditKind)).Empty End If - Return oldSymbols.Select(Function(s) New ValueTuple(Of ISymbol, ISymbol)(s, Nothing)) + Return oldSymbols.Select(Function(s) New ValueTuple(Of ISymbol, ISymbol, EditKind)(s, Nothing, editKind)) End If If editKind = EditKind.Insert Then If Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel, newSymbols, cancellationToken) Then - Return OneOrMany(Of (ISymbol, ISymbol)).Empty + Return OneOrMany(Of (ISymbol, ISymbol, EditKind)).Empty End If - Return newSymbols.Select(Function(s) New ValueTuple(Of ISymbol, ISymbol)(Nothing, s)) + Return newSymbols.Select(Function(s) New ValueTuple(Of ISymbol, ISymbol, EditKind)(Nothing, s, editKind)) End If If editKind = EditKind.Update Then If Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) OrElse Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel, newSymbols, cancellationToken) Then - Return OneOrMany(Of (ISymbol, ISymbol)).Empty + Return OneOrMany(Of (ISymbol, ISymbol, EditKind)).Empty End If If oldSymbols.Count = 1 AndAlso newSymbols.Count = 1 Then - Return OneOrMany.Create((oldSymbols(0), newSymbols(0))) + Return OneOrMany.Create((oldSymbols(0), newSymbols(0), editKind)) End If - ' This only occurs when field dentifiers are deleted/inserted/reordered from/to/within their variable declarator list, + ' This only occurs when field identifiers are deleted/inserted/reordered from/to/within their variable declarator list, ' or their shared initializer is updated. The particular inserted and deleted fields will be represented by separate edits, ' but the AsNew clause of the declarator may have been updated as well, which needs to update the remaining (matching) fields. - Dim builder = ArrayBuilder(Of (ISymbol, ISymbol)).GetInstance() + Dim builder = ArrayBuilder(Of (ISymbol, ISymbol, EditKind)).GetInstance() For Each oldSymbol In oldSymbols Dim newSymbol = newSymbols.FirstOrDefault(Function(s, o) CaseInsensitiveComparison.Equals(s.Name, o.Name), oldSymbol) If newSymbol IsNot Nothing Then - builder.Add((oldSymbol, newSymbol)) + builder.Add((oldSymbol, newSymbol, editKind)) End If Next @@ -1263,57 +1279,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ByRef symbols As OneOrMany(Of ISymbol), cancellationToken As CancellationToken) As Boolean - ' Avoid duplicate semantic edits - don't return symbols for statements within blocks. Select Case node.Kind() - Case SyntaxKind.OperatorStatement, - SyntaxKind.SubNewStatement, - SyntaxKind.SetAccessorStatement, - SyntaxKind.GetAccessorStatement, - SyntaxKind.AddHandlerAccessorStatement, - SyntaxKind.RemoveHandlerAccessorStatement, - SyntaxKind.RaiseEventAccessorStatement, - SyntaxKind.NamespaceStatement - Return False - - Case SyntaxKind.SimpleAsClause - If editKind = EditKind.Update AndAlso node.Parent.IsKind(SyntaxKind.FunctionStatement) Then - node = node.Parent ' for attributes on return types of functions - End If - - Case SyntaxKind.Parameter - If editKind = EditKind.Update Then - ' for delegate invoke methods we need to go one step higher, to the delegate itself - Dim parameterSymbol = model.GetDeclaredSymbol(node, cancellationToken) - Dim containingDelegate = TryCast(parameterSymbol.ContainingSymbol, IMethodSymbol) - If containingDelegate IsNot Nothing AndAlso containingDelegate.MethodKind = MethodKind.DelegateInvoke Then - parameterSymbol = containingDelegate.ContainingSymbol - End If - - symbols = OneOrMany.Create(parameterSymbol) - Return True - End If - - Return False - - Case SyntaxKind.TypeParameter - If editKind <> EditKind.Update Then - Return False - End If - Case SyntaxKind.ImportsStatement, + SyntaxKind.NamespaceStatement, SyntaxKind.NamespaceBlock Return False - Case SyntaxKind.ModifiedIdentifier - If Not node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then - Return False - End If - Case SyntaxKind.VariableDeclarator - If Not node.Parent.IsKind(SyntaxKind.FieldDeclaration) Then - Return False - End If - Dim variableDeclarator = CType(node, VariableDeclaratorSyntax) If variableDeclarator.Names.Count > 1 Then symbols = OneOrMany.Create(variableDeclarator.Names.SelectAsArray(Function(n) model.GetDeclaredSymbol(n, cancellationToken))) @@ -1324,10 +1296,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Case SyntaxKind.FieldDeclaration If editKind = EditKind.Update Then - ' If attributes on a field change then we get the field declaration here, but GetDeclaredSymbol needs an actual variable name - ' Fortunately attributes are shared across all of them, so we don't need to be too fancy Dim field = CType(node, FieldDeclarationSyntax) - node = field.Declarators.First().Names.First() + If field.Declarators.Count = 1 AndAlso field.Declarators(0).Names.Count = 1 Then + node = field.Declarators(0).Names(0) + Else + symbols = OneOrMany.Create( + (From declarator In field.Declarators + From name In declarator.Names + Select model.GetDeclaredSymbol(name, cancellationToken)).ToImmutableArray()) + + Return True + End If End If End Select @@ -1801,6 +1780,34 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Select End Function + Friend Overrides Function GetDisplayName(symbol As INamedTypeSymbol) As String + Select Case symbol.TypeKind + Case TypeKind.Structure + Return VBFeaturesResources.structure_ + Case TypeKind.Module + Return VBFeaturesResources.module_ + Case Else + Return MyBase.GetDisplayName(symbol) + End Select + End Function + + Friend Overrides Function GetDisplayName(symbol As IMethodSymbol) As String + Select Case symbol.MethodKind + Case MethodKind.StaticConstructor + Return VBFeaturesResources.Shared_constructor + Case Else + Return MyBase.GetDisplayName(symbol) + End Select + End Function + + Friend Overrides Function GetDisplayName(symbol As IPropertySymbol) As String + If symbol.IsWithEvents Then + Return VBFeaturesResources.WithEvents_field + End If + + Return MyBase.GetDisplayName(symbol) + End Function + Protected Overrides Function TryGetDisplayName(node As SyntaxNode, editKind As EditKind) As String Return TryGetDisplayNameImpl(node, editKind) End Function @@ -1904,7 +1911,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.SetAccessorBlock, SyntaxKind.GetAccessorStatement, SyntaxKind.SetAccessorStatement - Return VBFeaturesResources.property_accessor + Return FeaturesResources.property_accessor Case SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, @@ -2218,56 +2225,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Insert) Return - Case SyntaxKind.ImportsStatement - ' We don't report rude edits for new imports, though note we don't currently report edits - ' for semantic changes they cause either - Return - - Case SyntaxKind.ClassBlock, - SyntaxKind.StructureBlock, - SyntaxKind.InterfaceBlock, - SyntaxKind.EnumBlock, - SyntaxKind.ModuleBlock, - SyntaxKind.DelegateSubStatement, - SyntaxKind.DelegateFunctionStatement, - SyntaxKind.SubStatement, ' interface method - SyntaxKind.FunctionStatement, ' interface method - SyntaxKind.PropertyBlock, - SyntaxKind.PropertyStatement, ' autoprop or interface property - SyntaxKind.EventBlock, - SyntaxKind.EventStatement, - SyntaxKind.OperatorBlock, - SyntaxKind.SubBlock, - SyntaxKind.FunctionBlock, - SyntaxKind.DeclareSubStatement, - SyntaxKind.DeclareFunctionStatement, - SyntaxKind.ConstructorBlock, - SyntaxKind.GetAccessorBlock, - SyntaxKind.SetAccessorBlock, - SyntaxKind.AddHandlerAccessorBlock, - SyntaxKind.RemoveHandlerAccessorBlock, - SyntaxKind.RaiseEventAccessorBlock, - SyntaxKind.FieldDeclaration, - SyntaxKind.ModifiedIdentifier - Return - - Case SyntaxKind.VariableDeclarator - ' Ignore, errors will be reported for children (ModifiedIdentifier, AsClause) - Return - - Case SyntaxKind.EnumMemberDeclaration, - SyntaxKind.TypeParameter, - SyntaxKind.StructureConstraint, - SyntaxKind.TypeParameterSingleConstraintClause, - SyntaxKind.TypeParameterMultipleConstraintClause, - SyntaxKind.ClassConstraint, - SyntaxKind.StructureConstraint, - SyntaxKind.NewConstraint, - SyntaxKind.TypeConstraint, - SyntaxKind.TypeParameterList, - SyntaxKind.Parameter, - SyntaxKind.AttributesStatement, - SyntaxKind.SimpleAsClause + Case SyntaxKind.AttributesStatement + ' Module/assembly attribute ReportError(RudeEditKind.Insert) Return @@ -2286,25 +2245,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If Return - - Case SyntaxKind.ParameterList - ClassifyParameterInsert(DirectCast(node, ParameterListSyntax)) - Return - - Case Else - Throw ExceptionUtilities.UnexpectedValue(node.Kind()) End Select End Sub - Private Sub ClassifyParameterInsert(parameterList As ParameterListSyntax) - ' Sub M -> Sub M() is ok - If parameterList.Parameters.Count = 0 Then - Return - End If - - ReportError(RudeEditKind.Insert) - End Sub - #End Region #Region "Delete" @@ -2316,45 +2259,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Delete) Return - Case SyntaxKind.ImportsStatement - ' We don't report rude edits for deleting imports, though note we don't currently report edits - ' for semantic changes they cause either - Return - - Case SyntaxKind.ClassBlock, - SyntaxKind.StructureBlock, - SyntaxKind.InterfaceBlock, - SyntaxKind.ModuleBlock, - SyntaxKind.DelegateFunctionStatement, - SyntaxKind.DelegateSubStatement, - SyntaxKind.EnumBlock, - SyntaxKind.FieldDeclaration, - SyntaxKind.ModifiedIdentifier, - SyntaxKind.SubBlock, - SyntaxKind.FunctionBlock, - SyntaxKind.SubStatement, - SyntaxKind.FunctionStatement, - SyntaxKind.OperatorBlock, - SyntaxKind.PropertyBlock, - SyntaxKind.PropertyStatement, - SyntaxKind.EventBlock, - SyntaxKind.EventStatement, - SyntaxKind.DeclareSubStatement, - SyntaxKind.DeclareFunctionStatement, - SyntaxKind.ConstructorBlock - ' We do not report member delete here since the member might be moving to a different part of a partial type declaration. - ' If that is not the case the semantic analysis reports the rude edit. - Return - - Case SyntaxKind.GetAccessorBlock, - SyntaxKind.SetAccessorBlock, - SyntaxKind.AddHandlerAccessorBlock, - SyntaxKind.RemoveHandlerAccessorBlock, - SyntaxKind.RaiseEventAccessorBlock, - SyntaxKind.EnumMemberDeclaration - ' We do not report error here since it will be reported in semantic analysis. - Return - Case SyntaxKind.AttributeList ' Only module/assembly attributes are rude edits If oldNode.IsParentKind(SyntaxKind.AttributesStatement) Then @@ -2370,40 +2274,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If Return - - Case SyntaxKind.TypeParameter, - SyntaxKind.TypeParameterList, - SyntaxKind.Parameter, - SyntaxKind.TypeParameterSingleConstraintClause, - SyntaxKind.TypeParameterMultipleConstraintClause, - SyntaxKind.ClassConstraint, - SyntaxKind.StructureConstraint, - SyntaxKind.NewConstraint, - SyntaxKind.TypeConstraint, - SyntaxKind.SimpleAsClause - ReportError(RudeEditKind.Delete) - Return - - Case SyntaxKind.ParameterList - ClassifyDelete(DirectCast(oldNode, ParameterListSyntax)) - - Case SyntaxKind.VariableDeclarator - ' Ignore, errors will be reported for children (ModifiedIdentifier, AsClause) - Return - - Case Else - Throw ExceptionUtilities.UnexpectedValue(oldNode.Kind) End Select End Sub - - Private Sub ClassifyDelete(oldNode As ParameterListSyntax) - ' Sub Goo() -> Sub Goo is ok - If oldNode.Parameters.Count = 0 Then - Return - End If - - ReportError(RudeEditKind.Delete) - End Sub #End Region #Region "Update" @@ -2413,53 +2285,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Update) Return - Case SyntaxKind.ImportsStatement - ' We don't report rude edits for updating imports, though note we don't currently report edits - ' for semantic changes they cause either - Return - - Case SyntaxKind.NamespaceBlock - Return - Case SyntaxKind.NamespaceStatement ClassifyUpdate(DirectCast(oldNode, NamespaceStatementSyntax), DirectCast(newNode, NamespaceStatementSyntax)) Return - Case SyntaxKind.ClassBlock, - SyntaxKind.StructureBlock, - SyntaxKind.InterfaceBlock, - SyntaxKind.ModuleBlock - ClassifyUpdate(DirectCast(oldNode, TypeBlockSyntax), DirectCast(newNode, TypeBlockSyntax)) - Return - - Case SyntaxKind.ClassStatement, - SyntaxKind.StructureStatement, - SyntaxKind.InterfaceStatement, - SyntaxKind.ModuleStatement - ClassifyUpdate(DirectCast(oldNode, TypeStatementSyntax), DirectCast(newNode, TypeStatementSyntax)) - Return - - Case SyntaxKind.EnumBlock - Return - - Case SyntaxKind.EnumStatement - ClassifyUpdate(DirectCast(oldNode, EnumStatementSyntax), DirectCast(newNode, EnumStatementSyntax)) - Return - - Case SyntaxKind.DelegateSubStatement, - SyntaxKind.DelegateFunctionStatement - ClassifyUpdate(DirectCast(oldNode, DelegateStatementSyntax), DirectCast(newNode, DelegateStatementSyntax)) - Return - - Case SyntaxKind.FieldDeclaration - Return - Case SyntaxKind.VariableDeclarator ClassifyUpdate(DirectCast(oldNode, VariableDeclaratorSyntax), DirectCast(newNode, VariableDeclaratorSyntax)) Return Case SyntaxKind.ModifiedIdentifier - ClassifyUpdate(DirectCast(oldNode, ModifiedIdentifierSyntax), DirectCast(newNode, ModifiedIdentifierSyntax)) + ClassifyUpdate(DirectCast(newNode, ModifiedIdentifierSyntax)) Return Case SyntaxKind.SubBlock, @@ -2467,56 +2302,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ClassifyUpdate(DirectCast(oldNode, MethodBlockSyntax), DirectCast(newNode, MethodBlockSyntax)) Return - Case SyntaxKind.DeclareSubStatement, - SyntaxKind.DeclareFunctionStatement - ClassifyUpdate(DirectCast(oldNode, DeclareStatementSyntax), DirectCast(newNode, DeclareStatementSyntax)) - Return - Case SyntaxKind.SubStatement, SyntaxKind.FunctionStatement ClassifyUpdate(DirectCast(oldNode, MethodStatementSyntax), DirectCast(newNode, MethodStatementSyntax)) Return - Case SyntaxKind.SimpleAsClause - ClassifyUpdate(DirectCast(oldNode, SimpleAsClauseSyntax), DirectCast(newNode, SimpleAsClauseSyntax)) - Return - Case SyntaxKind.OperatorBlock ClassifyUpdate(DirectCast(oldNode, OperatorBlockSyntax), DirectCast(newNode, OperatorBlockSyntax)) Return - Case SyntaxKind.OperatorStatement - ClassifyUpdate(DirectCast(oldNode, OperatorStatementSyntax), DirectCast(newNode, OperatorStatementSyntax)) - Return - Case SyntaxKind.ConstructorBlock ClassifyUpdate(DirectCast(oldNode, ConstructorBlockSyntax), DirectCast(newNode, ConstructorBlockSyntax)) Return - Case SyntaxKind.SubNewStatement - Return - - Case SyntaxKind.PropertyBlock - Return - Case SyntaxKind.PropertyStatement ClassifyUpdate(DirectCast(oldNode, PropertyStatementSyntax), DirectCast(newNode, PropertyStatementSyntax)) Return - Case SyntaxKind.EventBlock - Return - Case SyntaxKind.EventStatement ClassifyUpdate(DirectCast(oldNode, EventStatementSyntax), DirectCast(newNode, EventStatementSyntax)) Return - Case SyntaxKind.GetAccessorStatement, - SyntaxKind.SetAccessorStatement, - SyntaxKind.AddHandlerAccessorStatement, - SyntaxKind.RemoveHandlerAccessorStatement, - SyntaxKind.RaiseEventAccessorStatement - Return - Case SyntaxKind.GetAccessorBlock, SyntaxKind.SetAccessorBlock, SyntaxKind.AddHandlerAccessorBlock, @@ -2529,30 +2335,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ClassifyUpdate(DirectCast(oldNode, EnumMemberDeclarationSyntax), DirectCast(newNode, EnumMemberDeclarationSyntax)) Return - Case SyntaxKind.StructureConstraint, - SyntaxKind.ClassConstraint, - SyntaxKind.NewConstraint - ReportError(RudeEditKind.ConstraintKindUpdate, - {DirectCast(oldNode, SpecialConstraintSyntax).ConstraintKeyword.ValueText, - DirectCast(newNode, SpecialConstraintSyntax).ConstraintKeyword.ValueText}) - Return - - Case SyntaxKind.TypeConstraint - ReportError(RudeEditKind.TypeUpdate) - Return - - Case SyntaxKind.TypeParameterMultipleConstraintClause, - SyntaxKind.TypeParameterSingleConstraintClause - Return - - Case SyntaxKind.TypeParameter - ClassifyUpdate(DirectCast(oldNode, TypeParameterSyntax), DirectCast(newNode, TypeParameterSyntax)) - Return - - Case SyntaxKind.Parameter - ClassifyUpdate(DirectCast(oldNode, ParameterSyntax), DirectCast(newNode, ParameterSyntax)) - Return - Case SyntaxKind.AttributesStatement ReportError(RudeEditKind.Update) Return @@ -2564,14 +2346,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If Return - - Case SyntaxKind.AttributeList, - SyntaxKind.TypeParameterList, - SyntaxKind.ParameterList - Return - - Case Else - Throw ExceptionUtilities.UnexpectedValue(newNode.Kind) End Select End Sub @@ -2580,76 +2354,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Renamed) End Sub - Private Sub ClassifyUpdate(oldNode As TypeStatementSyntax, newNode As TypeStatementSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - End If - End Sub - - Private Sub ClassifyUpdate(oldNode As TypeBlockSyntax, newNode As TypeBlockSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Inherits, newNode.Inherits) OrElse - Not SyntaxFactory.AreEquivalent(oldNode.Implements, newNode.Implements) Then - ReportError(RudeEditKind.BaseTypeOrInterfaceUpdate) - End If - - ' type member list separators - End Sub - - Private Sub ClassifyUpdate(oldNode As EnumStatementSyntax, newNode As EnumStatementSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.UnderlyingType, newNode.UnderlyingType) Then - ReportError(RudeEditKind.EnumUnderlyingTypeUpdate) - Return - End If - End Sub - - Private Sub ClassifyUpdate(oldNode As DelegateStatementSyntax, newNode As DelegateStatementSyntax) - ' Function changed to Sub or vice versa. Note that Function doesn't need to have AsClause. - If oldNode.RawKind <> newNode.RawKind Then - ReportError(RudeEditKind.TypeUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.AsClause, newNode.AsClause) Then - ReportError(RudeEditKind.TypeUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - End Sub - - Private Sub ClassifyUpdate(oldNode As ModifiedIdentifierSyntax, newNode As ModifiedIdentifierSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - - ' TODO (tomat): We could be smarter and consider the following syntax changes to be legal: - ' Dim a? As Integer <-> Dim a As Integer? - ' Dim a() As Integer <-> Dim a As Integer() - - If Not SyntaxFactory.AreEquivalent(oldNode.ArrayRankSpecifiers, newNode.ArrayRankSpecifiers) OrElse - Not SyntaxFactory.AreEquivalent(oldNode.Nullable, newNode.Nullable) Then - ReportError(RudeEditKind.TypeUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.ArrayBounds, newNode.ArrayBounds) Then - If oldNode.ArrayBounds Is Nothing OrElse - newNode.ArrayBounds Is Nothing OrElse - oldNode.ArrayBounds.Arguments.Count <> newNode.ArrayBounds.Arguments.Count Then - ReportError(RudeEditKind.TypeUpdate) - Return - End If - End If - + Private Sub ClassifyUpdate(newNode As ModifiedIdentifierSyntax) ' Otherwise only the size of the array changed, which is a legal initializer update ' unless it contains lambdas, queries etc. ClassifyDeclarationBodyRudeUpdates(newNode) @@ -2676,11 +2381,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Sub Private Sub ClassifyUpdate(oldNode As PropertyStatementSyntax, newNode As PropertyStatementSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - If Not SyntaxFactory.AreEquivalent(oldNode.ImplementsClause, newNode.ImplementsClause) Then ReportError(RudeEditKind.ImplementsClauseUpdate) Return @@ -2717,22 +2417,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ' A custom event can't be matched with a field event and vice versa: Debug.Assert(SyntaxFactory.AreEquivalent(oldNode.CustomKeyword, newNode.CustomKeyword)) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - If Not SyntaxFactory.AreEquivalent(oldNode.ImplementsClause, newNode.ImplementsClause) Then ReportError(RudeEditKind.ImplementsClauseUpdate) Return End If - - Dim oldHasGeneratedType = oldNode.ParameterList IsNot Nothing - Dim newHasGeneratedType = newNode.ParameterList IsNot Nothing - - If oldHasGeneratedType <> newHasGeneratedType Then - ReportError(RudeEditKind.TypeUpdate) - End If End Sub Private Sub ClassifyUpdate(oldNode As MethodBlockSyntax, newNode As MethodBlockSyntax) @@ -2743,16 +2431,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Sub Private Sub ClassifyUpdate(oldNode As MethodStatementSyntax, newNode As MethodStatementSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.DeclarationKeyword, newNode.DeclarationKeyword) Then - ReportError(RudeEditKind.MethodKindUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - ' TODO (tomat): We can support this If Not SyntaxFactory.AreEquivalent(oldNode.HandlesClause, newNode.HandlesClause) Then ReportError(RudeEditKind.HandlesClauseUpdate) @@ -2765,31 +2443,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If End Sub - Private Sub ClassifyUpdate(oldNode As DeclareStatementSyntax, newNode As DeclareStatementSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers) Then - ReportError(RudeEditKind.ModifiersUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.CharsetKeyword, newNode.CharsetKeyword) Then - ReportError(RudeEditKind.ModifiersUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.LibraryName, newNode.LibraryName) Then - ReportError(RudeEditKind.DeclareLibraryUpdate) - Return - End If - - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.AliasName, newNode.AliasName)) - ReportError(RudeEditKind.DeclareAliasUpdate) - End Sub - Private Sub ClassifyUpdate(oldNode As OperatorBlockSyntax, newNode As OperatorBlockSyntax) ClassifyMethodBodyRudeUpdate(oldNode, newNode, @@ -2797,18 +2450,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue containingType:=DirectCast(newNode.Parent, TypeBlockSyntax)) End Sub - Private Sub ClassifyUpdate(oldNode As OperatorStatementSyntax, newNode As OperatorStatementSyntax) - Dim oldWidening = oldNode.Modifiers.IndexOf(SyntaxKind.WideningKeyword) >= 0 - Dim oldNarrowing = oldNode.Modifiers.IndexOf(SyntaxKind.NarrowingKeyword) >= 0 - Dim newWidening = newNode.Modifiers.IndexOf(SyntaxKind.WideningKeyword) >= 0 - Dim newNarrowing = newNode.Modifiers.IndexOf(SyntaxKind.NarrowingKeyword) >= 0 - - If newWidening <> oldWidening OrElse newNarrowing <> oldNarrowing Then - ReportError(RudeEditKind.ModifiersUpdate) - Return - End If - End Sub - Private Sub ClassifyUpdate(oldNode As AccessorBlockSyntax, newNode As AccessorBlockSyntax) Debug.Assert(newNode.Parent.IsKind(SyntaxKind.EventBlock) OrElse newNode.Parent.IsKind(SyntaxKind.PropertyBlock)) @@ -2820,11 +2461,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Sub Private Sub ClassifyUpdate(oldNode As EnumMemberDeclarationSyntax, newNode As EnumMemberDeclarationSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - If Not SyntaxFactory.AreEquivalent(oldNode.Initializer, newNode.Initializer) Then ReportError(RudeEditKind.InitializerUpdate) Return @@ -2838,47 +2474,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue containingType:=DirectCast(newNode.Parent, TypeBlockSyntax)) End Sub - Private Sub ClassifyUpdate(oldNode As SimpleAsClauseSyntax, newNode As SimpleAsClauseSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type) Then - ReportError(RudeEditKind.TypeUpdate, newNode.Parent, newNode.Parent) - End If - End Sub - - Private Sub ClassifyUpdate(oldNode As TypeParameterSyntax, newNode As TypeParameterSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.VarianceKeyword, newNode.VarianceKeyword) Then - ReportError(RudeEditKind.VarianceUpdate) - Return - End If - End Sub - - Private Sub ClassifyUpdate(oldNode As ParameterSyntax, newNode As ParameterSyntax) - If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then - ReportError(RudeEditKind.Renamed) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers) Then - ReportError(RudeEditKind.ModifiersUpdate) - Return - End If - - If Not SyntaxFactory.AreEquivalent(oldNode.Default, newNode.Default) Then - ReportError(RudeEditKind.InitializerUpdate) - Return - End If - - If ClassifyTypeAndInitializerUpdates(oldNode.Default, oldNode.AsClause, newNode.Default, newNode.AsClause) Then - Return - End If - - ClassifyUpdate(oldNode.Identifier, newNode.Identifier) - End Sub - Private Sub ClassifyMethodBodyRudeUpdate(oldBody As MethodBlockBaseSyntax, newBody As MethodBlockBaseSyntax, containingMethod As MethodBlockSyntax, @@ -3051,79 +2646,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return RudeEditKind.None Case SymbolKind.Field - 'Dim field = DirectCast(newSymbol, IFieldSymbol) - ' TODO: - ' Can't insert WithEvents field since it is effectively a virtual property. - ' WithEvents X As C - 'If field.Modifiers.Any(SyntaxKind.WithEventsKeyword) Then - ' ReportError(RudeEditKind.Insert) - ' Return - 'End If - - 'Dim containingType = field.Parent - 'If containingType.IsKind(SyntaxKind.ModuleBlock) Then - ' ReportError(RudeEditKind.Insert) - ' Return - 'End If + ' Inserting a field into an enum is not allowed. + If newSymbol.ContainingType.TypeKind = TypeKind.Enum Then + Return RudeEditKind.Insert + End If + Return RudeEditKind.None End Select Return RudeEditKind.None End Function - Friend Overrides Sub ReportTypeDeclarationInsertDeleteRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), oldType As INamedTypeSymbol, newType As INamedTypeSymbol, newDeclaration As SyntaxNode, cancellationToken As CancellationToken) - Dim oldNodes = ArrayBuilder(Of SyntaxNode).GetInstance() - Dim newNodes = ArrayBuilder(Of SyntaxNode).GetInstance() - - Dim report = - Sub(addNodes As Action(Of ArrayBuilder(Of SyntaxNode), TypeBlockSyntax), rudeEditKind As RudeEditKind) - - For Each syntaxRef In oldType.DeclaringSyntaxReferences - addNodes(oldNodes, CType(GetSymbolDeclarationSyntax(syntaxRef, cancellationToken), TypeBlockSyntax)) - Next - - For Each syntaxRef In newType.DeclaringSyntaxReferences - addNodes(newNodes, CType(GetSymbolDeclarationSyntax(syntaxRef, cancellationToken), TypeBlockSyntax)) - Next - - If oldNodes.Count <> newNodes.Count OrElse - oldNodes.Zip(newNodes, Function(oldNode, newNode) SyntaxFactory.AreEquivalent(oldNode, newNode)).Any(Function(isEquivalent) Not isEquivalent) Then - - diagnostics.Add(New RudeEditDiagnostic( - rudeEditKind, - GetDiagnosticSpan(newDeclaration, EditKind.Update), - newDeclaration, - arguments:={GetDisplayName(newDeclaration, EditKind.Update)})) - End If - - oldNodes.Clear() - newNodes.Clear() - End Sub - - ' Consider better error messages - report(Sub(b, t) AddNodes(b, t.BlockStatement.AttributeLists), RudeEditKind.Update) - report(Sub(b, t) AddNodes(b, t.BlockStatement.TypeParameterList?.Parameters), RudeEditKind.Update) - - report(Sub(b, t) - For Each inherit In t.Inherits - For Each baseType In inherit.Types - b.Add(baseType) - Next - Next - End Sub, RudeEditKind.BaseTypeOrInterfaceUpdate) - - report(Sub(b, t) - For Each impl In t.Implements - For Each baseInterface In impl.Types - b.Add(baseInterface) - Next - Next - End Sub, RudeEditKind.BaseTypeOrInterfaceUpdate) - - oldNodes.Free() - newNodes.Free() - End Sub - #End Region #Region "Exception Handling Rude Edits" diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx index 8207738c7abe6..6d17e3c5da65b 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx @@ -1126,9 +1126,6 @@ Sub(<parameterList>) <statement> WithEvents field {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - as clause {Locked="as"} "as" is a VB keyword and should not be localized. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf index 2e37443237550..3c420f71bfa49 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf @@ -1692,11 +1692,6 @@ Sub(<seznam_parametrů>) <výraz> Pole WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - Přistupující objekt vlastnosti - - as clause Klauzule as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf index 21b10618decd0..6b0bd2b8cfa7c 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf @@ -1692,11 +1692,6 @@ Sub(<Parameterliste>) <Ausdruck> WithEvents-Feld {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - Eigenschaftenaccessor - - as clause as-Klausel diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf index 8aec5d2f4af3e..fa491b4189f2e 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf @@ -1692,11 +1692,6 @@ Sub(<listaDeParámetros>) <instrucción> Campo WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - descriptor de acceso de propiedades - - as clause cláusula as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf index c079d57e9c857..5955db7a2de01 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> Champ WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - accesseur de propriété - - as clause Clause as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf index e2659985552a3..6643c2d4daa02 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf @@ -1692,11 +1692,6 @@ Sub(<elencoParametri>) <istruzione> campo WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - funzione di accesso proprietà - - as clause clausola as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf index 9dcc48bb4a88f..22f4146515573 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> WithEvents フィールド {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - プロパティ アクセサー - - as clause as 句 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf index e341bfa2c49da..51d695655bdf0 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> WithEvents 필드 {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - 속성 접근자 - - as clause as 절 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf index 87db8a619e2ec..76edf86737818 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf @@ -1692,11 +1692,6 @@ Sub(<listaParametrów>) <instrukcja> Pole WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - metoda dostępu do właściwości - - as clause klauzula as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf index 6d6399f53cc24..cd43fbc21727c 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> Campo de WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - acessador de propriedade - - as clause cláusula as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf index cfe7315b26ee2..8e136c16872ba 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> Поле WithEvents {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - метод доступа к свойству - - as clause предложение as diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf index f627bf735a0d5..a16c71e81b830 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> WithEvents alanı {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - özellik erişeni - - as clause as yan tümcesi diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf index 591e1bd1549f3..db8ae8f1061a9 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> WithEvents 字段 {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - 属性访问器 - - as clause as 子句 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf index fd3f478081751..ea31afb2bc926 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf @@ -1692,11 +1692,6 @@ Sub(<parameterList>) <statement> WithEvents 欄位 {Locked="WithEvents"} "WithEvents" is a VB keyword and should not be localized. - - property accessor - 屬性存取子 - - as clause as 子句 diff --git a/src/Tools/ExternalAccess/FSharp/Editor/FSharpContentTypeNames.cs b/src/Tools/ExternalAccess/FSharp/Editor/FSharpContentTypeNames.cs index 8395b1e1b5cc1..4e79cc6eabf3e 100644 --- a/src/Tools/ExternalAccess/FSharp/Editor/FSharpContentTypeNames.cs +++ b/src/Tools/ExternalAccess/FSharp/Editor/FSharpContentTypeNames.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor internal static class FSharpContentTypeNames { public const string RoslynContentType = Microsoft.CodeAnalysis.Editor.ContentTypeNames.RoslynContentType; - public const string FSharpContentType = "F#"; - public const string FSharpSignatureHelpContentType = "F# Signature Help"; + public const string FSharpContentType = CodeAnalysis.Editor.ContentTypeNames.FSharpContentType; + public const string FSharpSignatureHelpContentType = CodeAnalysis.Editor.ContentTypeNames.FSharpSignatureHelpContentType; } } diff --git a/src/Tools/ExternalAccess/FSharp/Internal/FSharpContentTypeDefinitions.cs b/src/Tools/ExternalAccess/FSharp/Internal/FSharpContentTypeDefinitions.cs index 46c8e93e537c7..a58c4f2c126a3 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/FSharpContentTypeDefinitions.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/FSharpContentTypeDefinitions.cs @@ -6,6 +6,7 @@ using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor; +using Microsoft.VisualStudio.LanguageServer.Client; using Microsoft.VisualStudio.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal @@ -15,6 +16,7 @@ internal static class FSharpContentTypeDefinitions [Export] [Name(FSharpContentTypeNames.FSharpContentType)] [BaseDefinition(FSharpContentTypeNames.RoslynContentType)] + [BaseDefinition(CodeRemoteContentDefinition.CodeRemoteBaseTypeName)] public static readonly ContentTypeDefinition FSharpContentTypeDefinition; [Export] diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs index 2ad56cefb8014..582b93e9d7cc4 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs @@ -1017,7 +1017,9 @@ string wasUpdatedCheck(Field field) ? "!TypeSymbol.Equals({0}, this.{1}, TypeCompareKind.ConsiderEverything)" : TypeIsSymbol(field) ? "!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals({0}, this.{1})" - : "{0} != this.{1}"; + : IsValueType(field.Type) && field.Type[^1] == '?' + ? "{0}.Equals(this.{1})" + : "{0} != this.{1}"; return string.Format(format, ToCamelCase(field.Name), field.Name); } @@ -1738,7 +1740,7 @@ private bool IsAnyList(string typeName) return IsNodeList(typeName); } - private bool IsValueType(string typeName) => _valueTypes.Contains(GetGenericType(typeName)); + private bool IsValueType(string typeName) => _valueTypes.Contains(GetGenericType(typeName).TrimEnd('?')); private bool IsDerivedType(string typeName, string derivedTypeName) { diff --git a/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VisualStudioWorkspaceEditAndContinueListener.cs b/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VisualStudioWorkspaceEditAndContinueListener.cs new file mode 100644 index 0000000000000..bd737a536714d --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/EditAndContinue/VisualStudioWorkspaceEditAndContinueListener.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.EditAndContinue; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Roslyn.Utilities; + +namespace Microsoft.VisualStudio.LanguageServices.EditAndContinue +{ + /// + /// Connects to the ServiceHub services. + /// Launches ServiceHub if it is not running yet and starts services that push information from to the ServiceHub process. + /// + [ExportEventListener(WellKnownEventListeners.Workspace, WorkspaceKind.Host), Shared] + internal sealed class VisualStudioWorkspaceEditAndContinueListener : IEventListener, IEventListenerStoppable + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public VisualStudioWorkspaceEditAndContinueListener() + { + } + + public void StartListening(Workspace workspace, object serviceOpt) + { + if (workspace is not VisualStudioWorkspace) + { + return; + } + + workspace.DocumentOpened += WorkspaceDocumentOpened; + } + + public void StopListening(Workspace workspace) + { + if (workspace is not VisualStudioWorkspace) + { + return; + } + + workspace.DocumentOpened -= WorkspaceDocumentOpened; + } + + private void WorkspaceDocumentOpened(object? sender, DocumentEventArgs e) + { + var proxy = new RemoteEditAndContinueServiceProxy(e.Document.Project.Solution.Workspace); + _ = Task.Run(() => proxy.OnSourceFileUpdatedAsync(e.Document, CancellationToken.None)).ReportNonFatalErrorAsync(); + } + } +} diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActivateInProcLanguageClient.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActivateInProcLanguageClient.cs index e083b252a16d8..5a7a2b78d6a0b 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActivateInProcLanguageClient.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActivateInProcLanguageClient.cs @@ -20,12 +20,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageClient { /// - /// Language client responsible for handling C# / VB LSP requests in any scenario (both local and codespaces). + /// Language client responsible for handling C# / VB / F# LSP requests in any scenario (both local and codespaces). /// This powers "LSP only" features (e.g. cntrl+Q code search) that do not use traditional editor APIs. /// It is always activated whenever roslyn is activated. /// [ContentType(ContentTypeNames.CSharpContentType)] [ContentType(ContentTypeNames.VisualBasicContentType)] + [ContentType(ContentTypeNames.FSharpContentType)] [Export(typeof(ILanguageClient))] [Export(typeof(AlwaysActivateInProcLanguageClient))] internal class AlwaysActivateInProcLanguageClient : AbstractInProcLanguageClient @@ -35,7 +36,7 @@ internal class AlwaysActivateInProcLanguageClient : AbstractInProcLanguageClient [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, true)] public AlwaysActivateInProcLanguageClient( - CSharpVisualBasicRequestDispatcherFactory csharpVBRequestDispatcherFactory, + RequestDispatcherFactory csharpVBRequestDispatcherFactory, VisualStudioWorkspace workspace, IAsynchronousOperationListenerProvider listenerProvider, ILspWorkspaceRegistrationService lspWorkspaceRegistrationService, diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActiveLanguageClientEventListener.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActiveLanguageClientEventListener.cs index e5e7568918135..ef98fdd74f2c6 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActiveLanguageClientEventListener.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/AlwaysActiveLanguageClientEventListener.cs @@ -63,8 +63,12 @@ private async Task LoadAsync() // doesn't block the UI thread. await TaskScheduler.Default; - await _languageClientBroker.Value.LoadAsync( - new LanguageClientMetadata(new[] { ContentTypeNames.CSharpContentType, ContentTypeNames.VisualBasicContentType }), _languageClient).ConfigureAwait(false); + await _languageClientBroker.Value.LoadAsync(new LanguageClientMetadata(new[] + { + ContentTypeNames.CSharpContentType, + ContentTypeNames.VisualBasicContentType, + ContentTypeNames.FSharpContentType + }), _languageClient).ConfigureAwait(false); } catch (Exception e) when (FatalError.ReportAndCatch(e)) { diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareInProcLanguageClient.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareInProcLanguageClient.cs index abf4b240bdb10..d7e5b36d7931b 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareInProcLanguageClient.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/LiveShareInProcLanguageClient.cs @@ -32,7 +32,7 @@ internal class LiveShareInProcLanguageClient : AbstractInProcLanguageClient [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, true)] public LiveShareInProcLanguageClient( - CSharpVisualBasicRequestDispatcherFactory csharpVBRequestDispatcherFactory, + RequestDispatcherFactory csharpVBRequestDispatcherFactory, VisualStudioWorkspace workspace, IDiagnosticService diagnosticService, IAsynchronousOperationListenerProvider listenerProvider, diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorInProcLanguageClient.cs b/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorInProcLanguageClient.cs index 11624da7ca356..f6d3285a369d3 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorInProcLanguageClient.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageClient/RazorInProcLanguageClient.cs @@ -52,7 +52,7 @@ internal class RazorInProcLanguageClient : AbstractInProcLanguageClient [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public RazorInProcLanguageClient( - CSharpVisualBasicRequestDispatcherFactory csharpVBRequestDispatcherFactory, + RequestDispatcherFactory csharpVBRequestDispatcherFactory, VisualStudioWorkspace workspace, IDiagnosticService diagnosticService, IAsynchronousOperationListenerProvider listenerProvider, diff --git a/src/VisualStudio/Core/Def/Implementation/Progression/GraphQueries/SearchGraphQuery.cs b/src/VisualStudio/Core/Def/Implementation/Progression/GraphQueries/SearchGraphQuery.cs index 2a525df7db468..448311407c50b 100644 --- a/src/VisualStudio/Core/Def/Implementation/Progression/GraphQueries/SearchGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Implementation/Progression/GraphQueries/SearchGraphQuery.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Experiments; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.NavigateTo; using Microsoft.CodeAnalysis.PooledObjects; @@ -38,8 +39,11 @@ public SearchGraphQuery( public Task GetGraphAsync(Solution solution, IGraphContext context, CancellationToken cancellationToken) { + var experimentationService = solution.Workspace.Services.GetService(); + var forceLegacySearch = experimentationService?.IsExperimentEnabled(WellKnownExperimentNames.ProgressionForceLegacySearch) == true; + var option = solution.Options.GetOption(ProgressionOptions.SearchUsingNavigateToEngine); - return option + return !forceLegacySearch && option ? SearchUsingNavigateToEngineAsync(solution, context, cancellationToken) : SearchUsingSymbolsAsync(solution, context, cancellationToken); } diff --git a/src/VisualStudio/Core/Def/Implementation/Progression/ProgressionOptions.cs b/src/VisualStudio/Core/Def/Implementation/Progression/ProgressionOptions.cs index 94a3c73b535cf..4e9eeb23cbae7 100644 --- a/src/VisualStudio/Core/Def/Implementation/Progression/ProgressionOptions.cs +++ b/src/VisualStudio/Core/Def/Implementation/Progression/ProgressionOptions.cs @@ -13,7 +13,7 @@ internal static class ProgressionOptions private const string LocalRegistryPath = @"Roslyn\Internal\OnOff\Components\Progression\"; public static readonly Option2 SearchUsingNavigateToEngine = new( - nameof(ProgressionOptions), nameof(SearchUsingNavigateToEngine), defaultValue: false, + nameof(ProgressionOptions), nameof(SearchUsingNavigateToEngine), defaultValue: true, storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "SearchUsingNavigateToEngine")); } } diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs index cb0f903ba132b..b9b585607588d 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioDocumentNavigationService.cs @@ -251,7 +251,15 @@ private bool TryNavigateToLocation( { if (workspace.CurrentSolution.GetDocument(documentId) == null) { - var generatedDocument = workspace.CurrentSolution.GetProject(documentId.ProjectId) + var project = workspace.CurrentSolution.GetProject(documentId.ProjectId); + if (project is null) + { + // This is a source generated document shown in Solution Explorer, but is no longer valid since + // the configuration and/or platform changed since the last generation completed. + return false; + } + + var generatedDocument = project .GetSourceGeneratedDocumentAsync(documentId, cancellationToken) .GetAwaiter().GetResult(); diff --git a/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs b/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs index 0067a33d28229..636bbe37491eb 100644 --- a/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/LspDiagnosticsTests.cs @@ -397,7 +397,7 @@ await languageServer.ProcessDiagnosticUpdatedBatchAsync( static VisualStudioInProcLanguageServer CreateLanguageServer(Stream inputStream, Stream outputStream, TestWorkspace workspace, IDiagnosticService mockDiagnosticService) { - var dispatcherFactory = workspace.ExportProvider.GetExportedValue(); + var dispatcherFactory = workspace.ExportProvider.GetExportedValue(); var listenerProvider = workspace.ExportProvider.GetExportedValue(); var lspWorkspaceRegistrationService = workspace.ExportProvider.GetExportedValue(); var capabilitiesProvider = workspace.ExportProvider.GetExportedValue(); diff --git a/src/VisualStudio/Core/Test/Progression/SearchGraphQueryTests.vb b/src/VisualStudio/Core/Test/Progression/SearchGraphQueryTests.vb index ff84d6ee40eb9..4641ed11ca716 100644 --- a/src/VisualStudio/Core/Test/Progression/SearchGraphQueryTests.vb +++ b/src/VisualStudio/Core/Test/Progression/SearchGraphQueryTests.vb @@ -25,6 +25,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Progression ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="C", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -59,6 +60,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Progression ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="F", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -95,6 +97,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.Progression ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="M", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -144,6 +147,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="C", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -194,6 +198,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="Goo", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -243,6 +248,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="Z", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -284,6 +290,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="D.B", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -320,6 +327,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="C.B", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -344,6 +352,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="D.B", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -380,6 +389,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="A.D.B", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) @@ -416,6 +426,7 @@ End Namespace ) + testState.Workspace.SetOptions(testState.Workspace.Options.WithChangedOption(ProgressionOptions.SearchUsingNavigateToEngine, False)) Dim threadingContext = testState.Workspace.ExportProvider.GetExportedValue(Of IThreadingContext) Dim outputContext = Await testState.GetGraphContextAfterQuery( New Graph(), New SearchGraphQuery(searchPattern:="A.D.B", threadingContext, AsynchronousOperationListenerProvider.NullListener), GraphContextDirection.Custom) diff --git a/src/VisualStudio/Xaml/Impl/CodeFixes/RemoveUnnecessaryUsings/XamlRemoveUnnecessaryUsingsCodeFixProvider.cs b/src/VisualStudio/Xaml/Impl/CodeFixes/RemoveUnnecessaryUsings/XamlRemoveUnnecessaryUsingsCodeFixProvider.cs index 9771d3d3b17f3..699b695cdb236 100644 --- a/src/VisualStudio/Xaml/Impl/CodeFixes/RemoveUnnecessaryUsings/XamlRemoveUnnecessaryUsingsCodeFixProvider.cs +++ b/src/VisualStudio/Xaml/Impl/CodeFixes/RemoveUnnecessaryUsings/XamlRemoveUnnecessaryUsingsCodeFixProvider.cs @@ -60,7 +60,7 @@ private Task RemoveUnnecessaryImportsAsync( private class MyCodeAction : CodeAction.DocumentChangeAction { public MyCodeAction(Func> createChangedDocument) - : base(Resources.RemoveUnnecessaryNamespaces, createChangedDocument) + : base(Resources.RemoveUnnecessaryNamespaces, createChangedDocument, nameof(Resources.RemoveUnnecessaryNamespaces)) { } } diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index ceaff08c190dc..b0e04e3c7cfe1 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -87,10 +87,13 @@ internal override SyntaxNode DocumentationCommentTrivia(IEnumerable SyntaxFactory.List(nodes), SyntaxFactory.Token(SyntaxKind.EndOfDocumentationCommentToken)); - return docTrivia - .WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("/// ")) - .WithTrailingTrivia(trailingTrivia) - .WithTrailingTrivia( + docTrivia = docTrivia.WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("/// ")) + .WithTrailingTrivia(trailingTrivia); + + if (lastWhitespaceTrivia == default) + return docTrivia.WithTrailingTrivia(SyntaxFactory.EndOfLine(endOfLineString)); + + return docTrivia.WithTrailingTrivia( SyntaxFactory.EndOfLine(endOfLineString), lastWhitespaceTrivia); } diff --git a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs index 040d4ad5fef1f..25b448709c9d4 100644 --- a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs @@ -28,7 +28,7 @@ namespace Microsoft.CodeAnalysis.CSharp.FindSymbols internal class CSharpDeclaredSymbolInfoFactoryService : AbstractDeclaredSymbolInfoFactoryService< CompilationUnitSyntax, UsingDirectiveSyntax, - NamespaceDeclarationSyntax, + BaseNamespaceDeclarationSyntax, TypeDeclarationSyntax, EnumDeclarationSyntax, MemberDeclarationSyntax> @@ -89,7 +89,7 @@ private static void AddAliasMaps(SyntaxNode node, List GetChildren(CompilationUnitSyntax node) => node.Members; - protected override SyntaxList GetChildren(NamespaceDeclarationSyntax node) + protected override SyntaxList GetChildren(BaseNamespaceDeclarationSyntax node) => node.Members; protected override SyntaxList GetChildren(TypeDeclarationSyntax node) @@ -361,7 +361,7 @@ protected override IEnumerable GetChildren(EnumDeclarat protected override SyntaxList GetUsingAliases(CompilationUnitSyntax node) => node.Usings; - protected override SyntaxList GetUsingAliases(NamespaceDeclarationSyntax node) + protected override SyntaxList GetUsingAliases(BaseNamespaceDeclarationSyntax node) => node.Usings; private static bool IsNestedType(BaseTypeDeclarationSyntax typeDecl) diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index 85ba35de37291..9b850a7a00573 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -10099,5 +10099,21 @@ static void Main() } }", changedOptionSet: optionSet); } + + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public async Task FileScopedNamespace() + { + await AssertFormatAsync( + expected: @" +namespace NS; + +class C { } +", + code: @" +namespace NS; + + class C { } +"); + } } } diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs index 4c47010ceceba..a2dfd0dd5750f 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs @@ -430,7 +430,7 @@ internal class DocumentChangeAction : SimpleCodeAction public DocumentChangeAction( string title, Func> createChangedDocument, - string? equivalenceKey = null) + string? equivalenceKey) : base(title, equivalenceKey) { _createChangedDocument = createChangedDocument; @@ -447,7 +447,7 @@ internal class SolutionChangeAction : SimpleCodeAction public SolutionChangeAction( string title, Func> createChangedSolution, - string? equivalenceKey = null) + string? equivalenceKey) : base(title, equivalenceKey) { _createChangedSolution = createChangedSolution; @@ -461,7 +461,7 @@ internal class NoChangeAction : SimpleCodeAction { public NoChangeAction( string title, - string? equivalenceKey = null) + string? equivalenceKey) : base(title, equivalenceKey) { } diff --git a/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs b/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs index f120570649767..483f023be23b0 100644 --- a/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs +++ b/src/Workspaces/Core/Portable/Experiments/IExperimentationService.cs @@ -43,5 +43,6 @@ internal static class WellKnownExperimentNames public const string RazorLspEditorFeatureFlag = "Razor.LSP.Editor"; public const string InheritanceMargin = "Roslyn.InheritanceMargin"; public const string LspPullDiagnosticsFeatureFlag = "Lsp.PullDiagnostics"; + public const string ProgressionForceLegacySearch = "Roslyn.ProgressionForceLegacySearch"; } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index 69f4306df840d..3c47abb899ef1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -16,7 +17,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols { - using ProjectToDocumentMap = Dictionary>>; + using ProjectToDocumentMap = Dictionary>>; internal partial class FindReferencesSearchEngine { @@ -34,6 +35,8 @@ internal partial class FindReferencesSearchEngine private readonly TaskScheduler _scheduler; private static readonly TaskScheduler s_exclusiveScheduler = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler; + private readonly ConcurrentDictionary _symbolToGroup = new(); + public FindReferencesSearchEngine( Solution solution, IImmutableSet? documents, @@ -108,7 +111,7 @@ private async Task ProcessAsync(ProjectToDocumentMap projectToDocumentMap, Cance private static void ValidateProjectToDocumentMap( ProjectToDocumentMap projectToDocumentMap) { - var set = new HashSet<(SymbolGroup group, ISymbol symbol, IReferenceFinder finder)>(); + var set = new HashSet(); foreach (var documentMap in projectToDocumentMap.Values) { @@ -122,7 +125,33 @@ private static void ValidateProjectToDocumentMap( } } - private ValueTask HandleLocationAsync(SymbolGroup group, ISymbol symbol, ReferenceLocation location, CancellationToken cancellationToken) - => _progress.OnReferenceFoundAsync(group, symbol, location, cancellationToken); + private async ValueTask HandleLocationAsync(ISymbol symbol, ReferenceLocation location, CancellationToken cancellationToken) + { + var group = await GetOrCreateSymbolGroupAsync(symbol, cancellationToken).ConfigureAwait(false); + await _progress.OnReferenceFoundAsync(group, symbol, location, cancellationToken).ConfigureAwait(false); + } + + private async ValueTask GetOrCreateSymbolGroupAsync(ISymbol symbol, CancellationToken cancellationToken) + { + // See if this symbol is already associated with a symbol group. + if (!_symbolToGroup.TryGetValue(symbol, out var group)) + { + // If not, compute the group it should be associated with. + group = await DetermineSymbolGroupAsync(symbol, cancellationToken).ConfigureAwait(false); + + // now try to update our mapping. + lock (_symbolToGroup) + { + // Another thread may have beat us, so only do this if we're actually the first to get here. + if (!_symbolToGroup.TryGetValue(symbol, out _)) + { + foreach (var groupSymbol in group.Symbols) + Contract.ThrowIfFalse(_symbolToGroup.TryAdd(groupSymbol, group)); + } + } + } + + return group; + } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_DocumentProcessing.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_DocumentProcessing.cs index 9ea4179095976..7c570a68dbe33 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_DocumentProcessing.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_DocumentProcessing.cs @@ -16,7 +16,7 @@ internal partial class FindReferencesSearchEngine { private async Task ProcessDocumentQueueAsync( Document document, - HashSet<(SymbolGroup group, ISymbol symbol, IReferenceFinder finder)> documentQueue, + HashSet documentQueue, CancellationToken cancellationToken) { await _progress.OnFindInDocumentStartedAsync(document, cancellationToken).ConfigureAwait(false); @@ -29,8 +29,8 @@ private async Task ProcessDocumentQueueAsync( // start cache for this semantic model FindReferenceCache.Start(model); - foreach (var (group, symbol, finder) in documentQueue) - await ProcessDocumentAsync(document, model, group, symbol, finder, cancellationToken).ConfigureAwait(false); + foreach (var symbol in documentQueue) + await ProcessDocumentAsync(document, model, symbol, cancellationToken).ConfigureAwait(false); } finally { @@ -48,20 +48,19 @@ private async Task ProcessDocumentQueueAsync( private async Task ProcessDocumentAsync( Document document, SemanticModel semanticModel, - SymbolGroup group, ISymbol symbol, - IReferenceFinder finder, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.FindReference_ProcessDocumentAsync, s_logDocument, document, symbol, cancellationToken)) { try { - var references = await finder.FindReferencesInDocumentAsync( - symbol, document, semanticModel, _options, cancellationToken).ConfigureAwait(false); - foreach (var (_, location) in references) + foreach (var finder in _finders) { - await HandleLocationAsync(group, symbol, location, cancellationToken).ConfigureAwait(false); + var references = await finder.FindReferencesInDocumentAsync( + symbol, document, semanticModel, _options, cancellationToken).ConfigureAwait(false); + foreach (var (_, location) in references) + await HandleLocationAsync(symbol, location, cancellationToken).ConfigureAwait(false); } } finally diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs index 47010180cdfcb..b04db155d06db 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_MapCreation.cs @@ -17,9 +17,9 @@ namespace Microsoft.CodeAnalysis.FindSymbols { - using DocumentMap = Dictionary>; - using ProjectMap = Dictionary>; - using ProjectToDocumentMap = Dictionary>>; + using DocumentMap = Dictionary>; + using ProjectMap = Dictionary>; + using ProjectToDocumentMap = Dictionary>>; internal partial class FindReferencesSearchEngine { @@ -29,26 +29,26 @@ private async Task CreateProjectToDocumentMapAsync(Project { using (Logger.LogBlock(FunctionId.FindReference_CreateDocumentMapAsync, cancellationToken)) { - using var _ = ArrayBuilder, SymbolGroup, ISymbol, IReferenceFinder)>>.GetInstance(out var tasks); + using var _ = ArrayBuilder, ISymbol)>>.GetInstance(out var tasks); foreach (var (project, projectQueue) in projectMap) { - foreach (var (group, symbol, finder) in projectQueue) + foreach (var symbol in projectQueue) { tasks.Add(Task.Factory.StartNew(() => - DetermineDocumentsToSearchAsync(project, group, symbol, finder, cancellationToken), cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); + DetermineDocumentsToSearchAsync(project, symbol, cancellationToken), cancellationToken, TaskCreationOptions.None, _scheduler).Unwrap()); } } var results = await Task.WhenAll(tasks).ConfigureAwait(false); var finalMap = new ProjectToDocumentMap(); - foreach (var (documents, group, symbol, finder) in results) + foreach (var (documents, symbol) in results) { foreach (var document in documents) { finalMap.GetOrAdd(document.Project, s_createDocumentMap) - .MultiAdd(document, (group, symbol, finder)); + .MultiAdd(document, symbol); } } @@ -63,14 +63,25 @@ private async Task CreateProjectToDocumentMapAsync(Project } } - private async Task<(ImmutableArray, SymbolGroup, ISymbol, IReferenceFinder)> DetermineDocumentsToSearchAsync( - Project project, SymbolGroup group, ISymbol symbol, IReferenceFinder finder, CancellationToken cancellationToken) + private async Task<(ImmutableArray, ISymbol)> DetermineDocumentsToSearchAsync( + Project project, ISymbol symbol, CancellationToken cancellationToken) { - var documents = await finder.DetermineDocumentsToSearchAsync( - symbol, project, _documents, _options, cancellationToken).ConfigureAwait(false); - var finalDocs = documents.WhereNotNull().Distinct().Where( - d => _documents == null || _documents.Contains(d)).ToImmutableArray(); - return (finalDocs, group, symbol, finder); + using var _ = ArrayBuilder.GetInstance(out var result); + + foreach (var finder in _finders) + { + var documents = await finder.DetermineDocumentsToSearchAsync( + symbol, project, _documents, _options, cancellationToken).ConfigureAwait(false); + + foreach (var document in documents) + { + if (_documents == null || _documents.Contains(document)) + result.Add(document); + } + } + + result.RemoveDuplicates(); + return (result.ToImmutable(), symbol); } private async Task CreateProjectMapAsync(ConcurrentSet symbolGroups, CancellationToken cancellationToken) @@ -84,16 +95,14 @@ private async Task CreateProjectMapAsync(ConcurrentSet { foreach (var symbol in symbolGroup.Symbols) { - foreach (var finder in _finders) + cancellationToken.ThrowIfCancellationRequested(); + var projects = await DependentProjectsFinder.GetDependentProjectsAsync( + _solution, symbol, scope, cancellationToken).ConfigureAwait(false); + + foreach (var project in projects.Distinct().WhereNotNull()) { - cancellationToken.ThrowIfCancellationRequested(); - - var projects = await finder.DetermineProjectsToSearchAsync(symbol, _solution, scope, cancellationToken).ConfigureAwait(false); - foreach (var project in projects.Distinct().WhereNotNull()) - { - if (scope == null || scope.Contains(project)) - projectMap.MultiAdd(project, (symbolGroup, symbol, finder)); - } + if (scope == null || scope.Contains(project)) + projectMap.MultiAdd(project, symbol); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs index c1db21f5f435a..a6b0c28a10bd6 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine_ProjectProcessing.cs @@ -6,13 +6,13 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindSymbols.Finders; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { - using DocumentMap = Dictionary>; + using DocumentMap = Dictionary>; internal partial class FindReferencesSearchEngine { @@ -28,7 +28,7 @@ private async Task ProcessProjectAsync( // make sure we hold onto compilation while we search documents belong to this project var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var documentTasks = new List(); + using var _ = ArrayBuilder.GetInstance(out var documentTasks); foreach (var (document, documentQueue) in documentMap) { if (document.Project == project) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs index 113ff4ff76f93..8fbffac24712f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder.cs @@ -31,8 +31,6 @@ internal abstract partial class AbstractReferenceFinder : IReferenceFinder FindReferencesSearchOptions options, FindReferencesCascadeDirection cascadeDirection, CancellationToken cancellationToken); - public abstract Task> DetermineProjectsToSearchAsync(ISymbol symbol, Solution solution, IImmutableSet? projects, CancellationToken cancellationToken); - public abstract Task> DetermineDocumentsToSearchAsync( ISymbol symbol, Project project, IImmutableSet? documents, FindReferencesSearchOptions options, CancellationToken cancellationToken); @@ -85,18 +83,12 @@ protected static async Task> FindDocumentsAsync(Project protected static Task> FindDocumentsAsync( Project project, IImmutableSet? documents, - bool findInGlobalSuppressions, CancellationToken cancellationToken, params string[] values) { return FindDocumentsAsync(project, documents, async (d, c) => { var info = await SyntaxTreeIndex.GetRequiredIndexAsync(d, c).ConfigureAwait(false); - if (findInGlobalSuppressions && info.ContainsGlobalAttributes) - { - return true; - } - foreach (var value in values) { if (!info.ProbablyContainsIdentifier(value)) @@ -109,6 +101,21 @@ protected static Task> FindDocumentsAsync( }, cancellationToken); } + /// + /// Finds all the documents in the provided project that contain a global attribute in them. + /// + protected static Task> FindDocumentsWithGlobalAttributesAsync( + Project project, + IImmutableSet? documents, + CancellationToken cancellationToken) + { + return FindDocumentsAsync(project, documents, async (d, c) => + { + var info = await SyntaxTreeIndex.GetRequiredIndexAsync(d, c).ConfigureAwait(false); + return info.ContainsGlobalAttributes; + }, cancellationToken); + } + protected static Task> FindDocumentsAsync( Project project, IImmutableSet? documents, @@ -134,22 +141,19 @@ protected static Task> FindDocumentsAsync( CancellationToken cancellationToken) { if (op == PredefinedOperator.None) - { return SpecializedTasks.EmptyImmutableArray(); - } return FindDocumentsAsync(project, documents, async (d, c) => { var info = await SyntaxTreeIndex.GetRequiredIndexAsync(d, c).ConfigureAwait(false); - - // NOTE: Predefined operators can be referenced in global suppression attributes. - return info.ContainsPredefinedOperator(op) || info.ContainsGlobalAttributes; + return info.ContainsPredefinedOperator(op); }, cancellationToken); } protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string name, SyntaxToken token) => syntaxFacts.IsIdentifier(token) && syntaxFacts.TextMatch(token.ValueText, name); + [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] protected static ValueTask> FindReferencesInDocumentUsingIdentifierAsync( ISymbol symbol, string identifier, @@ -162,6 +166,7 @@ protected static ValueTask> FindReferencesInDocum cancellationToken: cancellationToken); } + [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] protected static ValueTask> FindReferencesInDocumentUsingIdentifierAsync( ISymbol symbol, string identifier, @@ -175,49 +180,26 @@ protected static ValueTask> FindReferencesInDocum symbol, identifier, document, semanticModel, symbolsMatch, cancellationToken); } - protected static ValueTask> FindReferencesInDocumentUsingIdentifierAsync( - ISymbol symbol, - string identifier, - Document document, - SemanticModel semanticModel, - Func> symbolsMatchAsync, - CancellationToken cancellationToken) - { - var findInGlobalSuppressions = ShouldFindReferencesInGlobalSuppressions(symbol, out var docCommentId); - return FindReferencesInDocumentUsingIdentifierAsync( - identifier, document, semanticModel, symbolsMatchAsync, - docCommentId, findInGlobalSuppressions, cancellationToken); - } - [PerformanceSensitive("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1224834", OftenCompletesSynchronously = true)] protected static async ValueTask> FindReferencesInDocumentUsingIdentifierAsync( + ISymbol _, string identifier, Document document, SemanticModel semanticModel, Func> symbolsMatchAsync, - string? docCommentId, - bool findInGlobalSuppressions, CancellationToken cancellationToken) { var tokens = await GetIdentifierOrGlobalNamespaceTokensWithTextAsync(document, semanticModel, identifier, cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); - var references = await FindReferencesInTokensAsync( + return await FindReferencesInTokensAsync( document, semanticModel, tokens, t => IdentifiersMatch(syntaxFacts, identifier, t), symbolsMatchAsync, cancellationToken).ConfigureAwait(false); - - if (!findInGlobalSuppressions) - return references; - - RoslynDebug.Assert(docCommentId != null); - var referencesInGlobalSuppressions = await FindReferencesInDocumentInsideGlobalSuppressionsAsync( - document, semanticModel, syntaxFacts, docCommentId, cancellationToken).ConfigureAwait(false); - return references.AddRange(referencesInGlobalSuppressions); } protected static async Task> GetIdentifierOrGlobalNamespaceTokensWithTextAsync(Document document, SemanticModel semanticModel, string identifier, CancellationToken cancellationToken) @@ -267,23 +249,18 @@ protected static async Task> GetIdentifierOrGlobalNa { return async (node, model) => { - var symbolInfoToMatch = FindReferenceCache.GetSymbolInfo(model, node, cancellationToken); + var symbolInfo = FindReferenceCache.GetSymbolInfo(model, node, cancellationToken); - var symbolToMatch = symbolInfoToMatch.Symbol; - var symbolToMatchCompilation = model.Compilation; - - if (await SymbolFinder.OriginalSymbolsMatchAsync(solution, searchSymbol, symbolInfoToMatch.Symbol, cancellationToken).ConfigureAwait(false)) - { + if (await SymbolFinder.OriginalSymbolsMatchAsync(solution, searchSymbol, symbolInfo.Symbol, cancellationToken).ConfigureAwait(false)) return (matched: true, CandidateReason.None); - } - else if (await symbolInfoToMatch.CandidateSymbols.AnyAsync(static (s, arg) => SymbolFinder.OriginalSymbolsMatchAsync(arg.solution, arg.searchSymbol, s, arg.cancellationToken), (solution, searchSymbol, cancellationToken)).ConfigureAwait(false)) - { - return (matched: true, symbolInfoToMatch.CandidateReason); - } - else + + foreach (var candidate in symbolInfo.CandidateSymbols) { - return (matched: false, CandidateReason.None); + if (await SymbolFinder.OriginalSymbolsMatchAsync(solution, searchSymbol, candidate, cancellationToken).ConfigureAwait(false)) + return (matched: true, symbolInfo.CandidateReason); } + + return default; }; } @@ -444,27 +421,23 @@ private static async Task> FindReferencesThroughA CancellationToken cancellationToken) { var syntaxFactsService = document.GetRequiredLanguageService(); - var allAliasReferences = ArrayBuilder.GetInstance(); + using var _ = ArrayBuilder.GetInstance(out var allAliasReferences); foreach (var aliasSymbol in aliasSymbols) { - var findInGlobalSuppressions = ShouldFindReferencesInGlobalSuppressions(aliasSymbol, out var docCommentId); - var aliasReferences = await FindReferencesInDocumentUsingIdentifierAsync( - aliasSymbol.Name, document, semanticModel, symbolsMatchAsync, - docCommentId, findInGlobalSuppressions, cancellationToken).ConfigureAwait(false); + aliasSymbol, aliasSymbol.Name, document, semanticModel, symbolsMatchAsync, cancellationToken).ConfigureAwait(false); allAliasReferences.AddRange(aliasReferences); // the alias may reference an attribute and the alias name may end with an "Attribute" suffix. In this case search for the // shortened name as well (e.g. using GooAttribute = MyNamespace.GooAttribute; [Goo] class C1 {}) if (TryGetNameWithoutAttributeSuffix(aliasSymbol.Name, syntaxFactsService, out var simpleName)) { aliasReferences = await FindReferencesInDocumentUsingIdentifierAsync( - simpleName, document, semanticModel, symbolsMatchAsync, - docCommentId, findInGlobalSuppressions, cancellationToken).ConfigureAwait(false); + aliasSymbol, simpleName, document, semanticModel, symbolsMatchAsync, cancellationToken).ConfigureAwait(false); allAliasReferences.AddRange(aliasReferences); } } - return allAliasReferences.ToImmutableAndFree(); + return allAliasReferences.ToImmutable(); } protected static Task> FindDocumentsWithPredicateAsync(Project project, IImmutableSet? documents, Func predicate, CancellationToken cancellationToken) @@ -491,11 +464,11 @@ protected static Task> FindDocumentsWithImplicitObjectC /// /// If the `node` implicitly matches the `symbol`, then it will be added to `locations`. /// - protected delegate void CollectMatchingReferences(ISymbol symbol, SyntaxNode node, - ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations); + protected delegate void CollectMatchingReferences( + SyntaxNode node, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations); protected static async Task> FindReferencesInDocumentAsync( - ISymbol symbol, + ISymbol _, Document document, Func isRelevantDocument, CollectMatchingReferences collectMatchingReferences, @@ -508,14 +481,12 @@ protected static async Task> FindReferencesInDocu var semanticFacts = document.GetRequiredLanguageService(); var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - using var _ = ArrayBuilder.GetInstance(out var locations); - - var originalUnreducedSymbolDefinition = symbol.GetOriginalUnreducedDefinition(); + using var _1 = ArrayBuilder.GetInstance(out var locations); foreach (var node in syntaxRoot.DescendantNodesAndSelf()) { cancellationToken.ThrowIfCancellationRequested(); - collectMatchingReferences(originalUnreducedSymbolDefinition, node, syntaxFacts, semanticFacts, locations); + collectMatchingReferences(node, syntaxFacts, semanticFacts, locations); } return locations.ToImmutable(); @@ -535,15 +506,15 @@ protected Task> FindReferencesInForEachStatements static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) => syntaxTreeInfo.ContainsForEachStatement; - void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, - ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) + void CollectMatchingReferences( + SyntaxNode node, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { var info = semanticFacts.GetForEachSymbols(semanticModel, node); - if (Matches(info.GetEnumeratorMethod, originalUnreducedSymbolDefinition) || - Matches(info.MoveNextMethod, originalUnreducedSymbolDefinition) || - Matches(info.CurrentProperty, originalUnreducedSymbolDefinition) || - Matches(info.DisposeMethod, originalUnreducedSymbolDefinition)) + if (Matches(info.GetEnumeratorMethod, symbol) || + Matches(info.MoveNextMethod, symbol) || + Matches(info.CurrentProperty, symbol) || + Matches(info.DisposeMethod, symbol)) { var location = node.GetFirstToken().GetLocation(); var symbolUsageInfo = GetSymbolUsageInfo(node, semanticModel, syntaxFacts, semanticFacts, cancellationToken); @@ -571,8 +542,8 @@ protected Task> FindReferencesInDeconstructionAsy static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) => syntaxTreeInfo.ContainsDeconstruction; - void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, - ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) + void CollectMatchingReferences( + SyntaxNode node, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { var deconstructMethods = semanticFacts.GetDeconstructionAssignmentMethods(semanticModel, node); if (deconstructMethods.IsEmpty) @@ -581,7 +552,7 @@ void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, Syntax deconstructMethods = semanticFacts.GetDeconstructionForEachMethods(semanticModel, node); } - if (deconstructMethods.Any(m => Matches(m, originalUnreducedSymbolDefinition))) + if (deconstructMethods.Any(m => Matches(m, symbol))) { var location = syntaxFacts.GetDeconstructionReferenceLocation(node); var symbolUsageInfo = GetSymbolUsageInfo(node, semanticModel, syntaxFacts, semanticFacts, cancellationToken); @@ -603,12 +574,12 @@ protected Task> FindReferencesInAwaitExpressionAs static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) => syntaxTreeInfo.ContainsAwait; - void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, - ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) + void CollectMatchingReferences( + SyntaxNode node, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { var awaitExpressionMethod = semanticFacts.GetGetAwaiterMethod(semanticModel, node); - if (Matches(awaitExpressionMethod, originalUnreducedSymbolDefinition)) + if (Matches(awaitExpressionMethod, symbol)) { var location = node.GetFirstToken().GetLocation(); var symbolUsageInfo = GetSymbolUsageInfo(node, semanticModel, syntaxFacts, semanticFacts, cancellationToken); @@ -630,8 +601,8 @@ protected Task> FindReferencesInImplicitObjectCre static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) => syntaxTreeInfo.ContainsImplicitObjectCreation; - void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, - ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) + void CollectMatchingReferences( + SyntaxNode node, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { if (!syntaxFacts.IsImplicitObjectCreation(node)) { @@ -641,7 +612,7 @@ void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, Syntax var constructor = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol; - if (Matches(constructor, originalUnreducedSymbolDefinition)) + if (Matches(constructor, symbol)) { var location = node.GetFirstToken().GetLocation(); var symbolUsageInfo = GetSymbolUsageInfo(node, semanticModel, syntaxFacts, semanticFacts, cancellationToken); @@ -652,11 +623,12 @@ void CollectMatchingReferences(ISymbol originalUnreducedSymbolDefinition, Syntax } } - protected static bool Matches(ISymbol? symbol1, ISymbol notNulloriginalUnreducedSymbol2) + protected static bool Matches(ISymbol? symbol1, ISymbol notNullOriginalUnreducedSymbol2) { + Contract.ThrowIfFalse(notNullOriginalUnreducedSymbol2.GetOriginalUnreducedDefinition().Equals(notNullOriginalUnreducedSymbol2)); return symbol1 != null && SymbolEquivalenceComparer.Instance.Equals( symbol1.GetOriginalUnreducedDefinition(), - notNulloriginalUnreducedSymbol2); + notNullOriginalUnreducedSymbol2); } protected static SymbolUsageInfo GetSymbolUsageInfo( @@ -907,13 +879,6 @@ protected abstract ValueTask> FindReferencesInDoc TSymbol symbol, Document document, SemanticModel semanticModel, FindReferencesSearchOptions options, CancellationToken cancellationToken); - public override Task> DetermineProjectsToSearchAsync(ISymbol symbol, Solution solution, IImmutableSet? projects, CancellationToken cancellationToken) - { - return symbol is TSymbol typedSymbol && CanFind(typedSymbol) - ? DetermineProjectsToSearchAsync(typedSymbol, solution, projects, cancellationToken) - : SpecializedTasks.EmptyImmutableArray(); - } - public override Task> DetermineDocumentsToSearchAsync( ISymbol symbol, Project project, IImmutableSet? documents, FindReferencesSearchOptions options, CancellationToken cancellationToken) @@ -948,13 +913,6 @@ symbol is TSymbol typedSymbol && return SpecializedTasks.EmptyImmutableArray<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>(); } - protected virtual Task> DetermineProjectsToSearchAsync( - TSymbol symbol, Solution solution, IImmutableSet? projects, CancellationToken cancellationToken) - { - return DependentProjectsFinder.GetDependentProjectsAsync( - solution, symbol, projects, cancellationToken); - } - protected virtual Task> DetermineCascadedSymbolsAsync( TSymbol symbol, Solution solution, IImmutableSet? projects, FindReferencesSearchOptions options, FindReferencesCascadeDirection cascadeDirection, @@ -1026,10 +984,8 @@ protected static ValueTask> FindReferencesInDocum Func? findParentNode, CancellationToken cancellationToken) { - var findInGlobalSuppressions = ShouldFindReferencesInGlobalSuppressions(symbol, out var docCommentId); var symbolsMatchAsync = GetStandardSymbolsMatchFunction(symbol, findParentNode, document.Project.Solution, cancellationToken); - return FindReferencesInDocumentAsync(document, semanticModel, tokensMatch, - symbolsMatchAsync, docCommentId, findInGlobalSuppressions, cancellationToken); + return FindReferencesInDocumentAsync(document, semanticModel, tokensMatch, symbolsMatchAsync, cancellationToken); } protected static async ValueTask> FindReferencesInDocumentAsync( @@ -1037,24 +993,13 @@ protected static async ValueTask> FindReferencesI SemanticModel semanticModel, Func tokensMatch, Func> symbolsMatchAsync, - string? docCommentId, - bool findInGlobalSuppressions, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Now that we have Doc Comments in place, We are searching for References in the Trivia as well by setting descendIntoTrivia: true var tokens = root.DescendantTokens(descendIntoTrivia: true); - var references = await FindReferencesInTokensAsync(document, semanticModel, tokens, tokensMatch, symbolsMatchAsync, cancellationToken).ConfigureAwait(false); - - if (!findInGlobalSuppressions) - return references; - - RoslynDebug.Assert(docCommentId != null); - var syntaxFacts = document.GetRequiredLanguageService(); - var referencesInGlobalSuppressions = await FindReferencesInDocumentInsideGlobalSuppressionsAsync( - document, semanticModel, syntaxFacts, docCommentId, cancellationToken).ConfigureAwait(false); - return references.AddRange(referencesInGlobalSuppressions); + return await FindReferencesInTokensAsync(document, semanticModel, tokens, tokensMatch, symbolsMatchAsync, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder_GlobalSuppressions.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder_GlobalSuppressions.cs index 5450352c9ed44..2c9d3ecb5c206 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder_GlobalSuppressions.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractReferenceFinder_GlobalSuppressions.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols.Finders { internal abstract partial class AbstractReferenceFinder : IReferenceFinder { - protected static bool ShouldFindReferencesInGlobalSuppressions(ISymbol symbol, [NotNullWhen(returnValue: true)] out string? documentationCommentId) + private static bool ShouldFindReferencesInGlobalSuppressions(ISymbol symbol, [NotNullWhen(returnValue: true)] out string? documentationCommentId) { if (!SupportsGlobalSuppression(symbol)) { @@ -54,29 +54,27 @@ static bool SupportsGlobalSuppression(ISymbol symbol) protected static async ValueTask> FindReferencesInDocumentInsideGlobalSuppressionsAsync( Document document, SemanticModel semanticModel, - ISyntaxFacts syntaxFacts, - string docCommentId, + ISymbol symbol, CancellationToken cancellationToken) { + if (!ShouldFindReferencesInGlobalSuppressions(symbol, out var docCommentId)) + return ImmutableArray.Empty; + // Check if we have any relevant global attributes in this document. var info = await SyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); if (!info.ContainsGlobalAttributes) - { return ImmutableArray.Empty; - } var suppressMessageAttribute = semanticModel.Compilation.SuppressMessageAttributeType(); if (suppressMessageAttribute == null) - { return ImmutableArray.Empty; - } // Check if we have any instances of the symbol documentation comment ID string literals within global attributes. // These string literals represent references to the symbol. if (!TryGetExpectedDocumentationCommentId(docCommentId, out var expectedDocCommentId)) - { return ImmutableArray.Empty; - } + + var syntaxFacts = document.GetRequiredLanguageService(); // We map the positions of documentation ID literals in tree to string literal tokens, // perform semantic checks to ensure these are valid references to the symbol diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs index 07e8bafc0afb6..992adf5d97b37 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs @@ -38,20 +38,20 @@ protected override async Task> DetermineDocumentsToSear CancellationToken cancellationToken) { var typeName = symbol.ContainingType.Name; - var documentsWithName = await FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, typeName).ConfigureAwait(false); + + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, typeName).ConfigureAwait(false); var documentsWithType = await FindDocumentsAsync(project, documents, symbol.ContainingType.SpecialType.ToPredefinedType(), cancellationToken).ConfigureAwait(false); + var documentsWithAttribute = TryGetNameWithoutAttributeSuffix(typeName, project.LanguageServices.GetRequiredService(), out var simpleName) - ? await FindDocumentsAsync(project, documents, findInGlobalSuppressions: false, cancellationToken, simpleName).ConfigureAwait(false) - : SpecializedCollections.EmptyEnumerable(); + ? await FindDocumentsAsync(project, documents, cancellationToken, simpleName).ConfigureAwait(false) + : ImmutableArray.Empty; var documentsWithImplicitObjectCreations = symbol.MethodKind == MethodKind.Constructor ? await FindDocumentsWithImplicitObjectCreationExpressionAsync(project, documents, cancellationToken).ConfigureAwait(false) : ImmutableArray.Empty; - return documentsWithName.Concat(documentsWithType, documentsWithImplicitObjectCreations) - .Concat(documentsWithAttribute) - .Distinct() - .ToImmutableArray(); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithName.Concat(documentsWithType, documentsWithImplicitObjectCreations, documentsWithAttribute, documentsWithGlobalAttributes); } private static bool IsPotentialReference( @@ -84,10 +84,13 @@ internal async ValueTask> FindAllReferencesInDocu var normalReferences = await FindReferencesInDocumentWorkerAsync(methodSymbol, document, semanticModel, findParentNode, cancellationToken).ConfigureAwait(false); var nonAliasTypeReferences = await NamedTypeSymbolReferenceFinder.FindNonAliasReferencesAsync(methodSymbol.ContainingType, document, semanticModel, cancellationToken).ConfigureAwait(false); + var aliasReferences = await FindAliasReferencesAsync( nonAliasTypeReferences, methodSymbol, document, semanticModel, findParentNode, cancellationToken).ConfigureAwait(false); - return normalReferences.Concat(aliasReferences); + + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, methodSymbol, cancellationToken).ConfigureAwait(false); + return normalReferences.Concat(aliasReferences, suppressionReferences); } private async Task> FindReferencesInDocumentWorkerAsync( @@ -170,9 +173,7 @@ static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo) => syntaxTreeInfo.ContainsImplicitObjectCreation; void CollectMatchingReferences( - ISymbol originalUnreducedSymbolDefinition, SyntaxNode node, - ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, - ArrayBuilder locations) + SyntaxNode node, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, ArrayBuilder locations) { if (!syntaxFacts.IsImplicitObjectCreationExpression(node)) return; @@ -187,7 +188,7 @@ void CollectMatchingReferences( return; var constructor = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol; - if (Matches(constructor, originalUnreducedSymbolDefinition)) + if (Matches(constructor, symbol)) { var location = node.GetFirstToken().GetLocation(); var symbolUsageInfo = GetSymbolUsageInfo(node, semanticModel, syntaxFacts, semanticFacts, cancellationToken); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs index 059f739c5c777..f9fb71287efab 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs @@ -37,14 +37,16 @@ protected override bool CanFind(IEventSymbol symbol) .Concat(associatedNamedTypes.SelectAsArray(n => ((ISymbol)n, cascadeDirection))); } - protected override Task> DetermineDocumentsToSearchAsync( + protected override async Task> DetermineDocumentsToSearchAsync( IEventSymbol symbol, Project project, IImmutableSet? documents, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, symbol.Name); + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, symbol.Name).ConfigureAwait(false); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithName.Concat(documentsWithGlobalAttributes); } protected override ValueTask> FindReferencesInDocumentAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs index 5ac344b258d8f..ce6d27ca569d1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs @@ -40,7 +40,7 @@ protected override async Task> DetermineDocumentsToSear var underlyingNamedType = GetUnderlyingNamedType(symbol.ReturnType); Contract.ThrowIfNull(underlyingNamedType); - var documentsWithName = await FindDocumentsAsync(project, documents, findInGlobalSuppressions: false, cancellationToken, underlyingNamedType.Name).ConfigureAwait(false); + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, underlyingNamedType.Name).ConfigureAwait(false); var documentsWithType = await FindDocumentsAsync(project, documents, underlyingNamedType.SpecialType.ToPredefinedType(), cancellationToken).ConfigureAwait(false); using var _ = ArrayBuilder.GetInstance(out var result); diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/FieldSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/FieldSymbolReferenceFinder.cs index 829999659d2f6..ccc563fb70130 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/FieldSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/FieldSymbolReferenceFinder.cs @@ -27,24 +27,28 @@ protected override bool CanFind(IFieldSymbol symbol) : SpecializedTasks.EmptyImmutableArray<(ISymbol symbol, FindReferencesCascadeDirection cascadeDirection)>(); } - protected override Task> DetermineDocumentsToSearchAsync( + protected override async Task> DetermineDocumentsToSearchAsync( IFieldSymbol symbol, Project project, IImmutableSet? documents, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, symbol.Name); + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, symbol.Name).ConfigureAwait(false); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithName.Concat(documentsWithGlobalAttributes); } - protected override ValueTask> FindReferencesInDocumentAsync( + protected override async ValueTask> FindReferencesInDocumentAsync( IFieldSymbol symbol, Document document, SemanticModel semanticModel, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return FindReferencesInDocumentUsingSymbolNameAsync(symbol, document, semanticModel, cancellationToken); + var nameReferences = await FindReferencesInDocumentUsingSymbolNameAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, symbol, cancellationToken).ConfigureAwait(false); + return nameReferences.Concat(suppressionReferences); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/IReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/IReferenceFinder.cs index d6219a3c8f7ab..6284cab2e7785 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/IReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/IReferenceFinder.cs @@ -29,22 +29,6 @@ internal interface IReferenceFinder FindReferencesSearchOptions options, FindReferencesCascadeDirection cascadeDirection, CancellationToken cancellationToken); - /// - /// Called by the find references search engine to determine which projects should be - /// searched for a given symbol. The returned projects will then be searched in parallel. If - /// the implementation does not care about the provided symbol then null can be returned - /// from this method. - /// - /// Implementations should endeavor to keep the list of returned projects as small as - /// possible to keep search time down to a minimum. Returning the entire list of projects in - /// a solution is not recommended (unless, of course, there is reasonable reason to believe - /// there are references in every project). - /// - /// Implementations of this method must be thread-safe. - /// - Task> DetermineProjectsToSearchAsync( - ISymbol symbol, Solution solution, IImmutableSet? projects, CancellationToken cancellationToken); - /// /// Called by the find references search engine to determine which documents in the supplied /// project need to be searched for references. Only projects returned by diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs index a2b57b754e0ee..5a412595189c7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs @@ -61,7 +61,7 @@ protected override Task> DetermineDocumentsToSearchAsyn // Also, we only look for files that have the name of the owning type. This helps filter // down the set considerably. Contract.ThrowIfNull(symbol.DeclaringMethod); - return FindDocumentsAsync(project, documents, findInGlobalSuppressions: false, cancellationToken, symbol.Name, + return FindDocumentsAsync(project, documents, cancellationToken, symbol.Name, GetMemberNameWithoutInterfaceName(symbol.DeclaringMethod.Name), symbol.DeclaringMethod.ContainingType.Name); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamedTypeSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamedTypeSymbolReferenceFinder.cs index 7ff58d482f381..80aa784e30f5d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamedTypeSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamedTypeSymbolReferenceFinder.cs @@ -54,14 +54,15 @@ protected override async Task> DetermineDocumentsToSear { var syntaxFacts = project.LanguageServices.GetRequiredService(); - var documentsWithName = await FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, symbol.Name).ConfigureAwait(false); + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, symbol.Name).ConfigureAwait(false); var documentsWithType = await FindDocumentsAsync(project, documents, symbol.SpecialType.ToPredefinedType(), cancellationToken).ConfigureAwait(false); + var documentsWithAttribute = TryGetNameWithoutAttributeSuffix(symbol.Name, syntaxFacts, out var simpleName) - ? await FindDocumentsAsync(project, documents, findInGlobalSuppressions: false, cancellationToken, simpleName).ConfigureAwait(false) + ? await FindDocumentsAsync(project, documents, cancellationToken, simpleName).ConfigureAwait(false) : ImmutableArray.Empty; - return documentsWithName.Concat(documentsWithType) - .Concat(documentsWithAttribute); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithName.Concat(documentsWithType, documentsWithAttribute, documentsWithGlobalAttributes); } private static bool IsPotentialReference( @@ -84,7 +85,8 @@ protected override async ValueTask> FindReference var nonAliasReferences = await FindNonAliasReferencesAsync(namedType, document, semanticModel, cancellationToken).ConfigureAwait(false); var symbolsMatch = GetStandardSymbolsMatchFunction(namedType, null, document.Project.Solution, cancellationToken); var aliasReferences = await FindAliasReferencesAsync(nonAliasReferences, document, semanticModel, symbolsMatch, cancellationToken).ConfigureAwait(false); - return nonAliasReferences.Concat(aliasReferences); + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, namedType, cancellationToken).ConfigureAwait(false); + return nonAliasReferences.Concat(aliasReferences, suppressionReferences); } internal static async ValueTask> FindNonAliasReferencesAsync( @@ -132,8 +134,6 @@ private static ValueTask> FindPredefinedTypeRefer return FindReferencesInDocumentAsync(document, semanticModel, t => IsPotentialReference(predefinedType, syntaxFacts, t), (t, m) => ValueTaskFactory.FromResult((matched: true, reason: CandidateReason.None)), - docCommentId: null, - findInGlobalSuppressions: false, cancellationToken); } @@ -146,8 +146,7 @@ private static ValueTask> FindAttributeReferences var symbolsMatch = GetStandardSymbolsMatchFunction(namedType, null, document.Project.Solution, cancellationToken); var syntaxFacts = document.GetRequiredLanguageService(); return TryGetNameWithoutAttributeSuffix(namedType.Name, syntaxFacts, out var simpleName) - ? FindReferencesInDocumentUsingIdentifierAsync(simpleName, document, semanticModel, - symbolsMatch, docCommentId: null, findInGlobalSuppressions: false, cancellationToken) + ? FindReferencesInDocumentUsingIdentifierAsync(namedType, simpleName, document, semanticModel, symbolsMatch, cancellationToken) : new ValueTask>(ImmutableArray.Empty); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamespaceSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamespaceSymbolReferenceFinder.cs index d12c73a8fb677..f4c5df42ff47b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamespaceSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/NamespaceSymbolReferenceFinder.cs @@ -17,14 +17,16 @@ internal class NamespaceSymbolReferenceFinder : AbstractReferenceFinder true; - protected override Task> DetermineDocumentsToSearchAsync( + protected override async Task> DetermineDocumentsToSearchAsync( INamespaceSymbol symbol, Project project, IImmutableSet? documents, FindReferencesSearchOptions options, CancellationToken cancellationToken) { - return FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, GetNamespaceIdentifierName(symbol)); + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, GetNamespaceIdentifierName(symbol)).ConfigureAwait(false); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithGlobalAttributes.Concat(documentsWithName); } private static string GetNamespaceIdentifierName(INamespaceSymbol symbol) @@ -55,12 +57,8 @@ protected override async ValueTask> FindReference var aliasReferences = await FindAliasReferencesAsync(nonAliasReferences, symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); - var suppressionReferences = ShouldFindReferencesInGlobalSuppressions(symbol, out var docCommentId) - ? await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, - syntaxFacts, docCommentId, cancellationToken).ConfigureAwait(false) - : ImmutableArray.Empty; - - return nonAliasReferences.Concat(aliasReferences).Concat(suppressionReferences); + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, symbol, cancellationToken).ConfigureAwait(false); + return nonAliasReferences.Concat(aliasReferences, suppressionReferences); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs index 901b9ad3be443..97b038df27d23 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OperatorSymbolReferenceFinder.cs @@ -15,7 +15,7 @@ internal class OperatorSymbolReferenceFinder : AbstractMethodOrPropertyOrEventSy protected override bool CanFind(IMethodSymbol symbol) => symbol.MethodKind == MethodKind.UserDefinedOperator; - protected override Task> DetermineDocumentsToSearchAsync( + protected override async Task> DetermineDocumentsToSearchAsync( IMethodSymbol symbol, Project project, IImmutableSet? documents, @@ -23,10 +23,12 @@ protected override Task> DetermineDocumentsToSearchAsyn CancellationToken cancellationToken) { var op = symbol.GetPredefinedOperator(); - return FindDocumentsAsync(project, documents, op, cancellationToken); + var documentsWithOp = await FindDocumentsAsync(project, documents, op, cancellationToken).ConfigureAwait(false); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithOp.Concat(documentsWithGlobalAttributes); } - protected override ValueTask> FindReferencesInDocumentAsync( + protected override async ValueTask> FindReferencesInDocumentAsync( IMethodSymbol symbol, Document document, SemanticModel semanticModel, @@ -36,9 +38,12 @@ protected override ValueTask> FindReferencesInDoc var syntaxFacts = document.GetRequiredLanguageService(); var op = symbol.GetPredefinedOperator(); - return FindReferencesInDocumentAsync(symbol, document, semanticModel, t => + var opReferences = await FindReferencesInDocumentAsync(symbol, document, semanticModel, t => IsPotentialReference(syntaxFacts, op, t), - cancellationToken); + cancellationToken).ConfigureAwait(false); + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, symbol, cancellationToken).ConfigureAwait(false); + + return opReferences.Concat(suppressionReferences); } private static bool IsPotentialReference( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs index c147242d317eb..277c7e024977d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/OrdinaryMethodReferenceFinder.cs @@ -84,7 +84,7 @@ protected override async Task> DetermineDocumentsToSear // searches for these, then we should find usages of 'lock(goo)' or 'synclock(goo)' // since they implicitly call those methods. - var ordinaryDocuments = await FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, methodSymbol.Name).ConfigureAwait(false); + var ordinaryDocuments = await FindDocumentsAsync(project, documents, cancellationToken, methodSymbol.Name).ConfigureAwait(false); var forEachDocuments = IsForEachMethod(methodSymbol) ? await FindDocumentsWithForEachStatementsAsync(project, documents, cancellationToken).ConfigureAwait(false) : ImmutableArray.Empty; @@ -97,7 +97,8 @@ protected override async Task> DetermineDocumentsToSear ? await FindDocumentsWithAwaitExpressionAsync(project, documents, cancellationToken).ConfigureAwait(false) : ImmutableArray.Empty; - return ordinaryDocuments.Concat(forEachDocuments).Concat(deconstructDocuments).Concat(awaitExpressionDocuments); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return ordinaryDocuments.Concat(forEachDocuments, deconstructDocuments, awaitExpressionDocuments, documentsWithGlobalAttributes); } private static bool IsForEachMethod(IMethodSymbol methodSymbol) @@ -120,31 +121,22 @@ protected override async ValueTask> FindReference FindReferencesSearchOptions options, CancellationToken cancellationToken) { - var nameMatches = await FindReferencesInDocumentUsingSymbolNameAsync( - symbol, - document, - semanticModel, - cancellationToken).ConfigureAwait(false); + var nameMatches = await FindReferencesInDocumentUsingSymbolNameAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); - if (IsForEachMethod(symbol)) - { - var forEachMatches = await FindReferencesInForEachStatementsAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); - nameMatches = nameMatches.Concat(forEachMatches); - } + var forEachMatches = IsForEachMethod(symbol) + ? await FindReferencesInForEachStatementsAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false) + : ImmutableArray.Empty; - if (IsDeconstructMethod(symbol)) - { - var deconstructMatches = await FindReferencesInDeconstructionAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); - nameMatches = nameMatches.Concat(deconstructMatches); - } + var deconstructMatches = IsDeconstructMethod(symbol) + ? await FindReferencesInDeconstructionAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false) + : ImmutableArray.Empty; - if (IsGetAwaiterMethod(symbol)) - { - var getAwaiterMatches = await FindReferencesInAwaitExpressionAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false); - nameMatches = nameMatches.Concat(getAwaiterMatches); - } + var getAwaiterMatches = IsGetAwaiterMethod(symbol) + ? await FindReferencesInAwaitExpressionAsync(symbol, document, semanticModel, cancellationToken).ConfigureAwait(false) + : ImmutableArray.Empty; - return nameMatches; + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, symbol, cancellationToken).ConfigureAwait(false); + return nameMatches.Concat(forEachMatches, deconstructMatches, getAwaiterMatches, suppressionReferences); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs index 68d79f6ce6aea..2c3b49348348c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ParameterSymbolReferenceFinder.cs @@ -31,7 +31,7 @@ protected override Task> DetermineDocumentsToSearchAsyn // elsewhere as "paramName:" or "paramName:=". We can narrow the search by // filtering down to matches of that form. For now we just return any document // that references something with this name. - return FindDocumentsAsync(project, documents, findInGlobalSuppressions: false, cancellationToken, symbol.Name); + return FindDocumentsAsync(project, documents, cancellationToken, symbol.Name); } protected override ValueTask> FindReferencesInDocumentAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs index f138a610f5a93..7c6cf9cdc3a13 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertyAccessorSymbolReferenceFinder.cs @@ -41,24 +41,23 @@ protected override async Task> DetermineDocumentsToSear // First, find any documents with the full name of the accessor (i.e. get_Goo). // This will find explicit calls to the method (which can happen when C# references // a VB parameterized property). - var result = await FindDocumentsAsync( - project, documents, findInGlobalSuppressions: true, cancellationToken, symbol.Name).ConfigureAwait(false); + var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, symbol.Name).ConfigureAwait(false); + var propertyDocuments = ImmutableArray.Empty; if (symbol.AssociatedSymbol is IPropertySymbol property && options.AssociatePropertyReferencesWithSpecificAccessor) { // we want to associate normal property references with the specific accessor being // referenced. So we also need to include documents with our property's name. Just // defer to the Property finder to find these docs and combine them with the result. - var propertyDocuments = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( + propertyDocuments = await ReferenceFinders.Property.DetermineDocumentsToSearchAsync( property, project, documents, options.With(associatePropertyReferencesWithSpecificAccessor: false), cancellationToken).ConfigureAwait(false); - - result = result.AddRange(propertyDocuments); } - return result; + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return documentsWithName.Concat(propertyDocuments, documentsWithGlobalAttributes); } protected override async ValueTask> FindReferencesInDocumentAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs index f91b23d9ebc7c..63f3f140c535a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PropertySymbolReferenceFinder.cs @@ -57,7 +57,7 @@ protected override async Task> DetermineDocumentsToSear FindReferencesSearchOptions options, CancellationToken cancellationToken) { - var ordinaryDocuments = await FindDocumentsAsync(project, documents, findInGlobalSuppressions: true, cancellationToken, symbol.Name).ConfigureAwait(false); + var ordinaryDocuments = await FindDocumentsAsync(project, documents, cancellationToken, symbol.Name).ConfigureAwait(false); var forEachDocuments = IsForEachProperty(symbol) ? await FindDocumentsWithForEachStatementsAsync(project, documents, cancellationToken).ConfigureAwait(false) @@ -71,9 +71,8 @@ protected override async Task> DetermineDocumentsToSear ? await FindDocumentWithIndexerMemberCrefAsync(project, documents, cancellationToken).ConfigureAwait(false) : ImmutableArray.Empty; - return ordinaryDocuments.Concat(forEachDocuments) - .Concat(elementAccessDocument) - .Concat(indexerMemberCrefDocument); + var documentsWithGlobalAttributes = await FindDocumentsWithGlobalAttributesAsync(project, documents, cancellationToken).ConfigureAwait(false); + return ordinaryDocuments.Concat(forEachDocuments, elementAccessDocument, indexerMemberCrefDocument, documentsWithGlobalAttributes); } private static bool IsForEachProperty(IPropertySymbol symbol) @@ -110,8 +109,8 @@ protected override async ValueTask> FindReference ? await FindIndexerReferencesAsync(symbol, document, semanticModel, options, cancellationToken).ConfigureAwait(false) : ImmutableArray.Empty; - return nameReferences.Concat(forEachReferences) - .Concat(indexerReferences); + var suppressionReferences = await FindReferencesInDocumentInsideGlobalSuppressionsAsync(document, semanticModel, symbol, cancellationToken).ConfigureAwait(false); + return nameReferences.Concat(forEachReferences, indexerReferences, suppressionReferences); } private static Task> FindDocumentWithElementAccessExpressionsAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs index 124111318696f..56835c1bd83c1 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs @@ -27,7 +27,7 @@ protected override Task> DetermineDocumentsToSearchAsyn // parameter has a different name in different parts that we won't find it. However, // this only happens in error situations. It is not legal in C# to use a different // name for a type parameter in different parts. - return FindDocumentsAsync(project, documents, findInGlobalSuppressions: false, cancellationToken, symbol.Name, symbol.ContainingType.Name); + return FindDocumentsAsync(project, documents, cancellationToken, symbol.Name, symbol.ContainingType.Name); } protected override ValueTask> FindReferencesInDocumentAsync( diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs index 853461ea064cf..ddce77cdad465 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxNodeExtensions.cs @@ -1046,7 +1046,7 @@ public static IEnumerable GetMembers(this SyntaxNode? n { case CompilationUnitSyntax compilation: return compilation.Members; - case NamespaceDeclarationSyntax @namespace: + case BaseNamespaceDeclarationSyntax @namespace: return @namespace.Members; case TypeDeclarationSyntax type: return type.Members; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 49c4a03e97194..db4360bfcb6d5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -773,7 +773,7 @@ node is EnumMemberDeclarationSyntax || public bool IsTopLevelNodeWithMembers([NotNullWhen(true)] SyntaxNode? node) { - return node is NamespaceDeclarationSyntax || + return node is BaseNamespaceDeclarationSyntax || node is TypeDeclarationSyntax || node is EnumDeclarationSyntax; } @@ -857,7 +857,8 @@ public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, strin case SyntaxKind.IncompleteMember: return missingTokenPlaceholder; case SyntaxKind.NamespaceDeclaration: - return GetName(((NamespaceDeclarationSyntax)node).Name, options); + case SyntaxKind.FileScopedNamespaceDeclaration: + return GetName(((BaseNamespaceDeclarationSyntax)node).Name, options); case SyntaxKind.QualifiedName: var qualified = (QualifiedNameSyntax)node; return GetName(qualified.Left, options) + dotToken + GetName(qualified.Right, options); @@ -1133,7 +1134,7 @@ private void AppendConstructors(SyntaxList members, Lis case ConstructorDeclarationSyntax constructor: constructors.Add(constructor); continue; - case NamespaceDeclarationSyntax @namespace: + case BaseNamespaceDeclarationSyntax @namespace: AppendConstructors(@namespace.Members, constructors, cancellationToken); break; case ClassDeclarationSyntax @class: diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index d29e741700762..769e4ae49edb8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -504,7 +504,8 @@ internal enum FunctionId CommandHandler_Paste_ImportsOnPaste = 470, - FindDocumentInWorkspace = 480, + // Superseded by LSP_FindDocumentInWorkspace + // obsolete: FindDocumentInWorkspace = 480, RegisterWorkspace = 481, LSP_RequestCounter = 482, @@ -524,5 +525,7 @@ internal enum FunctionId ValueTracking_TrackValueSource = 491, InheritanceMargin_GetInheritanceMemberItems = 492, + + LSP_FindDocumentInWorkspace = 493, } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs index 264d765f026a2..1b77ea3cb8f13 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs @@ -46,17 +46,6 @@ public bool AreEquivalent(ISymbol? x, ISymbol? y, Dictionary x, ImmutableArray _symbolEquivalenceComparer._ignoreNullableAnnotations || x.NullableAnnotation == y.NullableAnnotation; + private bool AreEquivalentWorker(ISymbol x, ISymbol y, SymbolKind k, Dictionary? equivalentTypesWithDifferingAssemblies) { Debug.Assert(x.Kind == y.Kind && x.Kind == k); @@ -125,7 +117,7 @@ private bool AreEquivalentWorker(ISymbol x, ISymbol y, SymbolKind k, Dictionary< { SymbolKind.ArrayType => ArrayTypesAreEquivalent((IArrayTypeSymbol)x, (IArrayTypeSymbol)y, equivalentTypesWithDifferingAssemblies), SymbolKind.Assembly => AssembliesAreEquivalent((IAssemblySymbol)x, (IAssemblySymbol)y), - SymbolKind.DynamicType => true, + SymbolKind.DynamicType => NullableAnnotationsEquivalent((IDynamicTypeSymbol)x, (IDynamicTypeSymbol)y), SymbolKind.Event => EventsAreEquivalent((IEventSymbol)x, (IEventSymbol)y, equivalentTypesWithDifferingAssemblies), SymbolKind.Field => FieldsAreEquivalent((IFieldSymbol)x, (IFieldSymbol)y, equivalentTypesWithDifferingAssemblies), SymbolKind.Label => LabelsAreEquivalent((ILabelSymbol)x, (ILabelSymbol)y), @@ -151,7 +143,8 @@ private bool ArrayTypesAreEquivalent(IArrayTypeSymbol x, IArrayTypeSymbol y, Dic return x.Rank == y.Rank && AreEquivalent(x.CustomModifiers, y.CustomModifiers, equivalentTypesWithDifferingAssemblies) && - AreEquivalent(x.ElementType, y.ElementType, equivalentTypesWithDifferingAssemblies); + AreEquivalent(x.ElementType, y.ElementType, equivalentTypesWithDifferingAssemblies) && + NullableAnnotationsEquivalent(x, y); } private bool AssembliesAreEquivalent(IAssemblySymbol x, IAssemblySymbol y) @@ -339,19 +332,20 @@ private bool HandleNamedTypesWorker(INamedTypeSymbol x, INamedTypeSymbol y, Dict { Debug.Assert(GetTypeKind(x) == GetTypeKind(y)); + if (x.IsTupleType) + return HandleTupleTypes(x, y, equivalentTypesWithDifferingAssemblies); + if (IsConstructedFromSelf(x) != IsConstructedFromSelf(y) || x.Arity != y.Arity || x.Name != y.Name || x.IsAnonymousType != y.IsAnonymousType || x.IsUnboundGenericType != y.IsUnboundGenericType || - x.IsTupleType != y.IsTupleType) + x.IsTupleType != y.IsTupleType || + !NullableAnnotationsEquivalent(x, y)) { return false; } - if (x.IsTupleType) - return HandleTupleTypes(x, y, equivalentTypesWithDifferingAssemblies); - if (x.Kind == SymbolKind.ErrorType && x.ContainingSymbol is INamespaceSymbol xNamespace && y.ContainingSymbol is INamespaceSymbol yNamespace) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs index 5ccb84400ffa7..ad77d06cf35a0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.GetHashCodeVisitor.cs @@ -47,12 +47,15 @@ public int GetHashCode(ISymbol? x, int currentHash) if (x.Kind == SymbolKind.DynamicType || (_objectAndDynamicCompareEqually && IsObjectType(x))) { - return Hash.Combine(typeof(IDynamicTypeSymbol), currentHash); + return Hash.Combine(GetNullableAnnotationsHashCode((ITypeSymbol)x), Hash.Combine(typeof(IDynamicTypeSymbol), currentHash)); } return GetHashCodeWorker(x, currentHash); } + private int GetNullableAnnotationsHashCode(ITypeSymbol type) + => _symbolEquivalenceComparer._ignoreNullableAnnotations ? 0 : type.NullableAnnotation.GetHashCode(); + private int GetHashCodeWorker(ISymbol x, int currentHash) => x.Kind switch { @@ -78,8 +81,9 @@ private int GetHashCodeWorker(ISymbol x, int currentHash) private int CombineHashCodes(IArrayTypeSymbol x, int currentHash) { return + Hash.Combine(GetNullableAnnotationsHashCode(x), Hash.Combine(x.Rank, - GetHashCode(x.ElementType, currentHash)); + GetHashCode(x.ElementType, currentHash))); } private int CombineHashCodes(IAssemblySymbol x, int currentHash) @@ -167,13 +171,13 @@ private int CombineNamedTypeHashCode(INamedTypeSymbol x, int currentHash) // If we want object and dynamic to be the same, and this is 'object', then return // the same hash we do for 'dynamic'. currentHash = - Hash.Combine(x.IsDefinition, + Hash.Combine((int)GetTypeKind(x), Hash.Combine(IsConstructedFromSelf(x), Hash.Combine(x.Arity, - Hash.Combine((int)GetTypeKind(x), Hash.Combine(x.Name, Hash.Combine(x.IsAnonymousType, Hash.Combine(x.IsUnboundGenericType, + Hash.Combine(GetNullableAnnotationsHashCode(x), GetHashCode(x.ContainingSymbol, currentHash)))))))); if (x.IsAnonymousType) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs index 1663493d1baa1..bb44f55ca85ff 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs @@ -39,12 +39,13 @@ internal partial class SymbolEquivalenceComparer : private readonly ImmutableArray _equivalenceVisitors; private readonly ImmutableArray _getHashCodeVisitors; - public static readonly SymbolEquivalenceComparer Instance = new(SimpleNameAssemblyComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false); - public static readonly SymbolEquivalenceComparer TupleNamesMustMatchInstance = new(SimpleNameAssemblyComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: true); - public static readonly SymbolEquivalenceComparer IgnoreAssembliesInstance = new(assemblyComparerOpt: null, distinguishRefFromOut: false, tupleNamesMustMatch: false); + public static readonly SymbolEquivalenceComparer Instance = new(SimpleNameAssemblyComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: true); + public static readonly SymbolEquivalenceComparer TupleNamesMustMatchInstance = new(SimpleNameAssemblyComparer.Instance, distinguishRefFromOut: false, tupleNamesMustMatch: true, ignoreNullableAnnotations: true); + public static readonly SymbolEquivalenceComparer IgnoreAssembliesInstance = new(assemblyComparerOpt: null, distinguishRefFromOut: false, tupleNamesMustMatch: false, ignoreNullableAnnotations: true); private readonly IEqualityComparer? _assemblyComparerOpt; private readonly bool _tupleNamesMustMatch; + private readonly bool _ignoreNullableAnnotations; public ParameterSymbolEqualityComparer ParameterEquivalenceComparer { get; } public SignatureTypeSymbolEquivalenceComparer SignatureTypeEquivalenceComparer { get; } @@ -52,10 +53,12 @@ internal partial class SymbolEquivalenceComparer : internal SymbolEquivalenceComparer( IEqualityComparer? assemblyComparerOpt, bool distinguishRefFromOut, - bool tupleNamesMustMatch) + bool tupleNamesMustMatch, + bool ignoreNullableAnnotations) { _assemblyComparerOpt = assemblyComparerOpt; _tupleNamesMustMatch = tupleNamesMustMatch; + _ignoreNullableAnnotations = ignoreNullableAnnotations; this.ParameterEquivalenceComparer = new ParameterSymbolEqualityComparer(this, distinguishRefFromOut); this.SignatureTypeEquivalenceComparer = new SignatureTypeSymbolEquivalenceComparer(this); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index 2616a24c40e74..48d07fe0b83b4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -489,6 +489,9 @@ public static bool IsTypeDeclarationContext( // class C { // int i; // | + + // namespace NS; + // | if (token.IsKind(SyntaxKind.SemicolonToken)) { if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective, SyntaxKind.UsingDirective)) @@ -537,6 +540,10 @@ public static bool IsTypeDeclarationContext( // [Bar] // | + // namespace NS; + // [Attr] + // | + if (token.IsKind(SyntaxKind.CloseBracketToken) && token.Parent.IsKind(SyntaxKind.AttributeList)) { @@ -551,7 +558,7 @@ public static bool IsTypeDeclarationContext( // the grandparent is the owner of the attribute // the great-grandparent is the container that the owner is in var container = token.Parent?.Parent?.Parent; - if (container is CompilationUnitSyntax or NamespaceDeclarationSyntax or TypeDeclarationSyntax) + if (container is CompilationUnitSyntax or BaseNamespaceDeclarationSyntax or TypeDeclarationSyntax) return true; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs index 9b0cadc0c9d4c..73c1a9ff9649d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeSymbolExtensions.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -177,5 +179,26 @@ public static bool IsIntrinsicType(this ITypeSymbol typeSymbol) return false; } } + + public static bool TryGetRecordPrimaryConstructor(this INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol? primaryConstructor) + { + if (typeSymbol.IsRecord) + { + Debug.Assert(typeSymbol.GetParameters().IsDefaultOrEmpty, "If GetParameters extension handles record, we can remove the handling here."); + + // A bit hacky to determine the parameters of primary constructor associated with a given record. + // Simplifying is tracked by: https://github.com/dotnet/roslyn/issues/53092. + // Note: When the issue is handled, we can remove the logic here and handle things in GetParameters extension. BUT + // if GetParameters extension method gets updated to handle records, we need to test EVERY usage + // of the extension method and make sure the change is applicable to all these usages. + + primaryConstructor = typeSymbol.InstanceConstructors.FirstOrDefault( + c => c.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is RecordDeclarationSyntax); + return primaryConstructor is not null; + } + + primaryConstructor = null; + return false; + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs index 976f450bbb254..82ef62a5f56c7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/CustomCodeActions.cs @@ -15,7 +15,7 @@ internal abstract class SimpleCodeAction : CodeAction { public SimpleCodeAction( string title, - string? equivalenceKey = null) + string? equivalenceKey) { Title = title; EquivalenceKey = equivalenceKey; @@ -32,7 +32,7 @@ internal class DocumentChangeAction : SimpleCodeAction public DocumentChangeAction( string title, Func> createChangedDocument, - string? equivalenceKey = null) + string? equivalenceKey) : base(title, equivalenceKey) { _createChangedDocument = createChangedDocument; @@ -49,7 +49,7 @@ internal class SolutionChangeAction : SimpleCodeAction public SolutionChangeAction( string title, Func> createChangedSolution, - string? equivalenceKey = null) + string? equivalenceKey) : base(title, equivalenceKey) { _createChangedSolution = createChangedSolution; diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index 9d1659d957012..4bf5bf06f207b 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -67,9 +67,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Friend Overrides Function DocumentationCommentTrivia(nodes As IEnumerable(Of SyntaxNode), trailingTrivia As SyntaxTriviaList, lastWhitespaceTrivia As SyntaxTrivia, endOfLineString As String) As SyntaxNode Dim node = SyntaxFactory.DocumentationCommentTrivia(SyntaxFactory.List(nodes)) - Return node.WithLeadingTrivia(SyntaxFactory.DocumentationCommentExteriorTrivia("''' ")). - WithTrailingTrivia(node.GetTrailingTrivia()). - WithTrailingTrivia(SyntaxFactory.EndOfLine(endOfLineString), lastWhitespaceTrivia) + node = node.WithLeadingTrivia(SyntaxFactory.DocumentationCommentExteriorTrivia("''' ")). + WithTrailingTrivia(node.GetTrailingTrivia()) + + If lastWhitespaceTrivia = Nothing Then + Return node.WithTrailingTrivia(SyntaxFactory.EndOfLine(endOfLineString)) + End If + + Return node.WithTrailingTrivia(SyntaxFactory.EndOfLine(endOfLineString), lastWhitespaceTrivia) End Function Friend Overrides Function DocumentationCommentTriviaWithUpdatedContent(trivia As SyntaxTrivia, content As IEnumerable(Of SyntaxNode)) As SyntaxNode