diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index de123cfde9b0f..9183540e07fe5 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -309,75 +309,19 @@ stages: pool: vmImage: windows-2019 steps: - - checkout: none - - - task: NuGetCommand@2 - displayName: 'Install RIT from Azure Artifacts' - inputs: - command: custom - arguments: 'install RoslynTools.VisualStudioInsertionTool -PreRelease -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' - - powershell: | - $response = Invoke-RestMethod -Headers @{Authorization = "Bearer $(System.AccessToken)"} "https://dev.azure.com/dnceng/internal/_apis/git/repositories/dotnet-roslyn/items?path=eng/config/PublishData.json&api-version=6.0" $branchName = "$(Build.SourceBranch)".Substring("refs/heads/".Length) - $branchData = $response.branches.$branchName - if (!$branchData) - { - Write-Host "No PublishData found for branch '$branchName'. Using PublishData for branch 'main'." - $branchData = $response.branches.main - } - if ($null -ne $branchData.insertionCreateDraftPR) - { - Write-Host "##vso[task.setvariable variable=Insertion.CreateDraftPR]$($branchData.insertionCreateDraftPR)" - } - if ($null -ne $branchData.insertionTitlePrefix) - { - Write-Host "##vso[task.setvariable variable=Insertion.TitlePrefix]$($branchData.insertionTitlePrefix)" - } - if ($null -ne $branchData.insertToolset) - { - Write-Host "##vso[task.setvariable variable=Insertion.InsertToolset]$($branchData.insertToolset)" - } - - Write-Host "##vso[task.setvariable variable=Insertion.AutoComplete]$(-not $branchData.insertionCreateDraftPR)" Write-Host "##vso[task.setvariable variable=ComponentBranchName]$branchName" - Write-Host "##vso[task.setvariable variable=VSBranchName]$($branchData.vsBranch)" - displayName: Set Insertion Variables + displayName: Get Branch Name - - powershell: | - mv RoslynTools.VisualStudioInsertionTool.* RIT - .\RIT\tools\OneOffInsertion.ps1 ` - -autoComplete "$(Insertion.AutoComplete)" ` - -buildQueueName "$(Build.DefinitionName)" ` - -cherryPick "(default)" ` - -clientId "$(ClientId)" ` - -clientSecret "$(ClientSecret)" ` - -componentAzdoUri "$(System.CollectionUri)" ` - -componentBranchName "$(ComponentBranchName)" ` - -componentGitHubRepoName "dotnet/roslyn" ` - -componentName "Roslyn" ` - -componentProjectName "internal" ` - -createDraftPR "$(Insertion.CreateDraftPR)" ` - -defaultValueSentinel "(default)" ` - -dropPath "(default)" ` - -insertCore "(default)" ` - -insertDevDiv "(default)" ` - -insertionCount "1" ` - -insertToolset "$(Insertion.InsertToolset)" ` - -titlePrefix "$(Insertion.TitlePrefix)" ` - -titleSuffix "$(Insertion.TitleSuffix)" ` - -queueValidation "true" ` - -requiredValueSentinel "REQUIRED" ` - -reviewerGUID "6c25b447-1d90-4840-8fde-d8b22cb8733e" ` - -specificBuild "$(Build.BuildNumber)" ` - -updateAssemblyVersions "(default)" ` - -updateCoreXTLibraries "(default)" ` - -visualStudioBranchName "$(VSBranchName)" ` - -writePullRequest "prid.txt" ` - displayName: 'Run OneOffInsertion.ps1' - - - script: 'echo. && echo. && type "prid.txt" && echo. && echo.' - displayName: 'Report PR URL' + - template: eng/pipelines/insert.yml + parameters: + clientId: $(ClientId) + clientSecret: $(ClientSecret) + componentBuildProjectName: internal + sourceBranch: "$(ComponentBranchName)" + publishDataURI: "https://dev.azure.com/dnceng/internal/_apis/git/repositories/dotnet-roslyn/items?path=eng/config/PublishData.json&api-version=6.0" + publishDataAccessToken: "$(System.AccessToken)" - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - template: eng\common\templates\post-build\post-build.yml diff --git a/azure-pipelines-pr-validation.yml b/azure-pipelines-pr-validation.yml index f0b6a42ce3194..494eceff5837c 100644 --- a/azure-pipelines-pr-validation.yml +++ b/azure-pipelines-pr-validation.yml @@ -13,6 +13,15 @@ parameters: type: number - name: CommitSHA type: string +- name: VisualStudioBranchName + type: string + default: default +- name: OptionalTitlePrefix + type: string + default: '[PR Validation]' +- name: InsertToolset + type: boolean + default: true # The variables `_DotNetArtifactsCategory` and `_DotNetValidationArtifactsCategory` are required for proper publishing of build artifacts. See https://github.com/dotnet/roslyn/pull/38259 variables: @@ -125,6 +134,7 @@ stages: /p:DotnetPublishUsingPipelines=false /p:IgnoreIbcMergeErrors=true /p:PreReleaseVersionLabel=pr-validation + /p:IgnoreIbcMergeErrors=true condition: succeeded() # Publish OptProf generated JSON files as a build artifact. This allows for easy inspection from @@ -195,3 +205,31 @@ stages: - task: ms-vseng.MicroBuildTasks.521a94ea-9e68-468a-8167-6dcf361ea776.MicroBuildCleanup@1 displayName: Perform Cleanup Tasks condition: succeededOrFailed() + +- stage: insert + displayName: Create Insertion + dependsOn: + - build + + jobs: + - job: insert + displayName: Insert to VS + pool: + vmImage: windows-2019 + steps: + - powershell: Write-Host "##vso[task.setvariable variable=SourceBranchName]$('$(Build.SourceBranch)'.Substring('refs/heads/'.Length))" + displayName: Setting SourceBranchName variable + condition: succeeded() + + - template: eng/pipelines/insert.yml + parameters: + createDraftPR: true + autoComplete: false + insertToolset: ${{ parameters.InsertToolset }} + clientId: $(ClientId) + clientSecret: $(ClientSecret) + vsBranchName: ${{ parameters.VisualStudioBranchName }} + titlePrefix: ${{ parameters.OptionalTitlePrefix }} + sourceBranch: $(SourceBranchName) + publishDataURI: "https://raw.githubusercontent.com/dotnet/roslyn/main/eng/config/PublishData.json" + diff --git a/eng/Tools.props b/eng/Tools.props index 6be67695ad0a4..1579b5105586b 100644 --- a/eng/Tools.props +++ b/eng/Tools.props @@ -8,6 +8,7 @@ See https://github.com/dotnet/arcade/issues/7205 --> + \ No newline at end of file diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 80ab050deb631..891b5125d73dc 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -6,25 +6,25 @@ 7e80445ee82adbf9a8e6ae601ac5e239d982afaa - + https://github.com/dotnet/source-build - 3855598428e676010315576808fcca0a29363cfc + 088128e88da570109769253bac46f044dcb43370 - + https://github.com/dotnet/arcade - cab4a3c50fd042677ea17cfc5171b4ce8b29930f + dd3652e2ae5ea89703a2286295de9efe908974f1 https://github.com/dotnet/roslyn 5f124755232afa7b9903d6bdfcaeb47f39c8838e - + https://github.com/dotnet/arcade - cab4a3c50fd042677ea17cfc5171b4ce8b29930f + dd3652e2ae5ea89703a2286295de9efe908974f1 diff --git a/eng/Versions.props b/eng/Versions.props index 1308f9b2470c9..7f95f4a085702 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -90,7 +90,6 @@ 1.1.0-beta2-20115-01 16.9.0-beta1.21055.5 1.5.0 - 6.0.0-beta.21319.2 5.0.0 5.0.0 3.13.8 diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj index f46d5efe2e32a..2067b8df8d985 100644 --- a/eng/common/internal/Tools.csproj +++ b/eng/common/internal/Tools.csproj @@ -1,5 +1,5 @@ + - net472 diff --git a/eng/pipelines/insert.yml b/eng/pipelines/insert.yml new file mode 100644 index 0000000000000..6d72c335a25a6 --- /dev/null +++ b/eng/pipelines/insert.yml @@ -0,0 +1,178 @@ +parameters: + # These are actually booleans but must be defined as string. + # Parameters are evaluated at compile time, but all variables are strings at compile time. + # So in order to pass a parameter that comes from a variable these must be typed as string. + - name: createDraftPR + type: string + default: '' + - name: autoComplete + type: string + default: '' + - name: insertToolset + type: string + default: '' + + - name: clientId + type: string + - name: clientSecret + type: string + - name: publishDataURI + type: string + - name: publishDataAccessToken + type: string + default: '' + + - name: vsBranchName + type: string + default: '' + - name: componentBuildProjectName + type: string + default: '' + - name: titlePrefix + type: string + default: '' + + - name: sourceBranch + type: string + +steps: + - checkout: none + + - task: NuGetCommand@2 + displayName: 'Install RIT from Azure Artifacts' + inputs: + command: custom + arguments: 'install RoslynTools.VisualStudioInsertionTool -PreRelease -Source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json' + + - powershell: | + $authorization = if ("" -ne $Env:PublishDataAccessToken) { "Bearer $Env:PublishDataAccessToken" } else { "" } + $response = Invoke-RestMethod -Headers @{Authorization = $authorization} "${{ parameters.publishDataURI }}" + $branchName = "${{ parameters.sourceBranch }}" + $branchData = $response.branches.$branchName + if (!$branchData) + { + Write-Host "No PublishData found for branch '$branchName'. Using PublishData for branch 'main'." + $branchData = $response.branches.main + } + + # Set our template variables to reasonable defaults + Write-Host "##vso[task.setvariable variable=Template.CreateDraftPR]$($true)" + Write-Host "##vso[task.setvariable variable=Template.AutoComplete]$($false)" + Write-Host "##vso[task.setvariable variable=Template.TitlePrefix]$('')" + Write-Host "##vso[task.setvariable variable=Template.TitleSuffix]$('[Skip-SymbolCheck]')" + Write-Host "##vso[task.setvariable variable=Template.InsertToolset]$($true)" + Write-Host "##vso[task.setvariable variable=Template.ComponentAzdoUri]$('')" + Write-Host "##vso[task.setvariable variable=Template.ComponentProjectName]$('')" + + Write-Host "##vso[task.setvariable variable=Template.ComponentBranchName]$branchName" + Write-Host "##vso[task.setvariable variable=Template.VSBranchName]$($branchData.vsBranch)" + + # Overwrite the default template variables with the values from PublishData for this sourceBranch + if ($null -ne $branchData.insertionCreateDraftPR) + { + Write-Host "##vso[task.setvariable variable=Template.CreateDraftPR]$($branchData.insertionCreateDraftPR)" + } + + if ($null -ne $branchData.insertionCreateDraftPR) + { + Write-Host "##vso[task.setvariable variable=Template.AutoComplete]$(-not $branchData.insertionCreateDraftPR)" + } + + if ($null -ne $branchData.insertionTitlePrefix) + { + Write-Host "##vso[task.setvariable variable=Template.TitlePrefix]$($branchData.insertionTitlePrefix)" + } + + if ($null -ne $branchData.insertToolset) + { + Write-Host "##vso[task.setvariable variable=Template.InsertToolset]$($branchData.insertToolset)" + } + + displayName: Set Variables from PublishData + env: + PublishDataAccessToken: ${{ parameters.publishDataAccessToken }} + + - powershell: | + # Overwrite template variables with values passed into this template as parameters + if ("" -ne $Env:CreateDraftPR) + { + Write-Host "Setting CreateDraftPR to $Env:CreateDraftPR" + Write-Host "##vso[task.setvariable variable=Template.CreateDraftPR]$Env:CreateDraftPR" + } + + if ("" -ne $Env:AutoComplete) + { + Write-Host "Setting AutoComplete to $Env:AutoComplete" + Write-Host "##vso[task.setvariable variable=Template.AutoComplete]$Env:AutoComplete" + } + + if ("" -ne $Env:TitlePrefix) + { + Write-Host "Setting TitlePrefix to $Env:TitlePrefix" + Write-Host "##vso[task.setvariable variable=Template.TitlePrefix]$Env:TitlePrefix" + } + + if ("" -ne $Env:InsertToolset) + { + Write-Host "Setting InsertToolset to $Env:InsertToolset" + Write-Host "##vso[task.setvariable variable=Template.InsertToolset]$Env:InsertToolset" + } + + # Workaround for pipeline parameters not supporting optional empty parameters. + if ("" -ne $Env:VSBranchName -and "default" -ne $Env:VSBranchName) + { + Write-Host "Setting VSBranchName to $Env:VSBranchName" + Write-Host "##vso[task.setvariable variable=Template.VSBranchName]$Env:VSBranchName" + } + + if ("" -ne $Env:ComponentBuildProjectName) + { + Write-Host "Setting component Azdo parameters $('$(System.CollectionUri)') and $Env:ComponentBuildProjectName" + Write-Host "##vso[task.setvariable variable=Template.ComponentAzdoUri]$('$(System.CollectionUri)')" + Write-Host "##vso[task.setvariable variable=Template.ComponentProjectName]$Env:ComponentBuildProjectName" + } + + displayName: Set Variables from Input Parameters + env: + CreateDraftPR: ${{ parameters.createDraftPR }} + AutoComplete: ${{ parameters.autoComplete }} + TitlePrefix: ${{ parameters.titlePrefix }} + InsertToolset: ${{ parameters.insertToolset }} + VSBranchName: ${{ parameters.vsBranchName }} + ComponentBuildProjectName: ${{ parameters.componentBuildProjectName }} + + # Now that everything is set, actually perform the insertion. + - powershell: | + mv RoslynTools.VisualStudioInsertionTool.* RIT + .\RIT\tools\OneOffInsertion.ps1 ` + -autoComplete "$(Template.AutoComplete)" ` + -buildQueueName "$(Build.DefinitionName)" ` + -cherryPick "(default)" ` + -clientId "${{ parameters.clientId }}" ` + -clientSecret "${{ parameters.clientSecret }}" ` + -componentAzdoUri "$(Template.ComponentAzdoUri)" ` + -componentProjectName "$(Template.ComponentProjectName)" ` + -componentName "Roslyn" ` + -componentGitHubRepoName "dotnet/roslyn" ` + -componentBranchName "$(Template.ComponentBranchName)" ` + -createDraftPR "$(Template.CreateDraftPR)" ` + -defaultValueSentinel "(default)" ` + -dropPath "(default)" ` + -insertCore "(default)" ` + -insertDevDiv "(default)" ` + -insertionCount "1" ` + -insertToolset "$(Template.InsertToolset)" ` + -titlePrefix "$(Template.TitlePrefix)" ` + -titleSuffix "$(Template.TitleSuffix)" ` + -queueValidation "true" ` + -requiredValueSentinel "REQUIRED" ` + -reviewerGUID "6c25b447-1d90-4840-8fde-d8b22cb8733e" ` + -specificBuild "$(Build.BuildNumber)" ` + -updateAssemblyVersions "(default)" ` + -updateCoreXTLibraries "(default)" ` + -visualStudioBranchName "$(Template.VSBranchName)" ` + -writePullRequest "prid.txt" ` + displayName: 'Run OneOffInsertion.ps1' + + - script: 'echo. && echo. && type "prid.txt" && echo. && echo.' + displayName: 'Report PR URL' diff --git a/global.json b/global.json index 5ea98158dee04..c2d9c7a14809e 100644 --- a/global.json +++ b/global.json @@ -10,7 +10,7 @@ "xcopy-msbuild": "16.10.0-preview2" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21377.2", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21377.2" + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21378.2", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.21378.2" } } diff --git a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs new file mode 100644 index 0000000000000..62361d757fa24 --- /dev/null +++ b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs @@ -0,0 +1,90 @@ +// 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.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers +{ + internal class CSharpAddAccessibilityModifiers : AbstractAddAccessibilityModifiers + { + public static readonly CSharpAddAccessibilityModifiers Instance = new(); + + protected CSharpAddAccessibilityModifiers() + { + } + + public override bool ShouldUpdateAccessibilityModifier( + ISyntaxFacts syntaxFacts, + MemberDeclarationSyntax member, + AccessibilityModifiersRequired option, + out SyntaxToken name) + { + // Have to have a name to report the issue on. + name = member.GetNameToken(); + if (name.Kind() == SyntaxKind.None) + return false; + + // Certain members never have accessibility. Don't bother reporting on them. + if (!syntaxFacts.CanHaveAccessibility(member)) + return false; + + // This analyzer bases all of its decisions on the accessibility + var accessibility = syntaxFacts.GetAccessibility(member); + + // Omit will flag any accessibility values that exist and are default + // The other options will remove or ignore accessibility + var isOmit = option == AccessibilityModifiersRequired.OmitIfDefault; + + if (isOmit) + { + if (accessibility == Accessibility.NotApplicable) + return false; + + var parentKind = member.GetRequiredParent().Kind(); + switch (parentKind) + { + // Check for default modifiers in namespace and outside of namespace + case SyntaxKind.CompilationUnit: + case SyntaxKind.FileScopedNamespaceDeclaration: + case SyntaxKind.NamespaceDeclaration: + { + // Default is internal + if (accessibility != Accessibility.Internal) + return false; + } + + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.RecordDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.RecordStructDeclaration: + { + // Inside a type, default is private + if (accessibility != Accessibility.Private) + return false; + } + + break; + + default: + return false; // Unknown parent kind, don't do anything + } + } + else + { + // Mode is always, so we have to flag missing modifiers + if (accessibility != Accessibility.NotApplicable) + return false; + } + + return true; + } + } +} diff --git a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs index 54c27c7ce7d1a..9bcbb1ae5ac4b 100644 --- a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersDiagnosticAnalyzer.cs @@ -62,76 +62,8 @@ private void ProcessMemberDeclaration( } #endif - // Have to have a name to report the issue on. - var name = member.GetNameToken(); - if (name.Kind() == SyntaxKind.None) - { - return; - } - - // Certain members never have accessibility. Don't bother reporting on them. - if (!SyntaxFacts.CanHaveAccessibility(member)) - { + if (!CSharpAddAccessibilityModifiers.Instance.ShouldUpdateAccessibilityModifier(SyntaxFacts, member, option.Value, out var name)) return; - } - - // This analyzer bases all of its decisions on the accessibility - var accessibility = SyntaxFacts.GetAccessibility(member); - - // Omit will flag any accessibility values that exist and are default - // The other options will remove or ignore accessibility - var isOmit = option.Value == AccessibilityModifiersRequired.OmitIfDefault; - - if (isOmit) - { - if (accessibility == Accessibility.NotApplicable) - { - return; - } - - var parentKind = member.GetRequiredParent().Kind(); - switch (parentKind) - { - // Check for default modifiers in namespace and outside of namespace - case SyntaxKind.CompilationUnit: - case SyntaxKind.FileScopedNamespaceDeclaration: - case SyntaxKind.NamespaceDeclaration: - { - // Default is internal - if (accessibility != Accessibility.Internal) - { - return; - } - } - - break; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.RecordDeclaration: - case SyntaxKind.StructDeclaration: - case SyntaxKind.RecordStructDeclaration: - { - // Inside a type, default is private - if (accessibility != Accessibility.Private) - { - return; - } - } - - break; - - default: - return; // Unknown parent kind, don't do anything - } - } - else - { - // Mode is always, so we have to flag missing modifiers - if (accessibility != Accessibility.NotApplicable) - { - return; - } - } // Have an issue to flag, either add or remove. Report issue to user. var additionalLocations = ImmutableArray.Create(member.GetLocation()); diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems index e9940207cf13d..ac1a5e472fb1f 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzers.projitems @@ -15,6 +15,7 @@ + @@ -119,4 +120,4 @@ - + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs index 2ac35553102e6..e9e1372f02e80 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpUseNotPatternDiagnosticAnalyzer.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UsePatternMatching @@ -64,27 +65,34 @@ private void SyntaxNodeAction(SyntaxNodeAnalysisContext syntaxContext) if (!styleOption.Value) return; - // Look for the form: !(x is Y y) - if (!(node is PrefixUnaryExpressionSyntax + // Look for the form: !(...) + if (node is not PrefixUnaryExpressionSyntax(SyntaxKind.LogicalNotExpression) { - Operand: ParenthesizedExpressionSyntax - { - Expression: IsPatternExpressionSyntax - { - Pattern: DeclarationPatternSyntax, - } isPattern, - }, - } notExpression)) + Operand: ParenthesizedExpressionSyntax parenthesizedExpression + }) { return; } - // Put a diagnostic with the appropriate severity on `is` keyword. + var isKeywordLocation = parenthesizedExpression.Expression switch + { + // Look for the form: !(x is Y y) and !(x is const) + IsPatternExpressionSyntax { Pattern: DeclarationPatternSyntax or ConstantPatternSyntax } isPattern => isPattern.IsKeyword.GetLocation(), + + // Look for the form: !(x is Y) + BinaryExpressionSyntax(SyntaxKind.IsExpression) { Right: TypeSyntax } isExpression => isExpression.OperatorToken.GetLocation(), + + _ => null + }; + + if (isKeywordLocation is null) + return; + syntaxContext.ReportDiagnostic(DiagnosticHelper.Create( Descriptor, - isPattern.IsKeyword.GetLocation(), + isKeywordLocation, styleOption.Notification.Severity, - ImmutableArray.Create(notExpression.GetLocation()), + ImmutableArray.Create(node.GetLocation()), properties: null)); } } diff --git a/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs b/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs new file mode 100644 index 0000000000000..65fd2072bd996 --- /dev/null +++ b/src/Analyzers/CSharp/CodeFixes/AddAccessibilityModifiers/CSharpAddAccessibilityModifiersService.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.AddAccessibilityModifiers; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.CSharp.AddAccessibilityModifiers +{ + [ExportLanguageService(typeof(IAddAccessibilityModifiersService), LanguageNames.CSharp), Shared] + internal class CSharpAddAccessibilityModifiersService : CSharpAddAccessibilityModifiers, IAddAccessibilityModifiersService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpAddAccessibilityModifiersService() + { + } + } +} diff --git a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems index d9a2923277073..5f80d36063302 100644 --- a/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems +++ b/src/Analyzers/CSharp/CodeFixes/CSharpCodeFixes.projitems @@ -14,6 +14,7 @@ + diff --git a/src/Analyzers/CSharp/CodeFixes/UsePatternMatching/CSharpUseNotPatternCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UsePatternMatching/CSharpUseNotPatternCodeFixProvider.cs index 2b32550c04602..1653aee97bd9a 100644 --- a/src/Analyzers/CSharp/CodeFixes/UsePatternMatching/CSharpUseNotPatternCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UsePatternMatching/CSharpUseNotPatternCodeFixProvider.cs @@ -65,14 +65,20 @@ private static void ProcessDiagnostic( var notExpression = (PrefixUnaryExpressionSyntax)notExpressionLocation.FindNode(getInnermostNodeForTie: true, cancellationToken); var parenthesizedExpression = (ParenthesizedExpressionSyntax)notExpression.Operand; - var isPattern = (IsPatternExpressionSyntax)parenthesizedExpression.Expression; + var oldExpression = parenthesizedExpression.Expression; + + var updatedPattern = oldExpression switch + { + IsPatternExpressionSyntax isPattern => isPattern.WithPattern(UnaryPattern(Token(SyntaxKind.NotKeyword), isPattern.Pattern)), + BinaryExpressionSyntax { Right: TypeSyntax type } binaryIsExpression + => IsPatternExpression(binaryIsExpression.Left, UnaryPattern(Token(SyntaxKind.NotKeyword), TypePattern(type))), + _ => throw ExceptionUtilities.UnexpectedValue(oldExpression) + }; - var updatedPattern = isPattern.WithPattern(UnaryPattern(Token(SyntaxKind.NotKeyword), isPattern.Pattern)); editor.ReplaceNode( notExpression, updatedPattern.WithPrependedLeadingTrivia(notExpression.GetLeadingTrivia()) .WithAppendedTrailingTrivia(notExpression.GetTrailingTrivia())); - } private class MyCodeAction : CustomCodeActions.DocumentChangeAction diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs index 4652a7023ca74..801a9c52fa294 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpUseNotPatternTests.cs @@ -18,6 +18,66 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UsePatternMatching public partial class CSharpUseNotPatternTests { + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNotPattern)] + [WorkItem(50690, "https://github.com/dotnet/roslyn/issues/50690")] + public async Task BinaryIsExpression() + { + await new VerifyCS.Test + { + TestCode = +@"class C +{ + void M(object x) + { + if (!(x [|is|] string)) + { + } + } +}", + FixedCode = +@"class C +{ + void M(object x) + { + if (x is not string) + { + } + } +}", + LanguageVersion = LanguageVersion.CSharp9, + }.RunAsync(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNotPattern)] + [WorkItem(50690, "https://github.com/dotnet/roslyn/issues/50690")] + public async Task ConstantPattern() + { + await new VerifyCS.Test + { + TestCode = +@"class C +{ + void M(object x) + { + if (!(x [|is|] null)) + { + } + } +}", + FixedCode = +@"class C +{ + void M(object x) + { + if (x is not null) + { + } + } +}", + LanguageVersion = LanguageVersion.CSharp9, + }.RunAsync(); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNotPattern)] [WorkItem(46699, "https://github.com/dotnet/roslyn/issues/46699")] public async Task UseNotPattern() diff --git a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs new file mode 100644 index 0000000000000..cc359b8d33a48 --- /dev/null +++ b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiers.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.LanguageServices; + +namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers +{ + internal abstract class AbstractAddAccessibilityModifiers : IAddAccessibilityModifiers + where TMemberDeclarationSyntax : SyntaxNode + { + public bool ShouldUpdateAccessibilityModifier( + ISyntaxFacts syntaxFacts, + SyntaxNode member, + AccessibilityModifiersRequired option, + out SyntaxToken name) + { + name = default; + return member is TMemberDeclarationSyntax memberDecl && + ShouldUpdateAccessibilityModifier(syntaxFacts, memberDecl, option, out name); + } + + public abstract bool ShouldUpdateAccessibilityModifier( + ISyntaxFacts syntaxFacts, + TMemberDeclarationSyntax member, + AccessibilityModifiersRequired option, + out SyntaxToken name); + } +} diff --git a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs index f7b2d1cbfd35f..462188d6dcd5e 100644 --- a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersDiagnosticAnalyzer.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; @@ -36,9 +34,7 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) var language = syntaxTree.Options.Language; var option = context.GetOption(CodeStyleOptions2.RequireAccessibilityModifiers, language); if (option.Value == AccessibilityModifiersRequired.Never) - { return; - } ProcessCompilationUnit(context, option, (TCompilationUnitSyntax)syntaxTree.GetRoot(cancellationToken)); } diff --git a/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs new file mode 100644 index 0000000000000..da5e86dfb43dc --- /dev/null +++ b/src/Analyzers/Core/Analyzers/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.LanguageServices; + +namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers +{ + internal interface IAddAccessibilityModifiers + { + bool ShouldUpdateAccessibilityModifier( + ISyntaxFacts syntaxFacts, + SyntaxNode member, + AccessibilityModifiersRequired option, + out SyntaxToken name); + } +} diff --git a/src/Analyzers/Core/Analyzers/Analyzers.projitems b/src/Analyzers/Core/Analyzers/Analyzers.projitems index 010bb8993e949..987454bd75641 100644 --- a/src/Analyzers/Core/Analyzers/Analyzers.projitems +++ b/src/Analyzers/Core/Analyzers/Analyzers.projitems @@ -17,7 +17,9 @@ + + diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs index f3b9f136e8c32..653b69703b24c 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs @@ -53,55 +53,12 @@ protected sealed override async Task FixAllAsync( { var declaration = diagnostic.AdditionalLocations[0].FindNode(cancellationToken); var declarator = MapToDeclarator(declaration); - var symbol = semanticModel.GetDeclaredSymbol(declarator, cancellationToken); Contract.ThrowIfNull(symbol); - - var preferredAccessibility = GetPreferredAccessibility(symbol); - - // Check to see if we need to add or remove - // If there's a modifier, then we need to remove it, otherwise no modifier, add it. - editor.ReplaceNode( - declaration, - (currentDeclaration, _) => UpdateAccessibility(currentDeclaration, preferredAccessibility)); - } - - return; - - SyntaxNode UpdateAccessibility(SyntaxNode declaration, Accessibility preferredAccessibility) - { - var generator = editor.Generator; - - // If there was accessibility on the member, then remove it. If there was no accessibility, then add - // the preferred accessibility for this member. - return generator.GetAccessibility(declaration) == Accessibility.NotApplicable - ? generator.WithAccessibility(declaration, preferredAccessibility) - : generator.WithAccessibility(declaration, Accessibility.NotApplicable); + AddAccessibilityModifiersHelpers.UpdateDeclaration(editor, symbol, declaration); } } - private static Accessibility GetPreferredAccessibility(ISymbol symbol) - { - // If we have an overridden member, then if we're adding an accessibility modifier, use the - // accessibility of the member we're overriding as both should be consistent here. - if (symbol.GetOverriddenMember() is { DeclaredAccessibility: var accessibility }) - return accessibility; - - // Default abstract members to be protected, and virtual members to be public. They can't be private as - // that's not legal. And these are reasonable default values for them. - if (symbol is IMethodSymbol or IPropertySymbol or IEventSymbol) - { - if (symbol.IsAbstract) - return Accessibility.Protected; - - if (symbol.IsVirtual) - return Accessibility.Public; - } - - // Otherwise, default to whatever accessibility no-accessibility means for this member; - return symbol.DeclaredAccessibility; - } - private class MyCodeAction : CustomCodeActions.DocumentChangeAction { #if CODE_STYLE // 'CodeActionPriority' is not a public API, hence not supported in CodeStyle layer. diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs new file mode 100644 index 0000000000000..30e3f839b4d06 --- /dev/null +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers +{ + internal static class AddAccessibilityModifiersHelpers + { + public static void UpdateDeclaration( + SyntaxEditor editor, ISymbol symbol, SyntaxNode declaration) + { + Contract.ThrowIfNull(symbol); + + var preferredAccessibility = GetPreferredAccessibility(symbol); + + // Check to see if we need to add or remove + // If there's a modifier, then we need to remove it, otherwise no modifier, add it. + editor.ReplaceNode( + declaration, + (currentDeclaration, _) => UpdateAccessibility(currentDeclaration, preferredAccessibility)); + + return; + + SyntaxNode UpdateAccessibility(SyntaxNode declaration, Accessibility preferredAccessibility) + { + var generator = editor.Generator; + + // If there was accessibility on the member, then remove it. If there was no accessibility, then add + // the preferred accessibility for this member. + return generator.GetAccessibility(declaration) == Accessibility.NotApplicable + ? generator.WithAccessibility(declaration, preferredAccessibility) + : generator.WithAccessibility(declaration, Accessibility.NotApplicable); + } + } + + private static Accessibility GetPreferredAccessibility(ISymbol symbol) + { + // If we have an overridden member, then if we're adding an accessibility modifier, use the + // accessibility of the member we're overriding as both should be consistent here. + if (symbol.GetOverriddenMember() is { DeclaredAccessibility: var accessibility }) + return accessibility; + + // Default abstract members to be protected, and virtual members to be public. They can't be private as + // that's not legal. And these are reasonable default values for them. + if (symbol is IMethodSymbol or IPropertySymbol or IEventSymbol) + { + if (symbol.IsAbstract) + return Accessibility.Protected; + + if (symbol.IsVirtual) + return Accessibility.Public; + } + + // Otherwise, default to whatever accessibility no-accessibility means for this member; + return symbol.DeclaredAccessibility; + } + } +} diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs new file mode 100644 index 0000000000000..93c6f7ff23281 --- /dev/null +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/IAddAccessibilityModifiersService.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.AddAccessibilityModifiers +{ + internal interface IAddAccessibilityModifiersService : IAddAccessibilityModifiers, ILanguageService + { + } +} diff --git a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems index 888d7dd7d7bee..293e9c1f4196a 100644 --- a/src/Analyzers/Core/CodeFixes/CodeFixes.projitems +++ b/src/Analyzers/Core/CodeFixes/CodeFixes.projitems @@ -14,6 +14,8 @@ + + diff --git a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb new file mode 100644 index 0000000000000..2a7986d02aee6 --- /dev/null +++ b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiers.vb @@ -0,0 +1,83 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.AddAccessibilityModifiers +Imports Microsoft.CodeAnalysis.CodeStyle +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers + Friend Class VisualBasicAddAccessibilityModifiers + Inherits AbstractAddAccessibilityModifiers(Of StatementSyntax) + + Public Shared ReadOnly Instance As New VisualBasicAddAccessibilityModifiers() + + Protected Sub New() + End Sub + + Public Overrides Function ShouldUpdateAccessibilityModifier( + syntaxFacts As CodeAnalysis.LanguageServices.ISyntaxFacts, + member As StatementSyntax, + [option] As AccessibilityModifiersRequired, + ByRef name As SyntaxToken) As Boolean + + ' Have to have a name to report the issue on. + name = member.GetNameToken() + If name.Kind() = SyntaxKind.None Then + Return False + End If + + ' Certain members never have accessibility. Don't bother reporting on them. + If Not syntaxFacts.CanHaveAccessibility(member) Then + Return False + End If + + ' This analyzer bases all of its decisions on the accessibility + Dim Accessibility = syntaxFacts.GetAccessibility(member) + + ' Omit will flag any accesibility values that exist and are default + ' The other options will remove or ignore accessibility + Dim isOmit = [option] = AccessibilityModifiersRequired.OmitIfDefault + + If isOmit Then + If Accessibility = Accessibility.NotApplicable Then + ' Accessibility modifier already missing. nothing we need to do. + Return False + End If + + If Not MatchesDefaultAccessibility(Accessibility, member) Then + ' Explicit accessibility was different than the default accessibility. + ' We have to keep this here. + Return False + End If + + Else ' Require all, flag missing modidifers + If Accessibility <> Accessibility.NotApplicable Then + Return False + End If + End If + + Return True + End Function + + Private Shared Function MatchesDefaultAccessibility(accessibility As Accessibility, member As StatementSyntax) As Boolean + ' Top level items in a namespace or file + If member.IsParentKind(SyntaxKind.CompilationUnit) OrElse + member.IsParentKind(SyntaxKind.NamespaceBlock) Then + ' default is Friend + Return accessibility = Accessibility.Friend + End If + + ' default for const and field in a class is private + If member.IsParentKind(SyntaxKind.ClassBlock) OrElse + member.IsParentKind(SyntaxKind.ModuleBlock) Then + If member.IsKind(SyntaxKind.FieldDeclaration) Then + Return accessibility = Accessibility.Private + End If + End If + + ' Everything else has a default of public + Return accessibility = Accessibility.Public + End Function + End Class +End Namespace diff --git a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb index 16bad2c13f1e7..7ecda4c89eef7 100644 --- a/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersDiagnosticAnalyzer.vb @@ -47,42 +47,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers ProcessMembers(context, [option], typeBlock.Members) End If - ' Have to have a name to report the issue on. - Dim name = member.GetNameToken() - If name.Kind() = SyntaxKind.None Then + Dim name As SyntaxToken = Nothing + If Not VisualBasicAddAccessibilityModifiers.Instance.ShouldUpdateAccessibilityModifier(SyntaxFacts, member, [option].Value, name) Then Return End If - ' Certain members never have accessibility. Don't bother reporting on them. - If Not SyntaxFacts.CanHaveAccessibility(member) Then - Return - End If - - ' This analyzer bases all of its decisions on the accessibility - Dim Accessibility = SyntaxFacts.GetAccessibility(member) - - ' Omit will flag any accesibility values that exist and are default - ' The other options will remove or ignore accessibility - Dim isOmit = [option].Value = AccessibilityModifiersRequired.OmitIfDefault - - If isOmit Then - If Accessibility = Accessibility.NotApplicable Then - ' Accessibility modifier already missing. nothing we need to do. - Return - End If - - If Not MatchesDefaultAccessibility(Accessibility, member) Then - ' Explicit accessibility was different than the default accessibility. - ' We have to keep this here. - Return - End If - - Else ' Require all, flag missing modidifers - If Accessibility <> Accessibility.NotApplicable Then - Return - End If - End If - ' Have an issue to flag, either add or remove. Report issue to user. Dim additionalLocations = ImmutableArray.Create(member.GetLocation()) context.ReportDiagnostic(DiagnosticHelper.Create( @@ -92,25 +61,5 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers additionalLocations:=additionalLocations, properties:=Nothing)) End Sub - - Private Shared Function MatchesDefaultAccessibility(accessibility As Accessibility, member As StatementSyntax) As Boolean - ' Top level items in a namespace or file - If member.IsParentKind(SyntaxKind.CompilationUnit) OrElse - member.IsParentKind(SyntaxKind.NamespaceBlock) Then - ' default is Friend - Return accessibility = Accessibility.Friend - End If - - ' default for const and field in a class is private - If member.IsParentKind(SyntaxKind.ClassBlock) OrElse - member.IsParentKind(SyntaxKind.ModuleBlock) Then - If member.IsKind(SyntaxKind.FieldDeclaration) Then - Return accessibility = Accessibility.Private - End If - End If - - ' Everything else has a default of public - Return accessibility = Accessibility.Public - End Function End Class End Namespace diff --git a/src/Analyzers/VisualBasic/Analyzers/VisualBasicAnalyzers.projitems b/src/Analyzers/VisualBasic/Analyzers/VisualBasicAnalyzers.projitems index acbdb7df3bd8a..3ced98ebbf8e0 100644 --- a/src/Analyzers/VisualBasic/Analyzers/VisualBasicAnalyzers.projitems +++ b/src/Analyzers/VisualBasic/Analyzers/VisualBasicAnalyzers.projitems @@ -13,6 +13,7 @@ + diff --git a/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb b/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb new file mode 100644 index 0000000000000..3993b1e957f2f --- /dev/null +++ b/src/Analyzers/VisualBasic/CodeFixes/AddAccessibilityModifiers/VisualBasicAddAccessibilityModifiersService.vb @@ -0,0 +1,20 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.AddAccessibilityModifiers +Imports Microsoft.CodeAnalysis.Host.Mef + +Namespace Microsoft.CodeAnalysis.VisualBasic.AddAccessibilityModifiers + + Friend Class VisualBasicAddAccessibilityModifiersService + Inherits VisualBasicAddAccessibilityModifiers + Implements IAddAccessibilityModifiersService + + + + Public Sub New() + End Sub + End Class +End Namespace diff --git a/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems b/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems index 23087c51f179c..ba29babacf791 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems +++ b/src/Analyzers/VisualBasic/CodeFixes/VisualBasicCodeFixes.projitems @@ -14,6 +14,7 @@ + diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index dbba1849b977e..4e2a67dcf4eaa 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -1262,13 +1262,9 @@ private bool IsBadBaseAccess(SyntaxNode node, BoundExpression receiverOpt, Symbo } internal static uint GetInterpolatedStringHandlerConversionEscapeScope( - BoundInterpolatedString interpolatedString, + InterpolatedStringHandlerData data, uint scopeOfTheContainingExpression) - { - Debug.Assert(interpolatedString.InterpolationData != null); - var data = interpolatedString.InterpolationData.GetValueOrDefault(); - return GetValEscape(data.Construction, scopeOfTheContainingExpression); - } + => GetValEscape(data.Construction, scopeOfTheContainingExpression); /// /// Computes the scope to which the given invocation can escape @@ -2672,7 +2668,13 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin if (conversion.ConversionKind == ConversionKind.InterpolatedStringHandler) { - return GetInterpolatedStringHandlerConversionEscapeScope((BoundInterpolatedString)conversion.Operand, scopeOfTheContainingExpression); + var data = conversion.Operand switch + { + BoundInterpolatedString { InterpolationData: { } d } => d, + BoundBinaryOperator { InterpolatedStringHandlerData: { } d } => d, + _ => throw ExceptionUtilities.UnexpectedValue(conversion.Operand.Kind) + }; + return GetInterpolatedStringHandlerConversionEscapeScope(data, scopeOfTheContainingExpression); } return GetValEscape(conversion.Operand, scopeOfTheContainingExpression); @@ -3110,7 +3112,7 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint if (conversion.ConversionKind == ConversionKind.InterpolatedStringHandler) { - return CheckInterpolatedStringHandlerConversionEscape((BoundInterpolatedString)conversion.Operand, escapeFrom, escapeTo, diagnostics); + return CheckInterpolatedStringHandlerConversionEscape(conversion.Operand, escapeFrom, escapeTo, diagnostics); } return CheckValEscape(node, conversion.Operand, escapeFrom, escapeTo, checkingReceiver: false, diagnostics: diagnostics); @@ -3383,38 +3385,73 @@ private static bool CheckValEscape(ImmutableArray expressions, return true; } - private static bool CheckInterpolatedStringHandlerConversionEscape(BoundInterpolatedString interpolatedString, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) + private static bool CheckInterpolatedStringHandlerConversionEscape(BoundExpression expression, uint escapeFrom, uint escapeTo, BindingDiagnosticBag diagnostics) { - Debug.Assert(interpolatedString.InterpolationData is not null); + + var data = expression switch + { + BoundInterpolatedString { InterpolationData: { } d } => d, + BoundBinaryOperator { InterpolatedStringHandlerData: { } d } => d, + _ => throw ExceptionUtilities.UnexpectedValue(expression.Kind) + }; // 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); + CheckValEscape(expression.Syntax, data.Construction, escapeFrom, escapeTo, checkingReceiver: false, diagnostics); - foreach (var part in interpolatedString.Parts) + while (true) { - if (part is not BoundCall { Method: { Name: "AppendFormatted" } } call) + switch (expression) { - // 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; - } + case BoundBinaryOperator binary: + if (!checkParts((BoundInterpolatedString)binary.Right)) + { + return false; + } - // 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); + expression = binary.Left; + continue; - if (!success) - { - return false; + case BoundInterpolatedString interpolatedString: + if (!checkParts(interpolatedString)) + { + return false; + } + + return true; + + default: + throw ExceptionUtilities.UnexpectedValue(expression.Kind); } } - return true; + bool checkParts(BoundInterpolatedString interpolatedString) + { + 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 diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 78f651d81c914..33246b9d3e6d0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -152,10 +152,9 @@ protected BoundExpression CreateConversion( if (conversion.Kind == ConversionKind.InterpolatedStringHandler) { - var unconvertedSource = (BoundUnconvertedInterpolatedString)source; return new BoundConversion( syntax, - BindUnconvertedInterpolatedStringToHandlerType(unconvertedSource, (NamedTypeSymbol)destination, diagnostics, isHandlerConversion: true), + BindUnconvertedInterpolatedExpressionToHandlerType(source, (NamedTypeSymbol)destination, diagnostics), conversion, @checked: CheckOverflowAtRuntime, explicitCastInCode: isCast && !wasCompilerGenerated, @@ -557,9 +556,9 @@ private BoundExpression CreateAnonymousFunctionConversion(SyntaxNode syntax, Bou // UNDONE: is converted to a delegate that does not match. What to surface then? var unboundLambda = (UnboundLambda)source; - if (destination.SpecialType == SpecialType.System_Delegate || destination.IsNonGenericExpressionType()) + if ((destination.SpecialType == SpecialType.System_Delegate || destination.IsNonGenericExpressionType()) && + syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType)) { - CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInferredDelegateType, diagnostics); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var delegateType = unboundLambda.InferDelegateType(ref useSiteInfo); BoundLambda boundLambda; @@ -628,9 +627,9 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr hasErrors = true; } - if (destination.SpecialType == SpecialType.System_Delegate) + if (destination.SpecialType == SpecialType.System_Delegate && + syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType)) { - CheckFeatureAvailability(syntax, MessageID.IDS_FeatureInferredDelegateType, diagnostics); // https://github.com/dotnet/roslyn/issues/52869: Avoid calculating the delegate type multiple times during conversion. CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var delegateType = GetMethodGroupDelegateType(group, ref useSiteInfo); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 555bee88747ea..5fcc0998cd88b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -380,6 +380,11 @@ internal BoundExpression BindToNaturalType(BoundExpression expression, BindingDi result = BindUnconvertedInterpolatedStringToString(unconvertedInterpolatedString, diagnostics); } break; + case BoundBinaryOperator unconvertedBinaryOperator: + { + result = RebindSimpleBinaryOperatorAsConverted(unconvertedBinaryOperator, diagnostics); + } + break; default: result = expression; break; @@ -2999,10 +3004,10 @@ private void CoerceArguments( if (kind.IsInterpolatedStringHandler) { - Debug.Assert(argument is BoundUnconvertedInterpolatedString); + Debug.Assert(argument is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); 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); + arguments[arg] = BindInterpolatedStringHandlerInMemberCall(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? else if (!kind.IsIdentity) @@ -8568,7 +8573,6 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) } // This method was adapted from LoweredDynamicOperationFactory.GetDelegateType(). - // Consider using that method directly since it also synthesizes delegates if necessary. internal NamedTypeSymbol? GetMethodGroupOrLambdaDelegateType( RefKind returnRefKind, TypeWithAnnotations returnTypeOpt, @@ -8576,35 +8580,72 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) ImmutableArray parameterTypes, ref CompoundUseSiteInfo useSiteInfo) { + Debug.Assert(parameterRefKinds.IsDefault || parameterRefKinds.Length == parameterTypes.Length); Debug.Assert(returnTypeOpt.Type?.IsVoidType() != true); // expecting !returnTypeOpt.HasType rather than System.Void - if (returnRefKind == RefKind.None && - (parameterRefKinds.IsDefault || parameterRefKinds.All(refKind => refKind == RefKind.None))) + bool returnsVoid = !returnTypeOpt.HasType; + var typeArguments = returnsVoid ? parameterTypes : parameterTypes.Add(returnTypeOpt); + + if (returnsVoid && returnRefKind != RefKind.None) + { + // Invalid return type. + return null; + } + + if (!typeArguments.All(t => isValidTypeArgument(t.Type))) { - var wkDelegateType = returnTypeOpt.HasType ? - WellKnownTypes.GetWellKnownFunctionDelegate(invokeArgumentCount: parameterTypes.Length) : - WellKnownTypes.GetWellKnownActionDelegate(invokeArgumentCount: parameterTypes.Length); + // https://github.com/dotnet/roslyn/issues/55217: Support parameter + // and return types that are not valid generic type arguments. + return null; + } + + bool hasByRefParameters = !parameterRefKinds.IsDefault && parameterRefKinds.Any(refKind => refKind != RefKind.None); + + if (returnRefKind == RefKind.None && !hasByRefParameters) + { + var wkDelegateType = returnsVoid ? + WellKnownTypes.GetWellKnownActionDelegate(invokeArgumentCount: parameterTypes.Length) : + WellKnownTypes.GetWellKnownFunctionDelegate(invokeArgumentCount: parameterTypes.Length); if (wkDelegateType != WellKnownType.Unknown) { var delegateType = Compilation.GetWellKnownType(wkDelegateType); delegateType.AddUseSiteInfo(ref useSiteInfo); - if (returnTypeOpt.HasType) - { - parameterTypes = parameterTypes.Add(returnTypeOpt); - } - if (parameterTypes.Length == 0) + if (typeArguments.Length == 0) { return delegateType; } - if (checkConstraints(Compilation, Conversions, delegateType, parameterTypes)) + if (checkConstraints(Compilation, Conversions, delegateType, typeArguments)) { - return delegateType.Construct(parameterTypes); + return delegateType.Construct(typeArguments); } } } - return null; + var refKinds = (hasByRefParameters || returnRefKind != RefKind.None) ? RefKindVector.Create(parameterTypes.Length + (returnsVoid ? 0 : 1)) : default; + Debug.Assert(Enumerable.Range(0, refKinds.Capacity).All(i => refKinds[i] == RefKind.None)); + + if (hasByRefParameters) + { + for (int i = 0; i < parameterRefKinds.Length; i++) + { + refKinds[i] = parameterRefKinds[i]; + } + } + if (returnRefKind != RefKind.None) + { + refKinds[parameterTypes.Length] = returnRefKind; + } + + var synthesizedType = Compilation.AnonymousTypeManager.SynthesizeDelegate(parameterCount: parameterTypes.Length, refKinds, returnsVoid, generation: 0); + return synthesizedType.Construct(typeArguments); + + static bool isValidTypeArgument(TypeSymbol? type) + { + return type is { } && + !type.IsPointerOrFunctionPointer() && + !type.IsRestrictedType(); + } static bool checkConstraints(CSharpCompilation compilation, ConversionsBase conversions, NamedTypeSymbol delegateType, ImmutableArray typeArguments) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs index 952884e61b23b..46a2cd45f3ced 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs @@ -159,9 +159,7 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToString(BoundU } // 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 })) + if (unconvertedInterpolatedString.Parts.Length <= 4 && AllInterpolatedStringPartsAreStrings(unconvertedInterpolatedString.Parts)) { return constructWithData(BindInterpolatedStringParts(unconvertedInterpolatedString, diagnostics), data: null); } @@ -206,6 +204,170 @@ bool tryBindAsHandlerType([NotNullWhen(true)] out BoundInterpolatedString? resul } } + private static bool AllInterpolatedStringPartsAreStrings(ImmutableArray parts) + => parts.All(p => p is BoundLiteral or BoundStringInsert { Value: { Type: { SpecialType: SpecialType.System_String } }, Alignment: null, Format: null }); + + private bool TryBindUnconvertedBinaryOperatorToDefaultInterpolatedStringHandler(BoundBinaryOperator binaryOperator, BindingDiagnosticBag diagnostics, [NotNullWhen(true)] out BoundBinaryOperator? convertedBinaryOperator) + { + // Much like BindUnconvertedInterpolatedStringToString above, we only want to use DefaultInterpolatedStringHandler if it's worth it. We therefore + // check for cases 1 and 2: if they are present, we let normal string binary operator binding machinery handle it. Otherwise, we take care of it ourselves. + Debug.Assert(binaryOperator.IsUnconvertedInterpolatedStringAddition); + convertedBinaryOperator = null; + + if (InExpressionTree) + { + return false; + } + + var interpolatedStringHandlerType = Compilation.GetWellKnownType(WellKnownType.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler); + if (interpolatedStringHandlerType.IsErrorType()) + { + // Can't ever bind to the handler no matter what, so just let the default handling take care of it. Cases 4 and 5 are covered by this. + return false; + } + + bool isConstant = true; + var stack = ArrayBuilder.GetInstance(); + var partsArrayBuilder = ArrayBuilder>.GetInstance(); + int partsCount = 0; + + BoundBinaryOperator? current = binaryOperator; + + while (current != null) + { + Debug.Assert(current.IsUnconvertedInterpolatedStringAddition); + stack.Push(current); + isConstant = isConstant && current.Right.ConstantValue is not null; + var rightInterpolatedString = (BoundUnconvertedInterpolatedString)current.Right; + + if (rightInterpolatedString.Parts.ContainsAwaitExpression()) + { + // Exception to case 3. Delegate to standard binding. + stack.Free(); + partsArrayBuilder.Free(); + return false; + } + + partsCount += rightInterpolatedString.Parts.Length; + partsArrayBuilder.Add(rightInterpolatedString.Parts); + + switch (current.Left) + { + case BoundBinaryOperator leftOperator: + current = leftOperator; + continue; + case BoundUnconvertedInterpolatedString interpolatedString: + isConstant = isConstant && interpolatedString.ConstantValue is not null; + + if (interpolatedString.Parts.ContainsAwaitExpression()) + { + // Exception to case 3. Delegate to standard binding. + stack.Free(); + partsArrayBuilder.Free(); + return false; + } + + partsCount += interpolatedString.Parts.Length; + partsArrayBuilder.Add(interpolatedString.Parts); + current = null; + break; + default: + throw ExceptionUtilities.UnexpectedValue(current.Left.Kind); + } + } + + Debug.Assert(partsArrayBuilder.Count == stack.Count + 1); + Debug.Assert(partsArrayBuilder.Count >= 2); + + if (isConstant || + (partsCount <= 4 && partsArrayBuilder.All(static parts => AllInterpolatedStringPartsAreStrings(parts)))) + { + // This is case 1 and 2. Let the standard machinery handle it + stack.Free(); + partsArrayBuilder.Free(); + return false; + } + + // Parts were added to the array from right to left, but lexical order is left to right. + partsArrayBuilder.ReverseContents(); + + // Case 3. Bind as handler. + var (appendCalls, data) = BindUnconvertedInterpolatedPartsToHandlerType( + binaryOperator.Syntax, + partsArrayBuilder.ToImmutableAndFree(), + interpolatedStringHandlerType, + diagnostics, + isHandlerConversion: false, + additionalConstructorArguments: default, + additionalConstructorRefKinds: default); + + // Now that the parts have been bound, reconstruct the binary operators. + convertedBinaryOperator = UpdateBinaryOperatorWithInterpolatedContents(stack, appendCalls, data, binaryOperator.Syntax, diagnostics); + stack.Free(); + return true; + } + + private BoundBinaryOperator UpdateBinaryOperatorWithInterpolatedContents(ArrayBuilder stack, ImmutableArray> appendCalls, InterpolatedStringHandlerData data, SyntaxNode rootSyntax, BindingDiagnosticBag diagnostics) + { + Debug.Assert(appendCalls.Length == stack.Count + 1); + var @string = GetSpecialType(SpecialType.System_String, diagnostics, rootSyntax); + + var bottomOperator = stack.Pop(); + var result = createBinaryOperator(bottomOperator, createInterpolation(bottomOperator.Left, appendCalls[0]), rightIndex: 1); + + for (int i = 2; i < appendCalls.Length; i++) + { + result = createBinaryOperator(stack.Pop(), result, rightIndex: i); + } + + return result.Update(BoundBinaryOperator.UncommonData.InterpolatedStringHandlerAddition(data)); + + BoundBinaryOperator createBinaryOperator(BoundBinaryOperator original, BoundExpression left, int rightIndex) + => new BoundBinaryOperator( + original.Syntax, + BinaryOperatorKind.StringConcatenation, + left, + createInterpolation(original.Right, appendCalls[rightIndex]), + original.ConstantValue, + methodOpt: null, + constrainedToTypeOpt: null, + LookupResultKind.Viable, + originalUserDefinedOperatorsOpt: default, + @string, + original.HasErrors); + + static BoundInterpolatedString createInterpolation(BoundExpression expression, ImmutableArray parts) + { + Debug.Assert(expression is BoundUnconvertedInterpolatedString); + return new BoundInterpolatedString( + expression.Syntax, + interpolationData: null, + parts, + expression.ConstantValue, + expression.Type, + expression.HasErrors); + } + } + + private BoundExpression BindUnconvertedInterpolatedExpressionToHandlerType( + BoundExpression unconvertedExpression, + NamedTypeSymbol interpolatedStringHandlerType, + BindingDiagnosticBag diagnostics, + ImmutableArray additionalConstructorArguments = default, + ImmutableArray additionalConstructorRefKinds = default) + => unconvertedExpression switch + { + BoundUnconvertedInterpolatedString interpolatedString => BindUnconvertedInterpolatedStringToHandlerType( + interpolatedString, + interpolatedStringHandlerType, + diagnostics, + isHandlerConversion: true, + additionalConstructorArguments, + additionalConstructorRefKinds), + BoundBinaryOperator binary => BindUnconvertedBinaryOperatorToInterpolatedStringHandlerType(binary, interpolatedStringHandlerType, diagnostics, additionalConstructorArguments, additionalConstructorRefKinds), + _ => throw ExceptionUtilities.UnexpectedValue(unconvertedExpression.Kind) + }; + private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( BoundUnconvertedInterpolatedString unconvertedInterpolatedString, NamedTypeSymbol interpolatedStringHandlerType, @@ -213,6 +375,84 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( bool isHandlerConversion, ImmutableArray additionalConstructorArguments = default, ImmutableArray additionalConstructorRefKinds = default) + { + var (appendCalls, interpolationData) = BindUnconvertedInterpolatedPartsToHandlerType( + unconvertedInterpolatedString.Syntax, + ImmutableArray.Create(unconvertedInterpolatedString.Parts), + interpolatedStringHandlerType, diagnostics, + isHandlerConversion, + additionalConstructorArguments, + additionalConstructorRefKinds); + + Debug.Assert(appendCalls.Length == 1); + + return new BoundInterpolatedString( + unconvertedInterpolatedString.Syntax, + interpolationData, + appendCalls[0], + unconvertedInterpolatedString.ConstantValue, + unconvertedInterpolatedString.Type, + unconvertedInterpolatedString.HasErrors); + } + + private BoundBinaryOperator BindUnconvertedBinaryOperatorToInterpolatedStringHandlerType( + BoundBinaryOperator binaryOperator, + NamedTypeSymbol interpolatedStringHandlerType, + BindingDiagnosticBag diagnostics, + ImmutableArray additionalConstructorArguments, + ImmutableArray additionalConstructorRefKinds) + { + Debug.Assert(binaryOperator.IsUnconvertedInterpolatedStringAddition); + + var stack = ArrayBuilder.GetInstance(); + var partsArrayBuilder = ArrayBuilder>.GetInstance(); + + BoundBinaryOperator? current = binaryOperator; + + while (current != null) + { + stack.Push(current); + partsArrayBuilder.Add(((BoundUnconvertedInterpolatedString)current.Right).Parts); + + if (current.Left is BoundBinaryOperator next) + { + current = next; + } + else + { + partsArrayBuilder.Add(((BoundUnconvertedInterpolatedString)current.Left).Parts); + current = null; + } + } + + // Parts are added in right to left order, but lexical is left to right. + partsArrayBuilder.ReverseContents(); + + Debug.Assert(partsArrayBuilder.Count == stack.Count + 1); + Debug.Assert(partsArrayBuilder.Count >= 2); + + var (appendCalls, data) = BindUnconvertedInterpolatedPartsToHandlerType( + binaryOperator.Syntax, + partsArrayBuilder.ToImmutableAndFree(), + interpolatedStringHandlerType, + diagnostics, + isHandlerConversion: true, + additionalConstructorArguments, + additionalConstructorRefKinds); + + var result = UpdateBinaryOperatorWithInterpolatedContents(stack, appendCalls, data, binaryOperator.Syntax, diagnostics); + stack.Free(); + return result; + } + + private (ImmutableArray> AppendCalls, InterpolatedStringHandlerData Data) BindUnconvertedInterpolatedPartsToHandlerType( + SyntaxNode syntax, + ImmutableArray> partsArray, + NamedTypeSymbol interpolatedStringHandlerType, + BindingDiagnosticBag diagnostics, + bool isHandlerConversion, + ImmutableArray additionalConstructorArguments, + ImmutableArray additionalConstructorRefKinds) { Debug.Assert(additionalConstructorArguments.IsDefault ? additionalConstructorRefKinds.IsDefault @@ -220,12 +460,12 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( additionalConstructorArguments = additionalConstructorArguments.NullToEmpty(); additionalConstructorRefKinds = additionalConstructorRefKinds.NullToEmpty(); - ReportUseSite(interpolatedStringHandlerType, diagnostics, unconvertedInterpolatedString.Syntax); + ReportUseSite(interpolatedStringHandlerType, diagnostics, 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); + var implicitBuilderReceiver = new BoundInterpolatedStringHandlerPlaceholder(syntax, interpolatedStringHandlerType) { WasCompilerGenerated = true }; + var (appendCallsArray, usesBoolReturn, positionInfo, baseStringLength, numFormatHoles) = BindInterpolatedStringAppendCalls(partsArray, 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 @@ -235,46 +475,49 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( bool needToCheckConversionToObject = false; if (isHandlerConversion) { - CheckFeatureAvailability(unconvertedInterpolatedString.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); + CheckFeatureAvailability(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)); + Debug.Assert(appendCallsArray.Select(a => a.Length).SequenceEqual(partsArray.Select(a => a.Length))); + Debug.Assert(appendCallsArray.All(appendCalls => 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); + TypeSymbol objectType = GetSpecialType(SpecialType.System_Object, diagnostics, syntax); BindingDiagnosticBag conversionDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false); - foreach (var currentPart in unconvertedInterpolatedString.Parts) + foreach (var parts in partsArray) { - if (currentPart is BoundStringInsert insert) + foreach (var currentPart in parts) { - var value = insert.Value; - bool reported = false; - if (value.Type is not null) + if (currentPart is BoundStringInsert insert) { - value = BindToNaturalType(value, conversionDiagnostics); - if (conversionDiagnostics.HasAnyErrors()) + var value = insert.Value; + bool reported = false; + if (value.Type is not null) { - CheckFeatureAvailability(value.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); - reported = true; + 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()) + if (!reported) { - CheckFeatureAvailability(value.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); + _ = GenerateConversionForAssignment(objectType, value, conversionDiagnostics); + if (conversionDiagnostics.HasAnyErrors()) + { + CheckFeatureAvailability(value.Syntax, MessageID.IDS_FeatureImprovedInterpolatedStrings, diagnostics); + } } - } - conversionDiagnostics.Clear(); + conversionDiagnostics.Clear(); + } } } @@ -282,7 +525,7 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( } - var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, unconvertedInterpolatedString.Syntax); + var intType = GetSpecialType(SpecialType.System_Int32, diagnostics, syntax); int constructorArgumentLength = 3 + additionalConstructorArguments.Length; var argumentsBuilder = ArrayBuilder.GetInstance(constructorArgumentLength); @@ -293,15 +536,15 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( // 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 boolType = GetSpecialType(SpecialType.System_Boolean, diagnostics, syntax); + var trailingConstructorValidityPlaceholder = new BoundInterpolatedStringArgumentPlaceholder(syntax, BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter, valSafeToEscape: LocalScopeDepth, boolType); var outConstructorAdditionalArguments = additionalConstructorArguments.Add(trailingConstructorValidityPlaceholder); refKindsBuilder.Add(RefKind.Out); - populateArguments(unconvertedInterpolatedString.Syntax, outConstructorAdditionalArguments, baseStringLength, numFormatHoles, intType, argumentsBuilder); + populateArguments(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); + var outConstructorCall = MakeConstructorInvocation(interpolatedStringHandlerType, argumentsBuilder, refKindsBuilder, syntax, outConstructorDiagnostics); if (outConstructorCall is not BoundObjectCreationExpression { ResultKind: LookupResultKind.Viable }) { // MakeConstructorInvocation can call CoerceArguments on the builder if overload resolution succeeded ignoring accessibility, which @@ -309,11 +552,11 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( argumentsBuilder.Clear(); // Try again without an out parameter. - populateArguments(unconvertedInterpolatedString.Syntax, additionalConstructorArguments, baseStringLength, numFormatHoles, intType, argumentsBuilder); + populateArguments(syntax, additionalConstructorArguments, baseStringLength, numFormatHoles, intType, argumentsBuilder); refKindsBuilder.RemoveLast(); var nonOutConstructorDiagnostics = BindingDiagnosticBag.GetInstance(template: outConstructorDiagnostics); - BoundExpression nonOutConstructorCall = MakeConstructorInvocation(interpolatedStringHandlerType, argumentsBuilder, refKindsBuilder, unconvertedInterpolatedString.Syntax, nonOutConstructorDiagnostics); + BoundExpression nonOutConstructorCall = MakeConstructorInvocation(interpolatedStringHandlerType, argumentsBuilder, refKindsBuilder, syntax, nonOutConstructorDiagnostics); if (nonOutConstructorCall is BoundObjectCreationExpression { ResultKind: LookupResultKind.Viable }) { @@ -373,23 +616,19 @@ private BoundInterpolatedString BindUnconvertedInterpolatedStringToHandlerType( 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); + diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, 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); + var interpolationData = new InterpolatedStringHandlerData( + interpolatedStringHandlerType, + constructorCall, + usesBoolReturn, + LocalScopeDepth, + additionalConstructorArguments.NullToEmpty(), + positionInfo, + implicitBuilderReceiver); + + return (appendCallsArray, interpolationData); static void populateArguments(SyntaxNode syntax, ImmutableArray additionalConstructorArguments, int baseStringLength, int numFormatHoles, NamedTypeSymbol intType, ArrayBuilder argumentsBuilder) { @@ -447,117 +686,128 @@ private ImmutableArray BindInterpolatedStringParts(BoundUnconve return partsBuilder?.ToImmutableAndFree() ?? unconvertedInterpolatedString.Parts; } - private (ImmutableArray AppendFormatCalls, bool UsesBoolReturn, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>, int BaseStringLength, int NumFormatHoles) BindInterpolatedStringAppendCalls( - BoundUnconvertedInterpolatedString source, + private (ImmutableArray> AppendFormatCalls, bool UsesBoolReturn, ImmutableArray>, int BaseStringLength, int NumFormatHoles) BindInterpolatedStringAppendCalls( + ImmutableArray> partsArray, BoundInterpolatedStringHandlerPlaceholder implicitBuilderReceiver, BindingDiagnosticBag diagnostics) { - if (source.Parts.IsEmpty) + if (partsArray.IsEmpty && partsArray.All(p => p.IsEmpty)) { - return (ImmutableArray.Empty, false, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>.Empty, 0, 0); + return (ImmutableArray>.Empty, false, ImmutableArray>.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 firstPartsLength = partsArray[0].Length; + var builderAppendCallsArray = ArrayBuilder>.GetInstance(partsArray.Length); + var builderAppendCalls = ArrayBuilder.GetInstance(firstPartsLength); + var positionInfoArray = ArrayBuilder>.GetInstance(partsArray.Length); + var positionInfo = ArrayBuilder<(bool IsLiteral, bool HasAlignment, bool HasFormat)>.GetInstance(firstPartsLength); var argumentsBuilder = ArrayBuilder.GetInstance(3); var parameterNamesAndLocationsBuilder = ArrayBuilder<(string, Location)?>.GetInstance(3); int baseStringLength = 0; int numFormatHoles = 0; - foreach (var part in source.Parts) + foreach (var parts in partsArray) { - Debug.Assert(part is BoundLiteral or BoundStringInsert); - string methodName; - bool isLiteral; - bool hasAlignment; - bool hasFormat; - - if (part is BoundStringInsert insert) + foreach (var part in parts) { - methodName = "AppendFormatted"; - argumentsBuilder.Add(insert.Value); - parameterNamesAndLocationsBuilder.Add(null); - isLiteral = false; - hasAlignment = false; - hasFormat = false; - - if (insert.Alignment is not null) + Debug.Assert(part is BoundLiteral or BoundStringInsert); + string methodName; + bool isLiteral; + bool hasAlignment; + bool hasFormat; + + if (part is BoundStringInsert insert) { - hasAlignment = true; - argumentsBuilder.Add(insert.Alignment); - parameterNamesAndLocationsBuilder.Add(("alignment", insert.Alignment.Syntax.Location)); + 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++; } - if (insert.Format is not null) + else { - hasFormat = true; - argumentsBuilder.Add(insert.Format); - parameterNamesAndLocationsBuilder.Add(("format", insert.Format.Syntax.Location)); + 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; } - 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) + var arguments = argumentsBuilder.ToImmutableAndClear(); + ImmutableArray<(string, Location)?> parameterNamesAndLocations; + if (parameterNamesAndLocationsBuilder.Count > 1) { - // Interpolated string handler method '{0}' is malformed. It does not return 'void' or 'bool'. - diagnostics.Add(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, part.Syntax.Location, method); + parameterNamesAndLocations = parameterNamesAndLocationsBuilder.ToImmutableAndClear(); } - else if (builderPatternExpectsBool == null) + else { - builderPatternExpectsBool = methodReturnsBool; + Debug.Assert(parameterNamesAndLocationsBuilder.Count == 0 || parameterNamesAndLocationsBuilder[0] == null); + parameterNamesAndLocations = default; + parameterNamesAndLocationsBuilder.Clear(); } - else if (builderPatternExpectsBool != methodReturnsBool) + + 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 }) { - // 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); + 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); + } } } + + builderAppendCallsArray.Add(builderAppendCalls.ToImmutableAndClear()); + positionInfoArray.Add(positionInfo.ToImmutableAndClear()); } argumentsBuilder.Free(); parameterNamesAndLocationsBuilder.Free(); - return (builderAppendCalls.ToImmutableAndFree(), builderPatternExpectsBool ?? false, positionInfo.ToImmutableAndFree(), baseStringLength, numFormatHoles); + builderAppendCalls.Free(); + positionInfo.Free(); + return (builderAppendCallsArray.ToImmutableAndFree(), builderPatternExpectsBool ?? false, positionInfoArray.ToImmutableAndFree(), baseStringLength, numFormatHoles); } private BoundExpression BindInterpolatedStringHandlerInMemberCall( - BoundUnconvertedInterpolatedString unconvertedString, + BoundExpression unconvertedString, ArrayBuilder arguments, ImmutableArray parameters, ref MemberAnalysisResult memberAnalysisResult, @@ -567,6 +817,7 @@ private BoundExpression BindInterpolatedStringHandlerInMemberCall( uint receiverEscapeScope, BindingDiagnosticBag diagnostics) { + Debug.Assert(unconvertedString is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true }); var interpolatedStringConversion = memberAnalysisResult.ConversionForArg(interpolatedStringArgNum); Debug.Assert(interpolatedStringConversion.IsInterpolatedStringHandler); var interpolatedStringParameter = GetCorrespondingParameter(ref memberAnalysisResult, parameters, interpolatedStringArgNum); @@ -741,11 +992,10 @@ private BoundExpression BindInterpolatedStringHandlerInMemberCall( argumentRefKindsBuilder.Add(refKind); } - var interpolatedString = BindUnconvertedInterpolatedStringToHandlerType( + var interpolatedString = BindUnconvertedInterpolatedExpressionToHandlerType( unconvertedString, (NamedTypeSymbol)interpolatedStringParameter.Type, diagnostics, - isHandlerConversion: true, additionalConstructorArguments: argumentPlaceholdersBuilder.ToImmutableAndFree(), additionalConstructorRefKinds: argumentRefKindsBuilder.ToImmutableAndFree()); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 0e4bcb0bbecad..970a1c13a1824 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -481,7 +481,7 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi BindValueKind bindValueKind = GetBinaryAssignmentKind(syntaxNode.Kind()); BoundExpression left = CheckValue(result, bindValueKind, diagnostics); BoundExpression right = BindValue(syntaxNode.Right, diagnostics, BindValueKind.RValue); - BoundExpression boundOp = BindSimpleBinaryOperator(syntaxNode, diagnostics, left, right); + BoundExpression boundOp = BindSimpleBinaryOperator(syntaxNode, diagnostics, left, right, leaveUnconvertedIfInterpolatedString: true); result = boundOp; } @@ -490,7 +490,7 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi } private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, BindingDiagnosticBag diagnostics, - BoundExpression left, BoundExpression right) + BoundExpression left, BoundExpression right, bool leaveUnconvertedIfInterpolatedString) { BinaryOperatorKind kind = SyntaxKindToBinaryOperatorKind(node.Kind()); @@ -536,6 +536,15 @@ private BoundExpression BindSimpleBinaryOperator(BinaryExpressionSyntax node, Bi return BindTupleBinaryOperator(node, kind, left, right, diagnostics); } + if (leaveUnconvertedIfInterpolatedString + && kind == BinaryOperatorKind.Addition + && left is BoundUnconvertedInterpolatedString or BoundBinaryOperator { IsUnconvertedInterpolatedStringAddition: true } + && right is BoundUnconvertedInterpolatedString) + { + var stringConstant = FoldBinaryOperator(node, BinaryOperatorKind.StringConcatenation, left, right, SpecialType.System_String, diagnostics); + return new BoundBinaryOperator(node, BinaryOperatorKind.StringConcatenation, BoundBinaryOperator.UncommonData.UnconvertedInterpolatedStringAddition(stringConstant), LookupResultKind.Empty, left, right, right.Type); + } + // SPEC: For an operation of one of the forms x == null, null == x, x != null, null != x, // SPEC: where x is an expression of nullable type, if operator overload resolution // SPEC: fails to find an applicable operator, the result is instead computed from @@ -694,6 +703,40 @@ private bool BindSimpleBinaryOperatorParts(BinaryExpressionSyntax node, BindingD return foundOperator; } +#nullable enable + private BoundExpression RebindSimpleBinaryOperatorAsConverted(BoundBinaryOperator unconvertedBinaryOperator, BindingDiagnosticBag diagnostics) + { + if (TryBindUnconvertedBinaryOperatorToDefaultInterpolatedStringHandler(unconvertedBinaryOperator, diagnostics, out var convertedBinaryOperator)) + { + return convertedBinaryOperator; + } + + var stack = ArrayBuilder.GetInstance(); + BoundBinaryOperator? current = unconvertedBinaryOperator; + + while (current != null) + { + Debug.Assert(current.IsUnconvertedInterpolatedStringAddition); + stack.Push(current); + current = current.Left as BoundBinaryOperator; + } + + Debug.Assert(stack.Count > 0 && stack.Peek().Left is BoundUnconvertedInterpolatedString); + + BoundExpression? left = null; + while (stack.TryPop(out current)) + { + Debug.Assert(current.Right is BoundUnconvertedInterpolatedString); + left = BindSimpleBinaryOperator((BinaryExpressionSyntax)current.Syntax, diagnostics, left ?? current.Left, current.Right, leaveUnconvertedIfInterpolatedString: false); + } + + Debug.Assert(left != null); + Debug.Assert(stack.Count == 0); + stack.Free(); + return left; + } +#nullable disable + private static void ReportUnaryOperatorError(CSharpSyntaxNode node, BindingDiagnosticBag diagnostics, string operatorName, BoundExpression operand, LookupResultKind resultKind) { if (operand.IsLiteralDefault()) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 08cb704d851a2..34f2a42406241 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -2276,7 +2276,8 @@ void reportMethodGroupErrors(BoundMethodGroup methodGroup, bool fromAddressOf) { errorCode = ErrorCode.ERR_AddressOfToNonFunctionPointer; } - else if (targetType.SpecialType == SpecialType.System_Delegate) + else if (targetType.SpecialType == SpecialType.System_Delegate && + syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType)) { Error(diagnostics, ErrorCode.ERR_CannotInferDelegateType, location); return; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs index 0371c6befc478..99224f5d81fff 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_TupleOperators.cs @@ -95,7 +95,7 @@ private TupleBinaryOperatorInfo BindTupleBinaryOperatorInfo(BinaryExpressionSynt return BindTupleBinaryOperatorNestedInfo(node, kind, left, right, diagnostics); } - BoundExpression comparison = BindSimpleBinaryOperator(node, diagnostics, left, right); + BoundExpression comparison = BindSimpleBinaryOperator(node, diagnostics, left, right, leaveUnconvertedIfInterpolatedString: false); switch (comparison) { case BoundLiteral _: diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs index a19fc99c6915c..93c3e2c8c9d21 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -537,7 +537,10 @@ private Tests MakeTestsAndBindingsForRecursivePattern( Debug.Assert(recursive.HasAnyErrors); tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true))); } - tests.Add(MakeTestsAndBindings(currentInput, pattern, bindings)); + else + { + tests.Add(MakeTestsAndBindings(currentInput, pattern, bindings)); + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index 4d3ae007d3af5..f4365caae331a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -45,7 +45,7 @@ protected override ConversionsBase WithNullabilityCore(bool includeNullability) public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { // Must be a bona fide delegate type, not an expression tree type. - if (!(destination.IsDelegateType() || destination.SpecialType == SpecialType.System_Delegate)) + if (!(destination.IsDelegateType() || (destination.SpecialType == SpecialType.System_Delegate && IsFeatureInferredDelegateTypeEnabled(source)))) { return Conversion.NoConversion; } @@ -82,7 +82,7 @@ public override Conversion GetMethodGroupFunctionPointerConversion(BoundMethodGr return conversion; } - protected override Conversion GetInterpolatedStringConversion(BoundUnconvertedInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) + protected override Conversion GetInterpolatedStringConversion(TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { if (destination is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }) { @@ -132,7 +132,7 @@ private static MethodGroupResolution ResolveDelegateOrFunctionPointerMethodGroup return (signature, true, new CallingConventionInfo(signature.CallingConvention, signature.GetCallingConventionModifiers())); } - var delegateType = (type.SpecialType == SpecialType.System_Delegate) ? + var delegateType = (type.SpecialType == SpecialType.System_Delegate) && methodGroup.Syntax.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes) ? // https://github.com/dotnet/roslyn/issues/52869: Avoid calculating the delegate type multiple times during conversion. _binder.GetMethodGroupDelegateType(methodGroup, ref useSiteInfo) : type.GetDelegateType(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index 88611390f4181..39b04775d1f5a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -71,7 +71,7 @@ internal ConversionsBase WithNullability(bool includeNullability) protected abstract ConversionsBase CreateInstance(int currentRecursionDepth); - protected abstract Conversion GetInterpolatedStringConversion(BoundUnconvertedInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo); + protected abstract Conversion GetInterpolatedStringConversion(TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo); internal AssemblySymbol CorLibrary { get { return corLibrary; } } @@ -931,7 +931,8 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi break; case BoundKind.UnconvertedInterpolatedString: - Conversion interpolatedStringConversion = GetInterpolatedStringConversion((BoundUnconvertedInterpolatedString)sourceExpression, destination, ref useSiteInfo); + case BoundKind.BinaryOperator when ((BoundBinaryOperator)sourceExpression).IsUnconvertedInterpolatedStringAddition: + Conversion interpolatedStringConversion = GetInterpolatedStringConversion(destination, ref useSiteInfo); if (interpolatedStringConversion.Exists) { return interpolatedStringConversion; @@ -1407,20 +1408,31 @@ public static LambdaConversionResult IsAnonymousFunctionCompatibleWithType(Unbou if (type.SpecialType == SpecialType.System_Delegate) { - return LambdaConversionResult.Success; + if (IsFeatureInferredDelegateTypeEnabled(anonymousFunction)) + { + return LambdaConversionResult.Success; + } } else if (type.IsDelegateType()) { return IsAnonymousFunctionCompatibleWithDelegate(anonymousFunction, type, isTargetExpressionTree: false); } - else if (type.IsGenericOrNonGenericExpressionType(out bool _)) + else if (type.IsGenericOrNonGenericExpressionType(out bool isGenericType)) { - return IsAnonymousFunctionCompatibleWithExpressionTree(anonymousFunction, (NamedTypeSymbol)type); + if (isGenericType || IsFeatureInferredDelegateTypeEnabled(anonymousFunction)) + { + return IsAnonymousFunctionCompatibleWithExpressionTree(anonymousFunction, (NamedTypeSymbol)type); + } } return LambdaConversionResult.BadTargetType; } + internal static bool IsFeatureInferredDelegateTypeEnabled(BoundExpression expr) + { + return expr.Syntax.IsFeatureEnabled(MessageID.IDS_FeatureInferredDelegateType); + } + private static bool HasAnonymousFunctionConversion(BoundExpression source, TypeSymbol destination) { Debug.Assert(source != null); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs index 3599c567ad5f4..1579e8b570a58 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/TypeConversions.cs @@ -53,7 +53,7 @@ public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation throw ExceptionUtilities.Unreachable; } - protected override Conversion GetInterpolatedStringConversion(BoundUnconvertedInterpolatedString source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) + protected override Conversion GetInterpolatedStringConversion(TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) { // Conversions involving interpolated strings require a Binder. throw ExceptionUtilities.Unreachable; diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 52d777bb3f25b..b4a694c4a4cbb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -2138,7 +2138,7 @@ private static BetterResult PreferValOverInOrRefInterpolatedHandlerParameters + new UncommonData( + constantValue, + method: null, + constrainedToType: null, + originalUserDefinedOperatorsOpt: default, + isUnconvertedInterpolatedStringAddition: true, + interpolatedStringHandlerData: null); + + public static UncommonData InterpolatedStringHandlerAddition(InterpolatedStringHandlerData data) + => new UncommonData( + constantValue: null, + method: null, + constrainedToType: null, + originalUserDefinedOperatorsOpt: default, + isUnconvertedInterpolatedStringAddition: false, + data); + public static UncommonData? CreateIfNeeded(ConstantValue? constantValue, MethodSymbol? method, TypeSymbol? constrainedToType, ImmutableArray originalUserDefinedOperatorsOpt) { if (constantValue != null || method is not null || constrainedToType is not null || !originalUserDefinedOperatorsOpt.IsDefault) { - return new UncommonData(constantValue, method, constrainedToType, originalUserDefinedOperatorsOpt); + return new UncommonData(constantValue, method, constrainedToType, originalUserDefinedOperatorsOpt, isUnconvertedInterpolatedStringAddition: false, interpolatedStringHandlerData: null); } return null; @@ -24,18 +43,33 @@ internal class UncommonData public readonly ConstantValue? ConstantValue; public readonly MethodSymbol? Method; public readonly TypeSymbol? ConstrainedToType; + public readonly bool IsUnconvertedInterpolatedStringAddition; + public readonly InterpolatedStringHandlerData? InterpolatedStringHandlerData; // The set of method symbols from which this operator's method was chosen. // Only kept in the tree if the operator was an error and overload resolution // was unable to choose a best method. public readonly ImmutableArray OriginalUserDefinedOperatorsOpt; - private UncommonData(ConstantValue? constantValue, MethodSymbol? method, TypeSymbol? constrainedToType, ImmutableArray originalUserDefinedOperatorsOpt) + private UncommonData(ConstantValue? constantValue, MethodSymbol? method, TypeSymbol? constrainedToType, ImmutableArray originalUserDefinedOperatorsOpt, bool isUnconvertedInterpolatedStringAddition, InterpolatedStringHandlerData? interpolatedStringHandlerData) { + Debug.Assert(interpolatedStringHandlerData is null || !isUnconvertedInterpolatedStringAddition); ConstantValue = constantValue; Method = method; ConstrainedToType = constrainedToType; OriginalUserDefinedOperatorsOpt = originalUserDefinedOperatorsOpt; + IsUnconvertedInterpolatedStringAddition = isUnconvertedInterpolatedStringAddition; + InterpolatedStringHandlerData = interpolatedStringHandlerData; + } + + public UncommonData WithUpdatedMethod(MethodSymbol? method) + { + if ((object?)method == Method) + { + return this; + } + + return new UncommonData(ConstantValue, method, ConstrainedToType, OriginalUserDefinedOperatorsOpt, IsUnconvertedInterpolatedStringAddition, InterpolatedStringHandlerData); } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 1963b9eef2d5f..6e6a2fa580741 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -66,6 +66,8 @@ internal bool NeedsToBeConverted() // syntactic context where it could be either a pointer or a span, and // in that case it requires conversion to one or the other. return this.Type is null; + case BoundKind.BinaryOperator: + return ((BoundBinaryOperator)this).IsUnconvertedInterpolatedStringAddition; #if DEBUG case BoundKind.Local when !WasConverted: case BoundKind.Parameter when !WasConverted: @@ -284,6 +286,10 @@ internal partial class BoundBinaryOperator internal TypeSymbol? ConstrainedToType => Data?.ConstrainedToType; + internal bool IsUnconvertedInterpolatedStringAddition => Data?.IsUnconvertedInterpolatedStringAddition ?? false; + + internal InterpolatedStringHandlerData? InterpolatedStringHandlerData => Data?.InterpolatedStringHandlerData; + internal ImmutableArray OriginalUserDefinedOperatorsOpt => Data?.OriginalUserDefinedOperatorsOpt ?? default(ImmutableArray); } diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index b5f1b956ac165..f160d0ffb6154 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -417,6 +417,11 @@ public BoundBinaryOperator Update(BinaryOperatorKind operatorKind, var uncommonData = UncommonData.CreateIfNeeded(constantValueOpt, methodOpt, constrainedToTypeOpt, OriginalUserDefinedOperatorsOpt); return Update(operatorKind, uncommonData, resultKind, left, right, type); } + + public BoundBinaryOperator Update(UncommonData uncommonData) + { + return Update(OperatorKind, uncommonData, ResultKind, Left, Right, Type); + } } internal sealed partial class BoundUserDefinedConditionalLogicalOperator diff --git a/src/Compilers/CSharp/Portable/BoundTree/InterpolatedStringHandlerData.cs b/src/Compilers/CSharp/Portable/BoundTree/InterpolatedStringHandlerData.cs index f1dbba23e7cef..03968639e19e2 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/InterpolatedStringHandlerData.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/InterpolatedStringHandlerData.cs @@ -23,7 +23,7 @@ internal readonly struct InterpolatedStringHandlerData /// public readonly ImmutableArray ArgumentPlaceholders; - public readonly ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)> PositionInfo; + public readonly ImmutableArray> PositionInfo; public bool HasTrailingHandlerValidityParameter => ArgumentPlaceholders.Length > 0 && ArgumentPlaceholders[^1].ArgumentIndex == BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter; @@ -35,7 +35,7 @@ public InterpolatedStringHandlerData( bool usesBoolReturns, uint scopeOfContainingExpression, ImmutableArray placeholders, - ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)> positionInfo, + ImmutableArray> positionInfo, BoundInterpolatedStringHandlerPlaceholder receiverPlaceholder) { Debug.Assert(construction is BoundObjectCreationExpression or BoundDynamicObjectCreationExpression or BoundBadExpression); diff --git a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs index 1f110d2e55d3a..9fee831e3ffd8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs @@ -39,7 +39,7 @@ private BoundNode VisitBinaryOperatorBase(BoundBinaryOperatorBase binaryOperator stack.Push(currentBinary); currentBinary = currentBinary.Left as BoundBinaryOperatorBase; } - while (currentBinary is object); + while (currentBinary is not null); Debug.Assert(stack.Count > 0); var leftChild = (BoundExpression)Visit(stack.Peek().Left); @@ -56,7 +56,7 @@ private BoundNode VisitBinaryOperatorBase(BoundBinaryOperatorBase binaryOperator { BoundBinaryOperator binary => binary.Update( binary.OperatorKind, - BoundBinaryOperator.UncommonData.CreateIfNeeded(binary.ConstantValue, GetUpdatedSymbol(binary, binary.Method), binary.ConstrainedToType, binary.OriginalUserDefinedOperatorsOpt), + binary.Data?.WithUpdatedMethod(GetUpdatedSymbol(binary, binary.Method)), binary.ResultKind, leftChild, right, diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilationExtensions.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilationExtensions.cs index 038bf728ef2ce..46437b25d17a0 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilationExtensions.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilationExtensions.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Linq; namespace Microsoft.CodeAnalysis.CSharp @@ -12,7 +10,12 @@ internal static class CSharpCompilationExtensions { internal static bool IsFeatureEnabled(this CSharpCompilation compilation, MessageID feature) { - return ((CSharpParseOptions)compilation.SyntaxTrees.FirstOrDefault()?.Options)?.IsFeatureEnabled(feature) == true; + return ((CSharpParseOptions?)compilation.SyntaxTrees.FirstOrDefault()?.Options)?.IsFeatureEnabled(feature) == true; + } + + internal static bool IsFeatureEnabled(this SyntaxNode? syntax, MessageID feature) + { + return ((CSharpParseOptions?)syntax?.SyntaxTree.Options)?.IsFeatureEnabled(feature) == true; } } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index f74aab6458d45..64e4e7e680c5e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1150,75 +1150,32 @@ public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node) return null; } - protected BoundNode VisitInterpolatedStringBase(BoundInterpolatedStringBase node, InterpolatedStringHandlerData? data) +#nullable enable + protected BoundNode? VisitInterpolatedStringBase(BoundInterpolatedStringBase node, InterpolatedStringHandlerData? data) { // 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) + (BoundExpression? construction, bool useBoolReturns, bool firstPartIsConditional) = data switch { - 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; + null => (null, false, false), + { } d => (d.Construction, d.UsesBoolReturns, d.HasTrailingHandlerValidityParameter) + }; - if (hasTrailingValidityParameter) - { - beforePartsState = State.Clone(); - remainingParts = node.Parts.AsSpan(); - } - else - { - Visit(node.Parts[0]); - beforePartsState = State.Clone(); - remainingParts = node.Parts.AsSpan()[1..]; - } + VisitRvalue(construction); + bool hasConditionalEvaluation = useBoolReturns || firstPartIsConditional; + TLocalState? shortCircuitState = hasConditionalEvaluation ? State.Clone() : default; - foreach (var expr in remainingParts) - { - VisitRvalue(expr); - if (usesBoolReturns) - { - Join(ref beforePartsState, ref State); - } - } + _ = VisitInterpolatedStringHandlerParts(node, useBoolReturns, firstPartIsConditional, ref shortCircuitState); - if (usesBoolReturns) - { - // Already been joined after the last part, just assign - State = beforePartsState; - } - else - { - Debug.Assert(hasTrailingValidityParameter); - Join(ref State, ref beforePartsState); - } - - break; + if (hasConditionalEvaluation) + { + Debug.Assert(shortCircuitState != null); + Join(ref this.State, ref shortCircuitState); } return null; - - void visitParts() - { - foreach (var expr in node.Parts) - { - VisitRvalue(expr); - } - } } +#nullable disable public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) { @@ -2253,6 +2210,10 @@ public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) Debug.Assert(!node.OperatorKind.IsUserDefined()); VisitBinaryLogicalOperatorChildren(node); } + else if (node.InterpolatedStringHandlerData is { } data) + { + VisitBinaryInterpolatedStringAddition(node); + } else { VisitBinaryOperatorChildren(node); @@ -2404,7 +2365,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperator node) stack.Push(binary); binary = binary.Left as BoundBinaryOperator; } - while (binary != null && !binary.OperatorKind.IsLogical()); + while (binary != null && !binary.OperatorKind.IsLogical() && binary.InterpolatedStringHandlerData is null); VisitBinaryOperatorChildren(stack); stack.Free(); @@ -2537,6 +2498,88 @@ bool learnFromOperator(BoundBinaryOperator binary) return false; } } + + protected void VisitBinaryInterpolatedStringAddition(BoundBinaryOperator node) + { + Debug.Assert(node.InterpolatedStringHandlerData.HasValue); + var stack = ArrayBuilder.GetInstance(); + var data = node.InterpolatedStringHandlerData.GetValueOrDefault(); + + while (PushBinaryOperatorInterpolatedStringChildren(node, stack) is { } next) + { + node = next; + } + + Debug.Assert(stack.Count >= 2); + + VisitRvalue(data.Construction); + + bool visitedFirst = false; + bool hasTrailingHandlerValidityParameter = data.HasTrailingHandlerValidityParameter; + bool hasConditionalEvaluation = data.UsesBoolReturns || hasTrailingHandlerValidityParameter; + TLocalState? shortCircuitState = hasConditionalEvaluation ? State.Clone() : default; + + while (stack.TryPop(out var currentString)) + { + visitedFirst |= VisitInterpolatedStringHandlerParts(currentString, data.UsesBoolReturns, firstPartIsConditional: visitedFirst || hasTrailingHandlerValidityParameter, ref shortCircuitState); + } + + if (hasConditionalEvaluation) + { + Join(ref State, ref shortCircuitState); + } + + stack.Free(); + } + + protected virtual BoundBinaryOperator? PushBinaryOperatorInterpolatedStringChildren(BoundBinaryOperator node, ArrayBuilder stack) + { + stack.Push((BoundInterpolatedString)node.Right); + switch (node.Left) + { + case BoundBinaryOperator next: + return next; + case BoundInterpolatedString @string: + stack.Push(@string); + return null; + default: + throw ExceptionUtilities.UnexpectedValue(node.Left.Kind); + } + } + + protected virtual bool VisitInterpolatedStringHandlerParts(BoundInterpolatedStringBase node, bool usesBoolReturns, bool firstPartIsConditional, ref TLocalState? shortCircuitState) + { + Debug.Assert(shortCircuitState != null || (!usesBoolReturns && !firstPartIsConditional)); + if (node.Parts.IsEmpty) + { + return false; + } + + ReadOnlySpan parts; + + if (firstPartIsConditional) + { + parts = node.Parts.AsSpan(); + } + else + { + VisitRvalue(node.Parts[0]); + shortCircuitState = State.Clone(); + parts = node.Parts.AsSpan()[1..]; + } + + foreach (var part in parts) + { + VisitRvalue(part); + if (usesBoolReturns) + { + Debug.Assert(shortCircuitState != null); + Join(ref shortCircuitState, ref State); + } + } + + return true; + } #nullable disable public override BoundNode VisitUnaryOperator(BoundUnaryOperator node) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index d10c77498e301..1f7d55ec503f1 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -169,6 +169,11 @@ private void VerifyExpression(BoundExpression expression, bool overrideSkippedEx public override BoundNode? VisitBinaryOperator(BoundBinaryOperator node) { + if (node.InterpolatedStringHandlerData is { } data) + { + Visit(data.Construction); + } + VisitBinaryOperatorChildren(node); return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 59c1cdb3bedce..bed6d39441ebb 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -4024,6 +4024,20 @@ void splitAndLearnFromNonNullTest(BoundExpression operandComparedToNonNull, bool } } + protected override bool VisitInterpolatedStringHandlerParts(BoundInterpolatedStringBase node, bool usesBoolReturns, bool firstPartIsConditional, ref LocalState shortCircuitState) + { + var result = base.VisitInterpolatedStringHandlerParts(node, usesBoolReturns, firstPartIsConditional, ref shortCircuitState); + SetNotNullResult(node); + return result; + } + + protected override BoundBinaryOperator? PushBinaryOperatorInterpolatedStringChildren(BoundBinaryOperator node, ArrayBuilder stack) + { + var result = base.PushBinaryOperatorInterpolatedStringChildren(node, stack); + SetNotNullResult(node); + return result; + } + /// /// If we learn that the operand is non-null, we can infer that certain /// sub-expressions were also non-null. @@ -9769,14 +9783,11 @@ private static bool IsNullabilityMismatch(TypeSymbol type1, TypeSymbol type2) return result; } - 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; - } + // https://github.com/dotnet/roslyn/issues/54583 + // Better handle the constructor propogation + //public override BoundNode? VisitInterpolatedString(BoundInterpolatedString node) + //{ + //} public override BoundNode? VisitUnconvertedInterpolatedString(BoundUnconvertedInterpolatedString node) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 57112403252ab..538a84e6c6a70 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -111,6 +111,13 @@ public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDe public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryOperator? applyParentUnaryOperator) { + if (node.InterpolatedStringHandlerData is InterpolatedStringHandlerData data) + { + Debug.Assert(node.Type.SpecialType == SpecialType.System_String, "Non-string binary addition should have been handled by VisitConversion or VisitArguments"); + ImmutableArray parts = CollectBinaryOperatorInterpolatedStringParts(node); + return LowerPartsToString(data, parts, node.Syntax, node.Type); + } + // In machine-generated code we frequently end up with binary operator trees that are deep on the left, // such as a + b + c + d ... // To avoid blowing the call stack, we make an explicit stack of the binary operators to the left, @@ -120,6 +127,13 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO for (BoundBinaryOperator? current = node; current != null && current.ConstantValue == null; current = current.Left as BoundBinaryOperator) { + // The regular visit mechanism will handle this. + if (current.InterpolatedStringHandlerData is not null) + { + Debug.Assert(stack.Count >= 1); + break; + } + stack.Push(current); } @@ -136,6 +150,32 @@ public BoundExpression VisitBinaryOperator(BoundBinaryOperator node, BoundUnaryO return loweredLeft; } + private static ImmutableArray CollectBinaryOperatorInterpolatedStringParts(BoundBinaryOperator node) + { + Debug.Assert(node.OperatorKind == BinaryOperatorKind.StringConcatenation); + Debug.Assert(node.InterpolatedStringHandlerData is not null); + var partsBuilder = ArrayBuilder.GetInstance(); + while (true) + { + partsBuilder.AddRange(((BoundInterpolatedString)node.Right).Parts); + + if (node.Left is BoundBinaryOperator next) + { + node = next; + } + else + { + partsBuilder.AddRange(((BoundInterpolatedString)node.Left).Parts); + break; + } + } + + partsBuilder.ReverseContents(); + + ImmutableArray parts = partsBuilder.ToImmutableAndFree(); + return parts; + } + private BoundExpression MakeBinaryOperator( SyntaxNode syntax, BinaryOperatorKind operatorKind, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 57df57698e359..5f47e9158e3a7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -451,11 +451,16 @@ ImmutableArray addInterpolationPlace { var argument = arguments[argumentIndex]; - if (argument is BoundConversion { ConversionKind: ConversionKind.InterpolatedStringHandler, Operand: BoundInterpolatedString operand }) + if (argument is BoundConversion { ConversionKind: ConversionKind.InterpolatedStringHandler, Operand: BoundInterpolatedString or BoundBinaryOperator } conversion) { // Handler conversions are not supported in expression lambdas. Debug.Assert(!_inExpressionLambda); - var interpolationData = operand.InterpolationData.GetValueOrDefault(); + var interpolationData = conversion.Operand switch + { + BoundInterpolatedString { InterpolationData: { } d } => d, + BoundBinaryOperator { InterpolatedStringHandlerData: { } d } => d, + _ => throw ExceptionUtilities.UnexpectedValue(conversion.Operand.Kind) + }; var creation = (BoundObjectCreationExpression)interpolationData.Construction; if (creation.Arguments.Length > (interpolationData.HasTrailingHandlerValidityParameter ? 3 : 2)) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 005192bd4326f..dfef73364889b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -22,7 +22,14 @@ public override BoundNode VisitConversion(BoundConversion node) case ConversionKind.InterpolatedStringHandler: Debug.Assert(node.Type is NamedTypeSymbol { IsInterpolatedStringHandlerType: true }); - (ArrayBuilder handlerPatternExpressions, BoundLocal handlerLocal) = RewriteToInterpolatedStringHandlerPattern((BoundInterpolatedString)node.Operand); + var (data, parts) = node.Operand switch + { + BoundInterpolatedString { InterpolationData: { } d, Parts: { } p } => (d, p), + BoundBinaryOperator { InterpolatedStringHandlerData: { } d } binary => (d, CollectBinaryOperatorInterpolatedStringParts(binary)), + _ => throw ExceptionUtilities.UnexpectedValue(node.Operand.Kind) + }; + + (ArrayBuilder handlerPatternExpressions, BoundLocal handlerLocal) = RewriteToInterpolatedStringHandlerPattern(data, parts, node.Operand.Syntax); return _factory.Sequence(ImmutableArray.Create(handlerLocal.LocalSymbol), handlerPatternExpressions.ToImmutableAndFree(), handlerLocal); case ConversionKind.SwitchExpression or ConversionKind.ConditionalExpression: // Skip through target-typed conditionals and switches diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs index dc156979c39e3..41d28b15d083f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs @@ -43,12 +43,10 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv /// local temp. /// /// Caller is responsible for freeing the ArrayBuilder - private (ArrayBuilder HandlerPatternExpressions, BoundLocal Result) RewriteToInterpolatedStringHandlerPattern(BoundInterpolatedString node) + private (ArrayBuilder HandlerPatternExpressions, BoundLocal Result) RewriteToInterpolatedStringHandlerPattern(InterpolatedStringHandlerData data, ImmutableArray parts, SyntaxNode syntax) { - 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); + Debug.Assert(parts.All(static p => p is BoundCall or BoundDynamicInvocation)); + var builderTempSymbol = _factory.InterpolatedStringHandlerLocal(data.BuilderType, data.ScopeOfContainingExpression, syntax); BoundLocal builderTemp = _factory.Local(builderTempSymbol); // var handler = new HandlerType(baseStringLength, numFormatHoles, ...InterpolatedStringHandlerArgumentAttribute parameters, out bool appendShouldProceed); @@ -72,9 +70,9 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv AddPlaceholderReplacement(data.ReceiverPlaceholder, builderTemp); bool usesBoolReturns = data.UsesBoolReturns; - var resultExpressions = ArrayBuilder.GetInstance(node.Parts.Length + 1); + var resultExpressions = ArrayBuilder.GetInstance(parts.Length + 1); - foreach (var part in node.Parts) + foreach (var part in parts) { if (part is BoundCall call) { @@ -101,7 +99,7 @@ private BoundExpression RewriteInterpolatedStringConversion(BoundConversion conv 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); + Debug.Assert(!parts.IsEmpty); // Start the sequence with appendProceedLocal, if appropriate BoundExpression? currentExpression = appendShouldProceedLocal; @@ -234,18 +232,9 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) BoundExpression? result; - if (node.InterpolationData is not null) + if (node.InterpolationData is InterpolatedStringHandlerData data) { - // 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); + return LowerPartsToString(data, node.Parts, node.Syntax, node.Type); } else if (CanLowerToStringConcatenation(node)) { @@ -324,6 +313,20 @@ public override BoundNode VisitInterpolatedString(BoundInterpolatedString node) return result; } + private BoundExpression LowerPartsToString(InterpolatedStringHandlerData data, ImmutableArray parts, SyntaxNode syntax, TypeSymbol type) + { + // If we can lower to the builder pattern, do so. + (ArrayBuilder handlerPatternExpressions, BoundLocal handlerTemp) = RewriteToInterpolatedStringHandlerPattern(data, parts, syntax); + + // resultTemp = builderTemp.ToStringAndClear(); + var toStringAndClear = (MethodSymbol)Binder.GetWellKnownTypeMember(_compilation, WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear, _diagnostics, syntax: syntax); + BoundExpression toStringAndClearCall = toStringAndClear is not null + ? BoundCall.Synthesized(syntax, handlerTemp, toStringAndClear) + : new BoundBadExpression(syntax, LookupResultKind.Empty, symbols: ImmutableArray.Empty, childBoundNodes: ImmutableArray.Empty, type); + + return _factory.Sequence(ImmutableArray.Create(handlerTemp.LocalSymbol), handlerPatternExpressions.ToImmutableAndFree(), toStringAndClearCall); + } + [Conditional("DEBUG")] private static void AssertNoImplicitInterpolatedStringHandlerConversions(ImmutableArray arguments, bool allowConversionsWithNoContext = false) { @@ -331,9 +334,15 @@ private static void AssertNoImplicitInterpolatedStringHandlerConversions(Immutab { foreach (var arg in arguments) { - if (arg is BoundConversion { Conversion: { Kind: ConversionKind.InterpolatedStringHandler }, ExplicitCastInCode: false, Operand: BoundInterpolatedString @string }) + if (arg is BoundConversion { Conversion: { Kind: ConversionKind.InterpolatedStringHandler }, ExplicitCastInCode: false, Operand: var operand }) { - Debug.Assert(((BoundObjectCreationExpression)@string.InterpolationData!.Value.Construction).Arguments.All( + var data = operand switch + { + BoundInterpolatedString { InterpolationData: { } d } => d, + BoundBinaryOperator { InterpolatedStringHandlerData: { } d } => d, + _ => throw ExceptionUtilities.UnexpectedValue(operand.Kind) + }; + Debug.Assert(((BoundObjectCreationExpression)data.Construction).Arguments.All( a => a is BoundInterpolatedStringArgumentPlaceholder { ArgumentIndex: BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter } or not BoundInterpolatedStringArgumentPlaceholder)); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs index 9ae4b742898cf..fb7ee856914d8 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LoweredDynamicOperationFactory.cs @@ -764,31 +764,33 @@ internal FieldSymbol DefineCallSiteStorageSymbol(NamedTypeSymbol containerDefini } } - BitVector byRefs; + RefKindVector byRefs; if (hasByRefs) { - byRefs = BitVector.Create(1 + (loweredReceiver != null ? 1 : 0) + loweredArguments.Length + (loweredRight != null ? 1 : 0)); + byRefs = RefKindVector.Create(1 + (loweredReceiver != null ? 1 : 0) + loweredArguments.Length + (loweredRight != null ? 1 : 0) + (returnsVoid ? 0 : 1)); int j = 1; if (loweredReceiver != null) { - byRefs[j++] = receiverRefKind != RefKind.None; + byRefs[j++] = getRefKind(receiverRefKind); } if (!refKinds.IsDefault) { for (int i = 0; i < refKinds.Length; i++, j++) { - if (refKinds[i] != RefKind.None) - { - byRefs[j] = true; - } + byRefs[j] = getRefKind(refKinds[i]); } } + + if (!returnsVoid) + { + byRefs[j++] = RefKind.None; + } } else { - byRefs = default(BitVector); + byRefs = default(RefKindVector); } int parameterCount = delegateSignature.Length - (returnsVoid ? 0 : 1); @@ -796,9 +798,12 @@ internal FieldSymbol DefineCallSiteStorageSymbol(NamedTypeSymbol containerDefini int generation = _factory.CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal; var synthesizedType = _factory.Compilation.AnonymousTypeManager.SynthesizeDelegate(parameterCount, byRefs, returnsVoid, generation); return synthesizedType.Construct(delegateSignature); + + // The distinction between by-ref kinds is ignored for dynamic call-sites. + static RefKind getRefKind(RefKind refKind) => refKind == RefKind.None ? RefKind.None : RefKind.Ref; } - internal BoundExpression GetArgumentInfo( + private BoundExpression GetArgumentInfo( MethodSymbol argumentInfoFactory, BoundExpression boundArgument, string? name, @@ -873,7 +878,7 @@ internal BoundExpression GetArgumentInfo( return _factory.Call(null, argumentInfoFactory, _factory.Literal((int)flags), _factory.Literal(name)); } - internal static ImmutableArray GetCallSiteArguments(BoundExpression callSiteFieldAccess, BoundExpression? receiver, ImmutableArray arguments, BoundExpression? right) + private static ImmutableArray GetCallSiteArguments(BoundExpression callSiteFieldAccess, BoundExpression? receiver, ImmutableArray arguments, BoundExpression? right) { var result = new BoundExpression[1 + (receiver != null ? 1 : 0) + arguments.Length + (right != null ? 1 : 0)]; int j = 0; @@ -896,7 +901,7 @@ internal static ImmutableArray GetCallSiteArguments(BoundExpres return result.AsImmutableOrNull(); } - internal TypeSymbol[] MakeCallSiteDelegateSignature(TypeSymbol callSiteType, BoundExpression? receiver, ImmutableArray arguments, BoundExpression? right, TypeSymbol resultType) + private TypeSymbol[] MakeCallSiteDelegateSignature(TypeSymbol callSiteType, BoundExpression? receiver, ImmutableArray arguments, BoundExpression? right, TypeSymbol resultType) { var systemObjectType = _factory.SpecialType(SpecialType.System_Object); var result = new TypeSymbol[1 + (receiver != null ? 1 : 0) + arguments.Length + (right != null ? 1 : 0) + (resultType.IsVoidType() ? 0 : 1)]; diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 503967e4c493e..f605be682486e 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -959,7 +959,7 @@ private IOperation CreateBoundConversionOperation(BoundConversion boundConversio { // https://github.com/dotnet/roslyn/issues/54505 Support interpolation handlers in conversions Debug.Assert(!forceOperandImplicitLiteral); - Debug.Assert(boundOperand is BoundInterpolatedString); + Debug.Assert(boundOperand is BoundInterpolatedString { InterpolationData: not null } or BoundBinaryOperator { InterpolatedStringHandlerData: not null }); var interpolatedString = Create(boundOperand); return new NoneOperation(ImmutableArray.Create(interpolatedString), _semanticModel, boundConversion.Syntax, boundConversion.GetPublicTypeSymbol(), boundConversion.ConstantValue, isImplicit); } @@ -1301,20 +1301,24 @@ private IOperation CreateBoundBinaryOperatorBase(BoundBinaryOperatorBase boundBi // To solve this, we use a manual stack for the left side. var stack = ArrayBuilder.GetInstance(); BoundBinaryOperatorBase? currentBinary = boundBinaryOperatorBase; + InterpolatedStringHandlerData? interpolatedData = (boundBinaryOperatorBase as BoundBinaryOperator)?.InterpolatedStringHandlerData; do { stack.Push(currentBinary); currentBinary = currentBinary.Left as BoundBinaryOperatorBase; - } while (currentBinary is not null); + } while (currentBinary is not null and not BoundBinaryOperator { InterpolatedStringHandlerData: not null }); + + Debug.Assert(interpolatedData == null || interpolatedData.GetValueOrDefault().PositionInfo.Length == stack.Count + 1); Debug.Assert(stack.Count > 0); IOperation? left = null; + int positionInfoIndex = 0; while (stack.TryPop(out currentBinary)) { - left = left ?? Create(currentBinary.Left); - IOperation right = Create(currentBinary.Right); + left ??= visitOperand(currentBinary.Left); + IOperation right = visitOperand(currentBinary.Right); left = currentBinary switch { BoundBinaryOperator binaryOp => createBoundBinaryOperatorOperation(binaryOp, left, right), @@ -1327,6 +1331,11 @@ private IOperation CreateBoundBinaryOperatorBase(BoundBinaryOperatorBase boundBi stack.Free(); return left; + IOperation visitOperand(BoundExpression operand) + => interpolatedData is { } data + ? CreateBoundInterpolatedStringExpressionOperation((BoundInterpolatedString)operand, data.PositionInfo[positionInfoIndex++]) + : Create(operand); + IBinaryOperation createBoundBinaryOperatorOperation(BoundBinaryOperator boundBinaryOperator, IOperation left, IOperation right) { BinaryOperatorKind operatorKind = Helper.DeriveBinaryOperatorKind(boundBinaryOperator.OperatorKind); @@ -1986,9 +1995,10 @@ internal IOperation CreateBoundTupleOperation(BoundTupleExpression boundTupleExp return new TupleOperation(elements, naturalType.GetPublicSymbol(), _semanticModel, syntax, type, isImplicit); } - private IInterpolatedStringOperation CreateBoundInterpolatedStringExpressionOperation(BoundInterpolatedString boundInterpolatedString) + private IInterpolatedStringOperation CreateBoundInterpolatedStringExpressionOperation(BoundInterpolatedString boundInterpolatedString, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>? positionInfo = null) { - ImmutableArray parts = CreateBoundInterpolatedStringContentOperation(boundInterpolatedString.Parts, boundInterpolatedString.InterpolationData); + Debug.Assert(positionInfo == null || boundInterpolatedString.InterpolationData == null); + ImmutableArray parts = CreateBoundInterpolatedStringContentOperation(boundInterpolatedString.Parts, positionInfo ?? boundInterpolatedString.InterpolationData?.PositionInfo[0]); SyntaxNode syntax = boundInterpolatedString.Syntax; ITypeSymbol? type = boundInterpolatedString.GetPublicTypeSymbol(); ConstantValue? constantValue = boundInterpolatedString.ConstantValue; @@ -1996,9 +2006,9 @@ private IInterpolatedStringOperation CreateBoundInterpolatedStringExpressionOper return new InterpolatedStringOperation(parts, _semanticModel, syntax, type, constantValue, isImplicit); } - internal ImmutableArray CreateBoundInterpolatedStringContentOperation(ImmutableArray parts, InterpolatedStringHandlerData? data) + internal ImmutableArray CreateBoundInterpolatedStringContentOperation(ImmutableArray parts, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>? positionInfo) { - return data is { PositionInfo: var positionInfo } ? createHandlerInterpolatedStringContent(positionInfo) : createNonHandlerInterpolatedStringContent(); + return positionInfo is { } info ? createHandlerInterpolatedStringContent(info) : createNonHandlerInterpolatedStringContent(); ImmutableArray createNonHandlerInterpolatedStringContent() { diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs index 0b8246a81013e..0f0766e078a5f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/AnonymousTypeManager.Templates.cs @@ -38,12 +38,12 @@ internal sealed partial class AnonymousTypeManager private struct SynthesizedDelegateKey : IEquatable { - private readonly BitVector _byRefs; + private readonly RefKindVector _byRefs; private readonly ushort _parameterCount; private readonly bool _returnsVoid; private readonly int _generation; - public SynthesizedDelegateKey(int parameterCount, BitVector byRefs, bool returnsVoid, int generation) + public SynthesizedDelegateKey(int parameterCount, RefKindVector byRefs, bool returnsVoid, int generation) { _parameterCount = (ushort)parameterCount; _returnsVoid = returnsVoid; @@ -163,12 +163,12 @@ private ConcurrentDictionary S } } - internal SynthesizedDelegateSymbol SynthesizeDelegate(int parameterCount, BitVector byRefParameters, bool returnsVoid, int generation) + internal SynthesizedDelegateSymbol SynthesizeDelegate(int parameterCount, RefKindVector refKinds, bool returnsVoid, int generation) { // parameterCount doesn't include return type - Debug.Assert(byRefParameters.IsNull || parameterCount == byRefParameters.Capacity); + Debug.Assert(refKinds.IsNull || parameterCount == refKinds.Capacity - (returnsVoid ? 0 : 1)); - var key = new SynthesizedDelegateKey(parameterCount, byRefParameters, returnsVoid, generation); + var key = new SynthesizedDelegateKey(parameterCount, refKinds, returnsVoid, generation); SynthesizedDelegateValue result; if (this.SynthesizedDelegates.TryGetValue(key, out result)) @@ -177,17 +177,15 @@ internal SynthesizedDelegateSymbol SynthesizeDelegate(int parameterCount, BitVec } // NOTE: the newly created template may be thrown away if another thread wins - return this.SynthesizedDelegates.GetOrAdd(key, - new SynthesizedDelegateValue( - this, - new SynthesizedDelegateSymbol( - this.Compilation.Assembly.GlobalNamespace, - key.MakeTypeName(), - this.System_Object, - Compilation.GetSpecialType(SpecialType.System_IntPtr), - returnsVoid ? Compilation.GetSpecialType(SpecialType.System_Void) : null, - parameterCount, - byRefParameters))).Delegate; + var synthesizedDelegate = new SynthesizedDelegateSymbol( + this.Compilation.Assembly.GlobalNamespace, + key.MakeTypeName(), + this.System_Object, + Compilation.GetSpecialType(SpecialType.System_IntPtr), + returnsVoid ? Compilation.GetSpecialType(SpecialType.System_Void) : null, + parameterCount, + refKinds); + return this.SynthesizedDelegates.GetOrAdd(key, new SynthesizedDelegateValue(this, synthesizedDelegate)).Delegate; } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index 7f4e73a01a9db..833a432abb080 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -498,7 +498,7 @@ internal static string MakeDynamicCallSiteFieldName(int uniqueId) /// Produces name of the synthesized delegate symbol that encodes the parameter byref-ness and return type of the delegate. /// The arity is appended via `N suffix in MetadataName calculation since the delegate is generic. /// - internal static string MakeDynamicCallSiteDelegateName(BitVector byRefs, bool returnsVoid, int generation) + internal static string MakeDynamicCallSiteDelegateName(RefKindVector byRefs, bool returnsVoid, int generation) { var pooledBuilder = PooledStringBuilder.GetInstance(); var builder = pooledBuilder.Builder; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/RefKindVector.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/RefKindVector.cs new file mode 100644 index 0000000000000..a911d0b2dccf6 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/RefKindVector.cs @@ -0,0 +1,73 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal struct RefKindVector : IEquatable + { + private BitVector _bits; + + internal static RefKindVector Create(int capacity) + { + return new RefKindVector(capacity); + } + + private RefKindVector(int capacity) + { + _bits = BitVector.Create(capacity * 2); + } + + internal bool IsNull => _bits.IsNull; + + internal int Capacity => _bits.Capacity / 2; + + internal IEnumerable Words() => _bits.Words(); + + internal RefKind this[int index] + { + get + { + index *= 2; + return (_bits[index + 1], _bits[index]) switch + { + (false, false) => RefKind.None, + (false, true) => RefKind.Ref, + (true, false) => RefKind.Out, + (true, true) => RefKind.RefReadOnly, + }; + } + set + { + index *= 2; + (_bits[index + 1], _bits[index]) = value switch + { + RefKind.None => (false, false), + RefKind.Ref => (false, true), + RefKind.Out => (true, false), + RefKind.RefReadOnly => (true, true), + _ => throw ExceptionUtilities.UnexpectedValue(value) + }; + } + } + + public bool Equals(RefKindVector other) + { + return _bits.Equals(other._bits); + } + + public override bool Equals(object? obj) + { + return obj is RefKindVector other && this.Equals(other); + } + + public override int GetHashCode() + { + return _bits.GetHashCode(); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs index c91d8f3f3136c..9637c92593b2d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; +using System.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -28,14 +26,16 @@ public SynthesizedDelegateSymbol( string name, TypeSymbol objectType, TypeSymbol intPtrType, - TypeSymbol voidReturnTypeOpt, + TypeSymbol? voidReturnTypeOpt, int parameterCount, - BitVector byRefParameters) - : base(name, parameterCount, returnsVoid: (object)voidReturnTypeOpt != null) + RefKindVector refKinds) + : base(name, parameterCount, returnsVoid: voidReturnTypeOpt is not null) { + Debug.Assert(refKinds.IsNull || parameterCount == refKinds.Capacity - (voidReturnTypeOpt is { } ? 0 : 1)); + _containingSymbol = containingSymbol; _constructor = new DelegateConstructor(this, objectType, intPtrType); - _invoke = new InvokeMethod(this, byRefParameters, voidReturnTypeOpt); + _invoke = new InvokeMethod(this, refKinds, voidReturnTypeOpt); } public override Symbol ContainingSymbol @@ -116,27 +116,27 @@ private sealed class InvokeMethod : SynthesizedInstanceMethodSymbol private readonly ImmutableArray _parameters; private readonly TypeSymbol _containingType; private readonly TypeSymbol _returnType; + private readonly RefKind _returnRefKind; - internal InvokeMethod(SynthesizedDelegateSymbol containingType, BitVector byRefParameters, TypeSymbol voidReturnTypeOpt) + internal InvokeMethod(SynthesizedDelegateSymbol containingType, RefKindVector refKinds, TypeSymbol? voidReturnTypeOpt) { var typeParams = containingType.TypeParameters; _containingType = containingType; - // if we are given Void type the method returns Void, otherwise its return type is the last type parameter of the delegate: - _returnType = voidReturnTypeOpt ?? typeParams.Last(); - - int parameterCount = typeParams.Length - ((object)voidReturnTypeOpt != null ? 0 : 1); + int parameterCount = typeParams.Length - (voidReturnTypeOpt is null ? 1 : 0); var parameters = ArrayBuilder.GetInstance(parameterCount); for (int i = 0; i < parameterCount; i++) { - // we don't need to distinguish between out and ref since this is an internal synthesized symbol: - var refKind = !byRefParameters.IsNull && byRefParameters[i] ? RefKind.Ref : RefKind.None; - - parameters.Add(SynthesizedParameterSymbol.Create(this, TypeWithAnnotations.Create(typeParams[i]), i, refKind)); + var parameterRefKind = refKinds.IsNull ? RefKind.None : refKinds[i]; + parameters.Add(SynthesizedParameterSymbol.Create(this, TypeWithAnnotations.Create(typeParams[i]), i, parameterRefKind)); } _parameters = parameters.ToImmutableAndFree(); + + // if we are given Void type the method returns Void, otherwise its return type is the last type parameter of the delegate: + _returnType = voidReturnTypeOpt ?? typeParams[parameterCount]; + _returnRefKind = (refKinds.IsNull || voidReturnTypeOpt is { }) ? RefKind.None : refKinds[parameterCount]; } public override string Name @@ -192,7 +192,7 @@ internal override bool HasDeclarativeSecurity get { return false; } } - public override DllImportData GetDllImportData() + public override DllImportData? GetDllImportData() { return null; } @@ -202,7 +202,7 @@ public override DllImportData GetDllImportData() throw ExceptionUtilities.Unreachable; } - internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation + internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation { get { return null; } } @@ -234,7 +234,7 @@ public override bool IsAsync public override RefKind RefKind { - get { return RefKind.None; } + get { return _returnRefKind; } } public override TypeWithAnnotations ReturnTypeWithAnnotations @@ -271,7 +271,7 @@ public override ImmutableArray RefCustomModifiers get { return ImmutableArray.Empty; } } - public override Symbol AssociatedSymbol + public override Symbol? AssociatedSymbol { get { return null; } } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs index 94a249ba9bcd1..ddc6a79b2f120 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenDynamicTests.cs @@ -820,7 +820,7 @@ public dynamic M(dynamic d) }"; var verifier = CompileAndVerifyWithCSharp(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: peModule => { - var d = peModule.GlobalNamespace.GetMember("<>F{00000004}"); + var d = peModule.GlobalNamespace.GetMember("<>F{00000010}"); // the type: Assert.Equal(Accessibility.Internal, d.DeclaredAccessibility); @@ -917,7 +917,7 @@ .maxstack 7 .locals init (E V_0) //dict IL_0000: ldnull IL_0001: stloc.0 - IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>> C.<>o__0.<>p__0"" + IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>> C.<>o__0.<>p__0"" IL_0007: brtrue.s IL_003e IL_0009: ldc.i4.0 IL_000a: ldtoken ""C"" @@ -937,14 +937,14 @@ .locals init (E V_0) //dict IL_0029: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_002e: stelem.ref IL_002f: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Invoke(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0034: call ""System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>> System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0039: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>> C.<>o__0.<>p__0"" - IL_003e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>> C.<>o__0.<>p__0"" - IL_0043: ldfld ""<>F{00000004}, object> System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>>.Target"" - IL_0048: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}, object>> C.<>o__0.<>p__0"" + IL_0034: call ""System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>> System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0039: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>> C.<>o__0.<>p__0"" + IL_003e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>> C.<>o__0.<>p__0"" + IL_0043: ldfld ""<>F{00000010}, object> System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>>.Target"" + IL_0048: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}, object>> C.<>o__0.<>p__0"" IL_004d: ldarg.1 IL_004e: ldloca.s V_0 - IL_0050: callvirt ""object <>F{00000004}, object>.Invoke(System.Runtime.CompilerServices.CallSite, object, ref E)"" + IL_0050: callvirt ""object <>F{00000010}, object>.Invoke(System.Runtime.CompilerServices.CallSite, object, ref E)"" IL_0055: ret } "); @@ -8693,7 +8693,7 @@ public dynamic M(dynamic d) { // Code size 103 (0x67) .maxstack 9 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__0.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__0.<>p__0"" IL_0005: brtrue.s IL_004d IL_0007: ldc.i4.0 IL_0008: ldstr ""m"" @@ -8721,15 +8721,15 @@ .maxstack 9 IL_0038: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_003d: stelem.ref IL_003e: 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_0043: call ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> System.Runtime.CompilerServices.CallSite<<>F{0000000c}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0048: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__0.<>p__0"" - IL_004d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__0.<>p__0"" - IL_0052: ldfld ""<>F{0000000c} System.Runtime.CompilerServices.CallSite<<>F{0000000c}>.Target"" - IL_0057: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__0.<>p__0"" + IL_0043: call ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> System.Runtime.CompilerServices.CallSite<<>F{00000050}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0048: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__0.<>p__0"" + IL_004d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__0.<>p__0"" + IL_0052: ldfld ""<>F{00000050} System.Runtime.CompilerServices.CallSite<<>F{00000050}>.Target"" + IL_0057: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__0.<>p__0"" IL_005c: ldarg.1 IL_005d: ldarga.s V_1 IL_005f: ldarga.s V_1 - IL_0061: callvirt ""object <>F{0000000c}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object, ref object)"" + IL_0061: callvirt ""object <>F{00000050}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object, ref object)"" IL_0066: ret } "); @@ -8818,7 +8818,7 @@ .locals init (object V_0, //lo object V_1) //ld IL_0000: ldnull IL_0001: stloc.0 - IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000030}> C.<>o__2.<>p__0"" + IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000500}> C.<>o__2.<>p__0"" IL_0007: brtrue.s IL_0067 IL_0009: ldc.i4 0x102 IL_000e: ldstr ""f"" @@ -8858,11 +8858,11 @@ .locals init (object V_0, //lo IL_0052: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0057: stelem.ref IL_0058: 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_005d: call ""System.Runtime.CompilerServices.CallSite<<>A{00000030}> System.Runtime.CompilerServices.CallSite<<>A{00000030}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0062: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000030}> C.<>o__2.<>p__0"" - IL_0067: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000030}> C.<>o__2.<>p__0"" - IL_006c: ldfld ""<>A{00000030} System.Runtime.CompilerServices.CallSite<<>A{00000030}>.Target"" - IL_0071: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000030}> C.<>o__2.<>p__0"" + IL_005d: call ""System.Runtime.CompilerServices.CallSite<<>A{00000500}> System.Runtime.CompilerServices.CallSite<<>A{00000500}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0062: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000500}> C.<>o__2.<>p__0"" + IL_0067: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000500}> C.<>o__2.<>p__0"" + IL_006c: ldfld ""<>A{00000500} System.Runtime.CompilerServices.CallSite<<>A{00000500}>.Target"" + IL_0071: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000500}> C.<>o__2.<>p__0"" IL_0076: ldarg.0 IL_0077: ldarg.0 IL_0078: ldfld ""dynamic C.d"" @@ -8870,7 +8870,7 @@ .locals init (object V_0, //lo IL_007e: ldfld ""dynamic C.d"" IL_0083: ldloca.s V_0 IL_0085: ldloca.s V_1 - IL_0087: callvirt ""void <>A{00000030}.Invoke(System.Runtime.CompilerServices.CallSite, C, object, object, ref object, ref object)"" + IL_0087: callvirt ""void <>A{00000500}.Invoke(System.Runtime.CompilerServices.CallSite, C, object, object, ref object, ref object)"" IL_008c: ret } "); @@ -9070,7 +9070,7 @@ .maxstack 9 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" - IL_0008: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" + IL_0008: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" IL_000d: brtrue.s IL_004e IL_000f: ldc.i4 0x100 IL_0014: ldstr ""goo"" @@ -9092,14 +9092,14 @@ .locals init (S V_0) //s IL_0039: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_003e: stelem.ref IL_003f: 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_0044: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0049: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" - IL_004e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" - IL_0053: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_0058: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" + IL_0044: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0049: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" + IL_004e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" + IL_0053: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_0058: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" IL_005d: ldloca.s V_0 IL_005f: ldarg.1 - IL_0060: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0060: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_0065: ret }"); } @@ -9126,7 +9126,7 @@ public void goo(int a) {} { // Code size 94 (0x5e) .maxstack 9 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" IL_0005: brtrue.s IL_0046 IL_0007: ldc.i4 0x100 IL_000c: ldstr ""goo"" @@ -9148,14 +9148,14 @@ .maxstack 9 IL_0031: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0036: stelem.ref IL_0037: 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_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" - IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" - IL_004b: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" + IL_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" + IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" + IL_004b: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" IL_0055: ldarga.s V_1 IL_0057: ldarg.2 - IL_0058: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0058: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_005d: ret } "); @@ -9179,7 +9179,7 @@ public void M(dynamic d) { // Code size 93 (0x5d) .maxstack 9 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> S.<>o__1.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> S.<>o__1.<>p__0"" IL_0005: brtrue.s IL_0046 IL_0007: ldc.i4 0x100 IL_000c: ldstr ""Equals"" @@ -9201,14 +9201,14 @@ .maxstack 9 IL_0031: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0036: stelem.ref IL_0037: 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_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> S.<>o__1.<>p__0"" - IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> S.<>o__1.<>p__0"" - IL_004b: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> S.<>o__1.<>p__0"" + IL_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> S.<>o__1.<>p__0"" + IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> S.<>o__1.<>p__0"" + IL_004b: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> S.<>o__1.<>p__0"" IL_0055: ldarg.0 IL_0056: ldarg.1 - IL_0057: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0057: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_005c: ret } "); @@ -9298,7 +9298,7 @@ public void goo(int a) {} { // Code size 104 (0x68) .maxstack 9 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0005: brtrue.s IL_0046 IL_0007: ldc.i4 0x100 IL_000c: ldstr ""goo"" @@ -9320,17 +9320,17 @@ .maxstack 9 IL_0031: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0036: stelem.ref IL_0037: 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_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_004b: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_003c: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0041: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_004b: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0055: ldarg.0 IL_0056: ldfld ""S[] C.s"" IL_005b: ldc.i4.0 IL_005c: ldelema ""S"" IL_0061: ldarg.1 - IL_0062: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0062: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_0067: ret } "); @@ -9370,7 +9370,7 @@ .locals init (S V_0, //s IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: stloc.1 - IL_000c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_000c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0011: brtrue.s IL_0052 IL_0013: ldc.i4 0x100 IL_0018: ldstr ""goo"" @@ -9392,14 +9392,14 @@ .locals init (S V_0, //s IL_003d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0042: stelem.ref IL_0043: 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_0048: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0052: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0057: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_0048: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0052: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0057: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0061: ldloc.1 IL_0062: ldarg.1 - IL_0063: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0063: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_0068: ret } ", allowUnsafe: true, verify: Verification.Fails); @@ -9439,7 +9439,7 @@ .locals init (S V_0, //s IL_0008: ldloca.s V_0 IL_000a: conv.u IL_000b: stloc.1 - IL_000c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_000c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0011: brtrue.s IL_0052 IL_0013: ldc.i4 0x100 IL_0018: ldstr ""goo"" @@ -9461,14 +9461,14 @@ .locals init (S V_0, //s IL_003d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0042: stelem.ref IL_0043: 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_0048: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0052: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0057: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_0048: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0052: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0057: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0061: ldloc.1 IL_0062: ldarg.1 - IL_0063: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_0063: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_0068: ret } ", allowUnsafe: true, verify: Verification.Fails); @@ -9507,7 +9507,7 @@ .locals init (S* V_0) //ptr IL_0008: mul.ovf.un IL_0009: localloc IL_000b: stloc.0 - IL_000c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_000c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0011: brtrue.s IL_0052 IL_0013: ldc.i4 0x100 IL_0018: ldstr ""goo"" @@ -9529,16 +9529,16 @@ .locals init (S* V_0) //ptr IL_003d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0042: stelem.ref IL_0043: 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_0048: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0052: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" - IL_0057: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__1.<>p__0"" + IL_0048: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0052: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" + IL_0057: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_005c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__1.<>p__0"" IL_0061: ldloc.0 IL_0062: sizeof ""S"" IL_0068: add IL_0069: ldarg.1 - IL_006a: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" + IL_006a: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object)"" IL_006f: ret } ", allowUnsafe: true, verify: Verification.Fails); @@ -9570,7 +9570,7 @@ .locals init (int V_0, //a IL_0002: ldloca.s V_0 IL_0004: mkrefany ""int"" IL_0009: stloc.1 - IL_000a: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" + IL_000a: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" IL_000f: brtrue.s IL_0050 IL_0011: ldc.i4 0x100 IL_0016: ldstr ""Equals"" @@ -9592,15 +9592,15 @@ .locals init (int V_0, //a IL_003b: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0040: stelem.ref IL_0041: 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_0046: call ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004b: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" - IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" - IL_0055: ldfld ""<>A{00000002} System.Runtime.CompilerServices.CallSite<<>A{00000002}>.Target"" - IL_005a: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000002}> C.<>o__0.<>p__0"" + IL_0046: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004b: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" + IL_0050: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" + IL_0055: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_005a: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> C.<>o__0.<>p__0"" IL_005f: ldloc.1 IL_0060: refanyval ""int"" IL_0065: ldarg.1 - IL_0066: callvirt ""void <>A{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref int, object)"" + IL_0066: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref int, object)"" IL_006b: ret } "); @@ -11020,7 +11020,7 @@ public dynamic M(dynamic d) { // Code size 84 (0x54) .maxstack 7 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> C.<>o__0.<>p__0"" IL_0005: brtrue.s IL_003c IL_0007: ldc.i4.0 IL_0008: ldtoken ""C"" @@ -11040,14 +11040,14 @@ .maxstack 7 IL_0027: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_002c: stelem.ref IL_002d: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0032: call ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0037: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" - IL_003c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" - IL_0041: ldfld ""<>F{00000004} System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Target"" - IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" + IL_0032: call ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> System.Runtime.CompilerServices.CallSite<<>F{00000010}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0037: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> C.<>o__0.<>p__0"" + IL_003c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> C.<>o__0.<>p__0"" + IL_0041: ldfld ""<>F{00000010} System.Runtime.CompilerServices.CallSite<<>F{00000010}>.Target"" + IL_0046: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> C.<>o__0.<>p__0"" IL_004b: ldarg.1 IL_004c: ldarga.s V_1 - IL_004e: callvirt ""object <>F{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object)"" + IL_004e: callvirt ""object <>F{00000010}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object)"" IL_0053: ret } "); @@ -11068,7 +11068,7 @@ public dynamic M(dynamic d) { // Code size 133 (0x85) .maxstack 7 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" IL_0005: brtrue.s IL_006a IL_0007: ldc.i4.0 IL_0008: ldtoken ""C"" @@ -11106,17 +11106,17 @@ .maxstack 7 IL_0055: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_005a: stelem.ref IL_005b: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0060: call ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> System.Runtime.CompilerServices.CallSite<<>F{00000020}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0065: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" - IL_006a: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" - IL_006f: ldfld ""<>F{00000020} System.Runtime.CompilerServices.CallSite<<>F{00000020}>.Target"" - IL_0074: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" + IL_0060: call ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> System.Runtime.CompilerServices.CallSite<<>F{00000400}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0065: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" + IL_006a: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" + IL_006f: ldfld ""<>F{00000400} System.Runtime.CompilerServices.CallSite<<>F{00000400}>.Target"" + IL_0074: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" IL_0079: ldarg.1 IL_007a: ldc.i4.1 IL_007b: ldarg.1 IL_007c: ldnull IL_007d: ldarga.s V_1 - IL_007f: callvirt ""object <>F{00000020}.Invoke(System.Runtime.CompilerServices.CallSite, object, int, object, object, ref object)"" + IL_007f: callvirt ""object <>F{00000400}.Invoke(System.Runtime.CompilerServices.CallSite, object, int, object, object, ref object)"" IL_0084: ret } "); @@ -11798,7 +11798,7 @@ public dynamic M(dynamic d) { // Code size 144 (0x90) .maxstack 8 - IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" + IL_0000: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" IL_0005: brtrue.s IL_0074 IL_0007: ldc.i4.0 IL_0008: ldtoken ""C"" @@ -11842,18 +11842,18 @@ .maxstack 8 IL_005f: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0064: stelem.ref IL_0065: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.SetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_006a: call ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> System.Runtime.CompilerServices.CallSite<<>F{00000020}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_006f: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" - IL_0074: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" - IL_0079: ldfld ""<>F{00000020} System.Runtime.CompilerServices.CallSite<<>F{00000020}>.Target"" - IL_007e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000020}> C.<>o__0.<>p__0"" + IL_006a: call ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> System.Runtime.CompilerServices.CallSite<<>F{00000400}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_006f: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" + IL_0074: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" + IL_0079: ldfld ""<>F{00000400} System.Runtime.CompilerServices.CallSite<<>F{00000400}>.Target"" + IL_007e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000400}> C.<>o__0.<>p__0"" IL_0083: ldarg.1 IL_0084: ldarg.1 IL_0085: ldc.i4.0 IL_0086: ldnull IL_0087: ldarga.s V_1 IL_0089: ldnull - IL_008a: callvirt ""object <>F{00000020}.Invoke(System.Runtime.CompilerServices.CallSite, object, object, int, object, ref object, object)"" + IL_008a: callvirt ""object <>F{00000400}.Invoke(System.Runtime.CompilerServices.CallSite, object, object, int, object, ref object, object)"" IL_008f: ret } "); @@ -11886,7 +11886,7 @@ .maxstack 7 .locals init (S V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" - IL_0008: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> C.<>o__0.<>p__0"" + IL_0008: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" IL_000d: brtrue.s IL_004e IL_000f: ldc.i4.0 IL_0010: ldtoken ""C"" @@ -11912,15 +11912,15 @@ .locals init (S V_0) //s IL_0039: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_003e: stelem.ref IL_003f: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.SetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0044: call ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> System.Runtime.CompilerServices.CallSite<<>F{00000002}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0049: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> C.<>o__0.<>p__0"" - IL_004e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> C.<>o__0.<>p__0"" - IL_0053: ldfld ""<>F{00000002} System.Runtime.CompilerServices.CallSite<<>F{00000002}>.Target"" - IL_0058: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000002}> C.<>o__0.<>p__0"" + IL_0044: call ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0049: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" + IL_004e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" + IL_0053: ldfld ""<>F{00000004} System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Target"" + IL_0058: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> C.<>o__0.<>p__0"" IL_005d: ldloca.s V_0 IL_005f: ldarg.1 IL_0060: ldc.i4.1 - IL_0061: callvirt ""object <>F{00000002}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object, int)"" + IL_0061: callvirt ""object <>F{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref S, object, int)"" IL_0066: pop IL_0067: ret }"); @@ -13291,7 +13291,7 @@ .locals init (object V_0, IL_0018: ldfld ""C C.c"" IL_001d: ldfld ""C C.c"" IL_0022: stloc.3 - IL_0023: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__2"" + IL_0023: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__2"" IL_0028: brtrue.s IL_0082 IL_002a: ldc.i4 0x80 IL_002f: ldtoken ""C"" @@ -13329,11 +13329,11 @@ .locals init (object V_0, IL_006d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0072: stelem.ref IL_0073: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.SetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0078: call ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> System.Runtime.CompilerServices.CallSite<<>F{0000000c}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_007d: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__2"" - IL_0082: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__2"" - IL_0087: ldfld ""<>F{0000000c} System.Runtime.CompilerServices.CallSite<<>F{0000000c}>.Target"" - IL_008c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__2"" + IL_0078: call ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> System.Runtime.CompilerServices.CallSite<<>F{00000050}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_007d: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__2"" + IL_0082: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__2"" + IL_0087: ldfld ""<>F{00000050} System.Runtime.CompilerServices.CallSite<<>F{00000050}>.Target"" + IL_008c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__2"" IL_0091: ldloc.0 IL_0092: ldloc.1 IL_0093: ldloc.2 @@ -13364,7 +13364,7 @@ .locals init (object V_0, IL_00d2: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__3.<>p__1"" IL_00d7: ldfld ""System.Func System.Runtime.CompilerServices.CallSite>.Target"" IL_00dc: ldsfld ""System.Runtime.CompilerServices.CallSite> C.<>o__3.<>p__1"" - IL_00e1: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__0"" + IL_00e1: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__0"" IL_00e6: brtrue.s IL_0132 IL_00e8: ldc.i4.0 IL_00e9: ldtoken ""C"" @@ -13396,19 +13396,19 @@ .locals init (object V_0, IL_011d: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0122: stelem.ref IL_0123: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0128: call ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> System.Runtime.CompilerServices.CallSite<<>F{0000000c}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_012d: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__0"" - IL_0132: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__0"" - IL_0137: ldfld ""<>F{0000000c} System.Runtime.CompilerServices.CallSite<<>F{0000000c}>.Target"" - IL_013c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{0000000c}> C.<>o__3.<>p__0"" + IL_0128: call ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> System.Runtime.CompilerServices.CallSite<<>F{00000050}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_012d: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__0"" + IL_0132: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__0"" + IL_0137: ldfld ""<>F{00000050} System.Runtime.CompilerServices.CallSite<<>F{00000050}>.Target"" + IL_013c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000050}> C.<>o__3.<>p__0"" IL_0141: ldloc.0 IL_0142: ldloc.1 IL_0143: ldloc.2 IL_0144: ldloc.3 - IL_0145: callvirt ""object <>F{0000000c}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object, ref int, C)"" + IL_0145: callvirt ""object <>F{00000050}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object, ref int, C)"" IL_014a: ldarg.2 IL_014b: callvirt ""object System.Func.Invoke(System.Runtime.CompilerServices.CallSite, object, object)"" - IL_0150: callvirt ""object <>F{0000000c}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object, ref int, C, object)"" + IL_0150: callvirt ""object <>F{00000050}.Invoke(System.Runtime.CompilerServices.CallSite, object, ref object, ref int, C, object)"" IL_0155: ret }"); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleEqualityTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleEqualityTests.cs index 89ee7957084a1..ff2fa54d329e5 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleEqualityTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleEqualityTests.cs @@ -1358,12 +1358,9 @@ static void Main() // (6,30): error CS0034: Operator '==' is ambiguous on operands of type '' and 'default' // System.Console.Write((null, () => 1) == (default, default)); Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "(null, () => 1) == (default, default)").WithArguments("==", "", "default").WithLocation(6, 30), - // (6,37): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (6,30): error CS0019: Operator '==' cannot be applied to operands of type 'lambda expression' and 'default' // System.Console.Write((null, () => 1) == (default, default)); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => 1").WithArguments("inferred delegate type", "10.0").WithLocation(6, 37), - // (6,37): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // System.Console.Write((null, () => 1) == (default, default)); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => 1").WithArguments("inferred delegate type", "10.0").WithLocation(6, 37), + Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, () => 1) == (default, default)").WithArguments("==", "lambda expression", "default").WithLocation(6, 30), // (7,30): error CS0034: Operator '==' is ambiguous on operands of type '(, lambda expression)' and 'default' // System.Console.Write((null, () => 2) == default); Diagnostic(ErrorCode.ERR_AmbigBinaryOps, "(null, () => 2) == default").WithArguments("==", "(, lambda expression)", "default").WithLocation(7, 30) @@ -1672,31 +1669,16 @@ static void Main() }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( - // (6,65): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "x => x").WithArguments("inferred delegate type", "10.0").WithLocation(6, 65), - // (6,65): error CS8917: The delegate type could not be inferred. + // (6,30): error CS0019: Operator '==' cannot be applied to operands of type '' and 'lambda expression' // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => x").WithLocation(6, 65), - // (6,65): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })").WithArguments("==", "", "lambda expression").WithLocation(6, 30), + // (6,30): error CS0019: Operator '==' cannot be applied to operands of type '' and 'method group' // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "x => x").WithArguments("inferred delegate type", "10.0").WithLocation(6, 65), - // (6,65): error CS8917: The delegate type could not be inferred. - // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => x").WithLocation(6, 65), - // (6,73): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })").WithArguments("==", "", "method group").WithLocation(6, 30), + // (6,30): error CS0019: Operator '==' cannot be applied to operands of type '' and 'lambda expression' // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Main").WithArguments("inferred delegate type", "10.0").WithLocation(6, 73), - // (6,73): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Main").WithArguments("inferred delegate type", "10.0").WithLocation(6, 73), - // (6,79): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(int i) => { int j = 0; return i + j; }").WithArguments("inferred delegate type", "10.0").WithLocation(6, 79), - // (6,79): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "(int i) => { int j = 0; return i + j; }").WithArguments("inferred delegate type", "10.0").WithLocation(6, 79)); - verify(comp); + Diagnostic(ErrorCode.ERR_BadBinaryOps, "(null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })").WithArguments("==", "", "lambda expression").WithLocation(6, 30)); + verify(comp, inferDelegate: false); comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( @@ -1706,9 +1688,9 @@ static void Main() // (6,65): error CS8917: The delegate type could not be inferred. // System.Console.Write((null, null, null, null) == (null, x => x, Main, (int i) => { int j = 0; return i + j; })); Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => x").WithLocation(6, 65)); - verify(comp); + verify(comp, inferDelegate: true); - static void verify(CSharpCompilation comp) + static void verify(CSharpCompilation comp, bool inferDelegate) { var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); @@ -1732,19 +1714,19 @@ static void verify(CSharpCompilation comp) // ... its first lambda ... var firstLambda = tuple2.Arguments[1].Expression; Assert.Null(model.GetTypeInfo(firstLambda).Type); - Assert.Equal("System.Delegate", model.GetTypeInfo(firstLambda).ConvertedType.ToTestDisplayString()); + verifyType("System.Delegate", model.GetTypeInfo(firstLambda).ConvertedType, inferDelegate); // ... its method group ... var methodGroup = tuple2.Arguments[2].Expression; Assert.Null(model.GetTypeInfo(methodGroup).Type); - Assert.Equal("System.Delegate", model.GetTypeInfo(methodGroup).ConvertedType.ToTestDisplayString()); + verifyType("System.Delegate", model.GetTypeInfo(methodGroup).ConvertedType, inferDelegate); Assert.Null(model.GetSymbolInfo(methodGroup).Symbol); Assert.Equal(new[] { "void C.Main()" }, model.GetSymbolInfo(methodGroup).CandidateSymbols.Select(s => s.ToTestDisplayString())); // ... its second lambda and the symbols it uses var secondLambda = tuple2.Arguments[3].Expression; - Assert.Equal("System.Func", model.GetTypeInfo(secondLambda).Type.ToTestDisplayString()); - Assert.Equal("System.Delegate", model.GetTypeInfo(secondLambda).ConvertedType.ToTestDisplayString()); + verifyType("System.Func", model.GetTypeInfo(secondLambda).Type, inferDelegate); + verifyType("System.Delegate", model.GetTypeInfo(secondLambda).ConvertedType, inferDelegate); var addition = tree.GetCompilationUnitRoot().DescendantNodes().OfType().Last(); Assert.Equal("i + j", addition.ToString()); @@ -1755,6 +1737,18 @@ static void verify(CSharpCompilation comp) var j = addition.Right; Assert.Equal("System.Int32 j", model.GetSymbolInfo(j).Symbol.ToTestDisplayString()); } + + static void verifyType(string expectedType, ITypeSymbol type, bool inferDelegate) + { + if (inferDelegate) + { + Assert.Equal(expectedType, type.ToTestDisplayString()); + } + else + { + Assert.Null(type); + } + } } [Fact] @@ -2022,7 +2016,20 @@ public void M() if ("""" == 1) {} } }"; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); + comp.VerifyDiagnostics( + // (6,13): error CS0815: Cannot assign (, ) to an implicitly-typed variable + // var t = (null, null); + Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "t = (null, null)").WithArguments("(, )").WithLocation(6, 13), + // (7,13): error CS0019: Operator '==' cannot be applied to operands of type '' and 'lambda expression' + // if (null == (() => {}) ) {} + Diagnostic(ErrorCode.ERR_BadBinaryOps, "null == (() => {})").WithArguments("==", "", "lambda expression").WithLocation(7, 13), + // (8,13): error CS0019: Operator '==' cannot be applied to operands of type 'string' and 'int' + // if ("" == 1) {} + Diagnostic(ErrorCode.ERR_BadBinaryOps, @""""" == 1").WithArguments("==", "string", "int").WithLocation(8, 13) + ); + + comp = CreateCompilation(source); comp.VerifyDiagnostics( // (6,13): error CS0815: Cannot assign (, ) to an implicitly-typed variable // var t = (null, null); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 3aeed8be822f1..9f4dbc375f1f5 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -5101,10 +5101,10 @@ static int M(dynamic d, object o) var expectedNames = new[] { "", - "<>A{00000004}`3", - "<>A{00000018}`5", - "<>F{00000004}`5", - "<>F{00000008}`5", + "<>A{00000010}`3", + "<>A{00000140}`5", + "<>F{00000010}`5", + "<>F{00000040}`5", "C1", "C2", "C3", diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs index a618d8dcb2e43..8f68e383b9e56 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs @@ -3807,7 +3807,7 @@ static void F(dynamic d, out object x, object y) // Code size 112 (0x70) .maxstack 9 IL_0000: nop - IL_0001: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}>> C.<>o__0.<>p__0"" + IL_0001: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}>> C.<>o__0.<>p__0"" IL_0006: brfalse.s IL_000a IL_0008: br.s IL_0053 IL_000a: ldc.i4 0x100 @@ -3836,15 +3836,15 @@ .maxstack 9 IL_003e: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0043: stelem.ref IL_0044: 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_0049: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}>> System.Runtime.CompilerServices.CallSite<<>A{00000004}>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004e: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}>> C.<>o__0.<>p__0"" - IL_0053: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}>> C.<>o__0.<>p__0"" - IL_0058: ldfld ""<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>>.Target"" - IL_005d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}>> C.<>o__0.<>p__0"" + IL_0049: call ""System.Runtime.CompilerServices.CallSite<<>A{00000010}>> System.Runtime.CompilerServices.CallSite<<>A{00000010}>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004e: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}>> C.<>o__0.<>p__0"" + IL_0053: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}>> C.<>o__0.<>p__0"" + IL_0058: ldfld ""<>A{00000010}> System.Runtime.CompilerServices.CallSite<<>A{00000010}>>.Target"" + IL_005d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}>> C.<>o__0.<>p__0"" IL_0062: ldarg.0 IL_0063: ldarg.1 IL_0064: newobj ""<>f__AnonymousType0..ctor()"" - IL_0069: callvirt ""void <>A{00000004}>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref object, )"" + IL_0069: callvirt ""void <>A{00000010}>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref object, )"" IL_006e: nop IL_006f: ret }"); @@ -3858,7 +3858,7 @@ .maxstack 9 // Code size 113 (0x71) .maxstack 9 IL_0000: nop - IL_0001: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>> C.<>o__0#1.<>p__0"" + IL_0001: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>> C.<>o__0#1.<>p__0"" IL_0006: brfalse.s IL_000a IL_0008: br.s IL_0053 IL_000a: ldc.i4 0x100 @@ -3887,16 +3887,16 @@ .maxstack 9 IL_003e: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0043: stelem.ref IL_0044: 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_0049: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>> System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004e: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>> C.<>o__0#1.<>p__0"" - IL_0053: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>> C.<>o__0#1.<>p__0"" - IL_0058: ldfld ""<>A{00000004}#1> System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>>.Target"" - IL_005d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}#1>> C.<>o__0#1.<>p__0"" + IL_0049: call ""System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>> System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004e: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>> C.<>o__0#1.<>p__0"" + IL_0053: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>> C.<>o__0#1.<>p__0"" + IL_0058: ldfld ""<>A{00000010}#1> System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>>.Target"" + IL_005d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000010}#1>> C.<>o__0#1.<>p__0"" IL_0062: ldarg.0 IL_0063: ldarg.1 IL_0064: ldarg.2 IL_0065: newobj ""<>f__AnonymousType1..ctor(object)"" - IL_006a: callvirt ""void <>A{00000004}#1>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref object, )"" + IL_006a: callvirt ""void <>A{00000010}#1>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref object, )"" IL_006f: nop IL_0070: ret }"); @@ -3910,7 +3910,7 @@ .maxstack 9 // Code size 113 (0x71) .maxstack 9 IL_0000: nop - IL_0001: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>> C.<>o__0#2.<>p__0"" + IL_0001: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>> C.<>o__0#2.<>p__0"" IL_0006: brfalse.s IL_000a IL_0008: br.s IL_0053 IL_000a: ldc.i4 0x100 @@ -3939,16 +3939,16 @@ .maxstack 9 IL_003e: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_0043: stelem.ref IL_0044: 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_0049: call ""System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>> System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_004e: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>> C.<>o__0#2.<>p__0"" - IL_0053: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>> C.<>o__0#2.<>p__0"" - IL_0058: ldfld ""<>A{00000008}#2, object> System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>>.Target"" - IL_005d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000008}#2, object>> C.<>o__0#2.<>p__0"" + IL_0049: call ""System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>> System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_004e: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>> C.<>o__0#2.<>p__0"" + IL_0053: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>> C.<>o__0#2.<>p__0"" + IL_0058: ldfld ""<>A{00000040}#2, object> System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>>.Target"" + IL_005d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000040}#2, object>> C.<>o__0#2.<>p__0"" IL_0062: ldarg.0 IL_0063: ldarg.2 IL_0064: newobj ""<>f__AnonymousType1..ctor(object)"" IL_0069: ldarg.1 - IL_006a: callvirt ""void <>A{00000008}#2, object>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, , ref object)"" + IL_006a: callvirt ""void <>A{00000040}#2, object>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, , ref object)"" IL_006f: nop IL_0070: ret }"); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs index 57e306654bc84..479cd08f07134 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueStateMachineTests.cs @@ -225,55 +225,64 @@ static async Task F() }"; var compilation0 = CreateCompilationWithMscorlib45(source0, options: TestOptions.DebugDll); var compilation1 = compilation0.WithSource(source1); + var v0 = CompileAndVerify(compilation0, emitOptions: EmitOptions.Default.WithDebugInformationFormat(format)); + var f1 = compilation1.GetMember("C.F"); + + using var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var reader0 = md0.MetadataReader; + var generation0 = EmitBaseline.CreateInitialBaseline(md0, EmptyLocalsProvider); - var generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData), EmptyLocalsProvider); var diff1 = compilation1.EmitDifference( generation0, - ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Insert, null, compilation1.GetMember("C.F")))); + ImmutableArray.Create(SemanticEdit.Create(SemanticEditKind.Insert, null, f1))); - using (var md1 = diff1.GetMetadata()) - { - var reader1 = md1.Reader; - - // Add state machine type and its members: - // - Method '.ctor' - // - Method 'MoveNext' - // - Method 'SetStateMachine' - // - Field '<>1__state' - // - Field '<>t__builder' - // - Field '<>u__1' - // Add method F() - CheckEncLogDefinitions(reader1, - Row(1, TableIndex.StandAloneSig, EditAndContinueOperation.Default), - Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddField), - Row(1, TableIndex.Field, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddField), - Row(2, TableIndex.Field, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddField), - Row(3, TableIndex.Field, EditAndContinueOperation.Default), - Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), - Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), - Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), - Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), - Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), - Row(5, TableIndex.MethodDef, EditAndContinueOperation.AddParameter), - Row(1, TableIndex.Param, EditAndContinueOperation.Default), - Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), - Row(1, TableIndex.MethodImpl, EditAndContinueOperation.Default), - Row(2, TableIndex.MethodImpl, EditAndContinueOperation.Default), - Row(1, TableIndex.NestedClass, EditAndContinueOperation.Default), - Row(1, TableIndex.InterfaceImpl, EditAndContinueOperation.Default)); - - diff1.VerifyPdb(new[] { MetadataTokens.MethodDefinitionHandle(4) }, @" + using var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + var readers = new[] { reader0, reader1 }; + + CheckNames(readers, reader1.GetTypeDefNames(), "d__0#1"); + CheckNames(readers, reader1.GetMethodDefNames(), "F", ".ctor", "MoveNext", "SetStateMachine"); + CheckNames(readers, reader1.GetFieldDefNames(), "<>1__state", "<>t__builder", "<>u__1"); + + // Add state machine type and its members: + // - Method '.ctor' + // - Method 'MoveNext' + // - Method 'SetStateMachine' + // - Field '<>1__state' + // - Field '<>t__builder' + // - Field '<>u__1' + // Add method F() + CheckEncLogDefinitions(reader1, + Row(1, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddField), + Row(1, TableIndex.Field, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddField), + Row(2, TableIndex.Field, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddField), + Row(3, TableIndex.Field, EditAndContinueOperation.Default), + Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), + Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(3, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), + Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(5, TableIndex.MethodDef, EditAndContinueOperation.AddParameter), + Row(1, TableIndex.Param, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodImpl, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodImpl, EditAndContinueOperation.Default), + Row(1, TableIndex.NestedClass, EditAndContinueOperation.Default), + Row(1, TableIndex.InterfaceImpl, EditAndContinueOperation.Default)); + + diff1.VerifyPdb(new[] { MetadataTokens.MethodDefinitionHandle(4) }, @" @@ -306,7 +315,6 @@ static async Task F() "); - } } [Fact] diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs index 5d23747fa0a49..cb57c11eb7c86 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IInterpolatedStringOperation.cs @@ -1876,7 +1876,6 @@ public void InterpolationEscapeConstantValue_WithDefaultHandler() 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); @@ -1909,10 +1908,93 @@ public void InterpolationEscapeConstantValue_WithoutDefaultHandler() IInterpolatedStringTextOperation (OperationKind.InterpolatedStringText, Type: null) (Syntax: ' }}') Text: ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "" }}"", IsImplicit) (Syntax: ' }}') - "; VerifyOperationTreeAndDiagnosticsForTest(new[] { code }, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); } + + [Fact] + public void InterpolatedStringsAddedUnderObjectAddition_DefiniteAssignment() + { + var code = @" +#pragma warning disable CS0219 // Unused local +object o1; +object o2; +object o3; +_ = /**/$""{o1 = null}"" + $""{o2 = null}"" + $""{o3 = null}"" + 1/**/; +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: true) }); + + var expectedDiagnostics = DiagnosticDescription.None; + + string expectedOperationTree = @" +IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{o1 = nul ... null}"" + 1') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{o1 = nul ... o3 = null}""') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String) (Syntax: '$""{o1 = nul ... o2 = null}""') + Left: + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{o1 = null}""') + Parts(1): + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{o1 = null}') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object) (Syntax: 'o1 = null') + Left: + ILocalReferenceOperation: o1 (OperationKind.LocalReference, Type: System.Object) (Syntax: 'o1') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Alignment: + null + FormatString: + null + Right: + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{o2 = null}""') + Parts(1): + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{o2 = null}') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object) (Syntax: 'o2 = null') + Left: + ILocalReferenceOperation: o2 (OperationKind.LocalReference, Type: System.Object) (Syntax: 'o2') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Alignment: + null + FormatString: + null + Right: + IInterpolatedStringOperation (OperationKind.InterpolatedString, Type: System.String) (Syntax: '$""{o3 = null}""') + Parts(1): + IInterpolationOperation (OperationKind.Interpolation, Type: null) (Syntax: '{o3 = null}') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object) (Syntax: 'o3 = null') + Left: + ILocalReferenceOperation: o3 (OperationKind.LocalReference, Type: System.Object) (Syntax: 'o3') + Right: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, Constant: null, IsImplicit) (Syntax: 'null') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + ILiteralOperation (OperationKind.Literal, Type: null, Constant: null) (Syntax: 'null') + Alignment: + null + FormatString: + null + Right: + 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') + +"; + + VerifyOperationTreeAndDiagnosticsForTest(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: true) }, + expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularPreview); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index a7351eae5578f..76d15d826bc57 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -73,18 +73,18 @@ static void Main() var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( - // (6,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (6,13): error CS0428: Cannot convert method group 'Main' to non-delegate type 'Delegate'. Did you intend to invoke the method? // d = Main; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Main").WithArguments("inferred delegate type", "10.0").WithLocation(6, 13), - // (7,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "Main").WithArguments("Main", "System.Delegate").WithLocation(6, 13), + // (7,13): error CS1660: Cannot convert lambda expression to type 'Delegate' because it is not a delegate type // d = () => { }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => { }").WithArguments("inferred delegate type", "10.0").WithLocation(7, 13), - // (8,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => { }").WithArguments("lambda expression", "System.Delegate").WithLocation(7, 13), + // (8,13): error CS1660: Cannot convert anonymous method to type 'Delegate' because it is not a delegate type // d = delegate () { }; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "delegate () { }").WithArguments("inferred delegate type", "10.0").WithLocation(8, 13), - // (9,48): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "delegate () { }").WithArguments("anonymous method", "System.Delegate").WithLocation(8, 13), + // (9,48): error CS1660: Cannot convert lambda expression to type 'Expression' because it is not a delegate type // System.Linq.Expressions.Expression e = () => 1; - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => 1").WithArguments("inferred delegate type", "10.0").WithLocation(9, 48)); + Diagnostic(ErrorCode.ERR_AnonMethToNonDel, "() => 1").WithArguments("lambda expression", "System.Linq.Expressions.Expression").WithLocation(9, 48)); comp = CreateCompilation(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics(); @@ -160,17 +160,17 @@ static void Main() yield return getData("T F() => default;", "(new Program()).F", "F", null); yield return getData("void F(T t) { }", "(new Program()).F", "F", "System.Action"); yield return getData("void F(T t) { }", "(new Program()).F", "F", null); - yield return getData("static ref int F() => throw null;", "F", "F", null); - yield return getData("static ref readonly int F() => throw null;", "F", "F", null); + yield return getData("static ref int F() => throw null;", "F", "F", "<>F{00000001}"); + yield return getData("static ref readonly int F() => throw null;", "F", "F", "<>F{00000003}"); yield return getData("static void F() { }", "F", "F", "System.Action"); yield return getData("static void F(int x, int y) { }", "F", "F", "System.Action"); - yield return getData("static void F(out int x, int y) { x = 0; }", "F", "F", null); - yield return getData("static void F(int x, ref int y) { }", "F", "F", null); - yield return getData("static void F(int x, in int y) { }", "F", "F", null); + yield return getData("static void F(out int x, int y) { x = 0; }", "F", "F", "<>A{00000002}"); + yield return getData("static void F(int x, ref int y) { }", "F", "F", "<>A{00000004}"); + yield return getData("static void F(int x, in int y) { }", "F", "F", "<>A{0000000c}"); yield return getData("static void F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) { }", "F", "F", "System.Action"); - yield return getData("static void F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) { }", "F", "F", null); + yield return getData("static void F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) { }", "F", "F", "<>A"); yield return getData("static object F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) => null;", "F", "F", "System.Func"); - yield return getData("static object F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => null;", "F", "F", null); + yield return getData("static object F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => null;", "F", "F", "<>F"); object?[] getData(string methodDeclaration, string methodGroupExpression, string methodGroupOnly, string? expectedType) => new object?[] { methodDeclaration, methodGroupExpression, expectedType is null ? getExpectedDiagnostics(methodGroupExpression, methodGroupOnly) : null, expectedType }; @@ -276,16 +276,16 @@ static void Main() yield return getData("x => ref args[0]", null); yield return getData("(x, y) => { }", null); yield return getData("() => 1", "System.Func"); - yield return getData("() => ref args[0]", null); + yield return getData("() => ref args[0]", "<>F{00000001}"); yield return getData("() => { }", "System.Action"); yield return getData("(int x, int y) => { }", "System.Action"); - yield return getData("(out int x, int y) => { x = 0; }", null); - yield return getData("(int x, ref int y) => { x = 0; }", null); - yield return getData("(int x, in int y) => { x = 0; }", null); + yield return getData("(out int x, int y) => { x = 0; }", "<>A{00000002}"); + yield return getData("(int x, ref int y) => { x = 0; }", "<>A{00000004}"); + yield return getData("(int x, in int y) => { x = 0; }", "<>A{0000000c}"); yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) => { }", "System.Action"); - yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => { }", null); + yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => { }", "<>A"); yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) => _1", "System.Func"); - yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => _1", null); + yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => _1", "<>F"); yield return getData("static () => 1", "System.Func"); yield return getData("async () => { await System.Threading.Tasks.Task.Delay(0); }", "System.Func"); yield return getData("static async () => { await System.Threading.Tasks.Task.Delay(0); return 0; }", "System.Func>"); @@ -314,16 +314,16 @@ static void Main() { yield return getData("delegate { }", null); yield return getData("delegate () { return 1; }", "System.Func"); - yield return getData("delegate () { return ref args[0]; }", null); + yield return getData("delegate () { return ref args[0]; }", "<>F{00000001}"); yield return getData("delegate () { }", "System.Action"); yield return getData("delegate (int x, int y) { }", "System.Action"); - yield return getData("delegate (out int x, int y) { x = 0; }", null); - yield return getData("delegate (int x, ref int y) { x = 0; }", null); - yield return getData("delegate (int x, in int y) { x = 0; }", null); + yield return getData("delegate (out int x, int y) { x = 0; }", "<>A{00000002}"); + yield return getData("delegate (int x, ref int y) { x = 0; }", "<>A{00000004}"); + yield return getData("delegate (int x, in int y) { x = 0; }", "<>A{0000000c}"); yield return getData("delegate (int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) { }", "System.Action"); - yield return getData("delegate (int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) { }", null); + yield return getData("delegate (int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) { }", "<>A"); yield return getData("delegate (int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) { return _1; }", "System.Func"); - yield return getData("delegate (int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) { return _1; }", null); + yield return getData("delegate (int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) { return _1; }", "<>F"); static object?[] getData(string expr, string? expectedType) => new object?[] { expr, expectedType }; @@ -411,7 +411,7 @@ static void Main(string[] args) yield return getData("x => x", null); yield return getData("() => 1", "System.Func"); yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16) => _1", "System.Func"); - yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => _1", null); + yield return getData("(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => _1", "<>F"); yield return getData("static () => 1", "System.Func"); static object?[] getData(string expr, string? expectedType) => @@ -528,9 +528,6 @@ static void Main() // (7,47): error CS1061: 'object' does not contain a definition for 'Length' and no accessible extension method 'Length' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) // Delegate d1 = (object x1) => { _ = x1.Length; }; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Length").WithArguments("object", "Length").WithLocation(7, 47), - // (8,23): error CS8917: The delegate type could not be inferred. - // Delegate d2 = (ref object x2) => { _ = x2.Length; }; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(ref object x2) => { _ = x2.Length; }").WithLocation(8, 23), // (8,51): error CS1061: 'object' does not contain a definition for 'Length' and no accessible extension method 'Length' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) // Delegate d2 = (ref object x2) => { _ = x2.Length; }; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Length").WithArguments("object", "Length").WithLocation(8, 51), @@ -1239,6 +1236,7 @@ static void Main() // System.Func<> and System.Action<> cannot be used as the delegate type // when the parameters or return type are not valid type arguments. + [WorkItem(55217, "https://github.com/dotnet/roslyn/issues/55217")] [Fact] public void InvalidTypeArguments() { @@ -1280,8 +1278,8 @@ unsafe static Delegate F() return (T t, int* p) => { }; } }"; - // When we synthesize delegate types, and infer a synthesized - // delegate type, run the program to report the actual delegate type. + // When we synthesize delegate types with parameter types (such as int*) that cannot + // be used as type arguments, run the program to report the actual delegate type. var comp = CreateCompilation(new[] { source, s_utils }, parseOptions: TestOptions.RegularPreview, options: TestOptions.UnsafeReleaseExe); comp.VerifyDiagnostics( // (11,16): error CS8917: The delegate type could not be inferred. @@ -1821,14 +1819,10 @@ static class E public static void M(this object o, Action a) { Console.WriteLine(""E.M""); } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( - // (7,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // c.M(Main); // C#9: E.M(object x, Action y) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "Main").WithArguments("inferred delegate type", "10.0").WithLocation(7, 13), - // (8,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // c.M(() => { }); // C#9: E.M(object x, Action y) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => { }").WithArguments("inferred delegate type", "10.0").WithLocation(8, 13)); + CompileAndVerify(source, parseOptions: TestOptions.Regular9, expectedOutput: +@"E.M +E.M +"); // Breaking change from C#9 which binds to E.M. CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: @@ -1861,11 +1855,7 @@ static class E public static void M(this object o, Func a) { Console.WriteLine(""E.M""); } }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); - comp.VerifyDiagnostics( - // (8,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. - // c.M(() => 1); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => 1").WithArguments("inferred delegate type", "10.0").WithLocation(8, 13)); + CompileAndVerify(source, parseOptions: TestOptions.Regular9, expectedOutput: @"E.M"); // Breaking change from C#9 which binds to E.M. CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: @"C.M"); @@ -1904,24 +1894,24 @@ static void Main() var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( - // (14,12): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (14,12): error CS1503: Argument 1: cannot convert from 'method group' to 'Delegate' // FA(F2); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "F2").WithArguments("inferred delegate type", "10.0").WithLocation(14, 12), - // (15,12): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_BadArgType, "F2").WithArguments("1", "method group", "System.Delegate").WithLocation(14, 12), + // (15,12): error CS1503: Argument 1: cannot convert from 'method group' to 'Delegate' // FB(F1); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "F1").WithArguments("inferred delegate type", "10.0").WithLocation(15, 12), - // (18,12): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_BadArgType, "F1").WithArguments("1", "method group", "System.Delegate").WithLocation(15, 12), + // (18,18): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // FA(() => 0); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => 0").WithArguments("inferred delegate type", "10.0").WithLocation(18, 12), - // (19,12): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_IllegalStatement, "0").WithLocation(18, 18), + // (19,15): error CS1643: Not all code paths return a value in lambda expression of type 'Func' // FB(() => { }); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => { }").WithArguments("inferred delegate type", "10.0").WithLocation(19, 12), - // (22,12): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_AnonymousReturnExpected, "=>").WithArguments("lambda expression", "System.Func").WithLocation(19, 15), + // (22,26): error CS8030: Anonymous function converted to a void returning delegate cannot return a value // FA(delegate () { return 0; }); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "delegate () { return 0; }").WithArguments("inferred delegate type", "10.0").WithLocation(22, 12), - // (23,12): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + Diagnostic(ErrorCode.ERR_RetNoObjectRequiredLambda, "return").WithLocation(22, 26), + // (23,12): error CS1643: Not all code paths return a value in anonymous method of type 'Func' // FB(delegate () { }); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "delegate () { }").WithArguments("inferred delegate type", "10.0").WithLocation(23, 12)); + Diagnostic(ErrorCode.ERR_AnonymousReturnExpected, "delegate").WithArguments("anonymous method", "System.Func").WithLocation(23, 12)); CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: @"FA(Action) @@ -1959,9 +1949,12 @@ static void Main() var comp = CreateCompilation(source, parseOptions: TestOptions.Regular9); comp.VerifyDiagnostics( - // (11,11): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (11,17): error CS0029: Cannot implicitly convert type 'string' to 'int' // F(() => string.Empty); - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "() => string.Empty").WithArguments("inferred delegate type", "10.0").WithLocation(11, 11)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "string.Empty").WithArguments("string", "int").WithLocation(11, 17), + // (11,17): error CS1662: Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type + // F(() => string.Empty); + Diagnostic(ErrorCode.ERR_CantConvAnonMethReturns, "string.Empty").WithArguments("lambda expression").WithLocation(11, 17)); CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: @"F(Expression>): () => 0 @@ -2373,13 +2366,7 @@ static void Main() } }"; var comp = CreateCompilation(source); - comp.VerifyDiagnostics( - // (6,18): error CS8917: The delegate type could not be inferred. - // var d1 = F; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "F").WithLocation(6, 18), - // (7,18): error CS8917: The delegate type could not be inferred. - // var d2 = (ref int x) => x; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(ref int x) => x").WithLocation(7, 18)); + comp.VerifyDiagnostics(); } [Fact] @@ -2407,6 +2394,25 @@ static void Main() System.Func"); } + [Fact] + public void ImplicitlyTypedVariables_14() + { + var source = +@"delegate void D(string s); +class Program +{ + static void Main() + { + (D x, var y) = (() => string.Empty, () => string.Empty); + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,19): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'. + // (D x, var y) = (() => string.Empty, () => string.Empty); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(6, 19)); + } + [Fact] public void ImplicitlyTypedVariables_UseSiteErrors() { @@ -2476,6 +2482,913 @@ static void Main() comp.VerifyDiagnostics(); } + [Fact] + public void SynthesizedDelegateTypes_01() + { + var source = +@"using System; +class Program +{ + static void M1(T t) + { + var d = (ref T t) => t; + Report(d); + Console.WriteLine(d(ref t)); + } + static void M2(U u) where U : struct + { + var d = (ref U u) => u; + Report(d); + Console.WriteLine(d(ref u)); + } + static void M3(double value) + { + var d = (ref double d) => d; + Report(d); + Console.WriteLine(d(ref value)); + } + static void Main() + { + M1(41); + M2(42f); + M2(43d); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"<>F{00000001}`2[System.Int32,System.Int32] +41 +<>F{00000001}`2[System.Single,System.Single] +42 +<>F{00000001}`2[System.Double,System.Double] +43 +"); + verifier.VerifyIL("Program.M1", +@"{ + // Code size 55 (0x37) + .maxstack 2 + IL_0000: ldsfld ""<>F{00000001} Program.<>c__0.<>9__0_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c__0 Program.<>c__0.<>9"" + IL_000e: ldftn ""T Program.<>c__0.b__0_0(ref T)"" + IL_0014: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""<>F{00000001} Program.<>c__0.<>9__0_0"" + IL_001f: dup + IL_0020: call ""void Program.Report(System.Delegate)"" + IL_0025: ldarga.s V_0 + IL_0027: callvirt ""T <>F{00000001}.Invoke(ref T)"" + IL_002c: box ""T"" + IL_0031: call ""void System.Console.WriteLine(object)"" + IL_0036: ret +}"); + verifier.VerifyIL("Program.M2", +@"{ + // Code size 55 (0x37) + .maxstack 2 + IL_0000: ldsfld ""<>F{00000001} Program.<>c__1.<>9__1_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c__1 Program.<>c__1.<>9"" + IL_000e: ldftn ""U Program.<>c__1.b__1_0(ref U)"" + IL_0014: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""<>F{00000001} Program.<>c__1.<>9__1_0"" + IL_001f: dup + IL_0020: call ""void Program.Report(System.Delegate)"" + IL_0025: ldarga.s V_0 + IL_0027: callvirt ""U <>F{00000001}.Invoke(ref U)"" + IL_002c: box ""U"" + IL_0031: call ""void System.Console.WriteLine(object)"" + IL_0036: ret +}"); + verifier.VerifyIL("Program.M3", +@"{ + // Code size 50 (0x32) + .maxstack 2 + IL_0000: ldsfld ""<>F{00000001} Program.<>c.<>9__2_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_000e: ldftn ""double Program.<>c.b__2_0(ref double)"" + IL_0014: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""<>F{00000001} Program.<>c.<>9__2_0"" + IL_001f: dup + IL_0020: call ""void Program.Report(System.Delegate)"" + IL_0025: ldarga.s V_0 + IL_0027: callvirt ""double <>F{00000001}.Invoke(ref double)"" + IL_002c: call ""void System.Console.WriteLine(double)"" + IL_0031: ret +}"); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var nodes = tree.GetRoot().DescendantNodes(); + + var variables = nodes.OfType().Where(v => v.Identifier.Text == "d").ToArray(); + Assert.Equal(3, variables.Length); + VerifyLocalDelegateType(model, variables[0], "<>F{00000001} d", "T <>F{00000001}.Invoke(ref T)"); + VerifyLocalDelegateType(model, variables[1], "<>F{00000001} d", "U <>F{00000001}.Invoke(ref U)"); + VerifyLocalDelegateType(model, variables[2], "<>F{00000001} d", "System.Double <>F{00000001}.Invoke(ref System.Double)"); + + var identifiers = nodes.OfType().Where(i => i.Expression is IdentifierNameSyntax id && id.Identifier.Text == "Report").Select(i => i.ArgumentList.Arguments[0].Expression).ToArray(); + Assert.Equal(3, identifiers.Length); + VerifyExpressionType(model, identifiers[0], "<>F{00000001} d", "<>F{00000001}"); + VerifyExpressionType(model, identifiers[1], "<>F{00000001} d", "<>F{00000001}"); + VerifyExpressionType(model, identifiers[2], "<>F{00000001} d", "<>F{00000001}"); + } + + [Fact] + public void SynthesizedDelegateTypes_02() + { + var source = +@"using System; +class Program +{ + static void M1(A a, int value) + { + var d = a.F1; + d() = value; + } + static void M2(B b, float value) + { + var d = b.F2; + d() = value; + } + static void Main() + { + var a = new A(); + M1(a, 41); + var b = new B(); + M2(b, 42f); + Console.WriteLine((a._f, b._f)); + } +} +class A +{ + public int _f; + public ref int F1() => ref _f; +} +class B +{ + public float _f; +} +static class E +{ + public static ref float F2(this B b) => ref b._f; +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: @"(41, 42)"); + verifier.VerifyIL("Program.M1", +@"{ + // Code size 20 (0x14) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldftn ""ref int A.F1()"" + IL_0007: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_000c: callvirt ""ref int <>F{00000001}.Invoke()"" + IL_0011: ldarg.1 + IL_0012: stind.i4 + IL_0013: ret +}"); + verifier.VerifyIL("Program.M2", +@"{ + // Code size 20 (0x14) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldftn ""ref float E.F2(B)"" + IL_0007: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_000c: callvirt ""ref float <>F{00000001}.Invoke()"" + IL_0011: ldarg.1 + IL_0012: stind.r4 + IL_0013: ret +}"); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var variables = tree.GetRoot().DescendantNodes().OfType().Where(v => v.Identifier.Text == "d").ToArray(); + Assert.Equal(2, variables.Length); + VerifyLocalDelegateType(model, variables[0], "<>F{00000001} d", "ref System.Int32 <>F{00000001}.Invoke()"); + VerifyLocalDelegateType(model, variables[1], "<>F{00000001} d", "ref System.Single <>F{00000001}.Invoke()"); + } + + [Fact] + public void SynthesizedDelegateTypes_03() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Report((ref int x, int y) => { }); + Report((int x, ref int y) => { }); + Report((ref float x, int y) => { }); + Report((float x, ref int y) => { }); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"<>A{00000001}`2[System.Int32,System.Int32] +<>A{00000004}`2[System.Int32,System.Int32] +<>A{00000001}`2[System.Single,System.Int32] +<>A{00000004}`2[System.Single,System.Int32] +"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 145 (0x91) + .maxstack 2 + IL_0000: ldsfld ""<>A{00000001} Program.<>c.<>9__0_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_000e: ldftn ""void Program.<>c.
b__0_0(ref int, int)"" + IL_0014: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""<>A{00000001} Program.<>c.<>9__0_0"" + IL_001f: call ""void Program.Report(System.Delegate)"" + IL_0024: ldsfld ""<>A{00000004} Program.<>c.<>9__0_1"" + IL_0029: dup + IL_002a: brtrue.s IL_0043 + IL_002c: pop + IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0032: ldftn ""void Program.<>c.
b__0_1(int, ref int)"" + IL_0038: newobj ""<>A{00000004}..ctor(object, System.IntPtr)"" + IL_003d: dup + IL_003e: stsfld ""<>A{00000004} Program.<>c.<>9__0_1"" + IL_0043: call ""void Program.Report(System.Delegate)"" + IL_0048: ldsfld ""<>A{00000001} Program.<>c.<>9__0_2"" + IL_004d: dup + IL_004e: brtrue.s IL_0067 + IL_0050: pop + IL_0051: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0056: ldftn ""void Program.<>c.
b__0_2(ref float, int)"" + IL_005c: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_0061: dup + IL_0062: stsfld ""<>A{00000001} Program.<>c.<>9__0_2"" + IL_0067: call ""void Program.Report(System.Delegate)"" + IL_006c: ldsfld ""<>A{00000004} Program.<>c.<>9__0_3"" + IL_0071: dup + IL_0072: brtrue.s IL_008b + IL_0074: pop + IL_0075: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_007a: ldftn ""void Program.<>c.
b__0_3(float, ref int)"" + IL_0080: newobj ""<>A{00000004}..ctor(object, System.IntPtr)"" + IL_0085: dup + IL_0086: stsfld ""<>A{00000004} Program.<>c.<>9__0_3"" + IL_008b: call ""void Program.Report(System.Delegate)"" + IL_0090: ret +}"); + } + + [Fact] + public void SynthesizedDelegateTypes_04() + { + var source = +@"using System; +class Program +{ + static int i = 0; + static void Main() + { + Report(int () => i); + Report((ref int () => ref i)); + Report((ref readonly int () => ref i)); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"System.Func`1[System.Int32] +<>F{00000001}`1[System.Int32] +<>F{00000003}`1[System.Int32] +"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 109 (0x6d) + .maxstack 2 + IL_0000: ldsfld ""System.Func Program.<>c.<>9__1_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_000e: ldftn ""int Program.<>c.
b__1_0()"" + IL_0014: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""System.Func Program.<>c.<>9__1_0"" + IL_001f: call ""void Program.Report(System.Delegate)"" + IL_0024: ldsfld ""<>F{00000001} Program.<>c.<>9__1_1"" + IL_0029: dup + IL_002a: brtrue.s IL_0043 + IL_002c: pop + IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0032: ldftn ""ref int Program.<>c.
b__1_1()"" + IL_0038: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_003d: dup + IL_003e: stsfld ""<>F{00000001} Program.<>c.<>9__1_1"" + IL_0043: call ""void Program.Report(System.Delegate)"" + IL_0048: ldsfld ""<>F{00000003} Program.<>c.<>9__1_2"" + IL_004d: dup + IL_004e: brtrue.s IL_0067 + IL_0050: pop + IL_0051: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0056: ldftn ""ref readonly int Program.<>c.
b__1_2()"" + IL_005c: newobj ""<>F{00000003}..ctor(object, System.IntPtr)"" + IL_0061: dup + IL_0062: stsfld ""<>F{00000003} Program.<>c.<>9__1_2"" + IL_0067: call ""void Program.Report(System.Delegate)"" + IL_006c: ret +}"); + } + + [Fact] + public void SynthesizedDelegateTypes_05() + { + var source = +@"using System; +class Program +{ + static int i = 0; + static int F1() => i; + static ref int F2() => ref i; + static ref readonly int F3() => ref i; + static void Main() + { + Report(F1); + Report(F2); + Report(F3); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"System.Func`1[System.Int32] +<>F{00000001}`1[System.Int32] +<>F{00000003}`1[System.Int32] +"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 52 (0x34) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""int Program.F1()"" + IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000c: call ""void Program.Report(System.Delegate)"" + IL_0011: ldnull + IL_0012: ldftn ""ref int Program.F2()"" + IL_0018: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_001d: call ""void Program.Report(System.Delegate)"" + IL_0022: ldnull + IL_0023: ldftn ""ref readonly int Program.F3()"" + IL_0029: newobj ""<>F{00000003}..ctor(object, System.IntPtr)"" + IL_002e: call ""void Program.Report(System.Delegate)"" + IL_0033: ret +}"); + } + + [Fact] + public void SynthesizedDelegateTypes_06() + { + var source = +@"using System; +class Program +{ + static int i = 0; + static int F1() => i; + static ref int F2() => ref i; + static ref readonly int F3() => ref i; + static void Main() + { + var d1 = F1; + var d2 = F2; + var d3 = F3; + Report(d1); + Report(d2); + Report(d3); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + CompileAndVerify(comp, expectedOutput: +@"System.Func`1[System.Int32] +<>F{00000001}`1[System.Int32] +<>F{00000003}`1[System.Int32] +"); + } + + [Fact] + public void SynthesizedDelegateTypes_07() + { + var source = +@"using System; +class Program +{ + static void Main() + { + Report(int (ref int i) => i); + Report((ref int (ref int i) => ref i)); + Report((ref readonly int (ref int i) => ref i)); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: +@"<>F{00000001}`2[System.Int32,System.Int32] +<>F{00000005}`2[System.Int32,System.Int32] +<>F{0000000d}`2[System.Int32,System.Int32] +"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 109 (0x6d) + .maxstack 2 + IL_0000: ldsfld ""<>F{00000001} Program.<>c.<>9__0_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_000e: ldftn ""int Program.<>c.
b__0_0(ref int)"" + IL_0014: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""<>F{00000001} Program.<>c.<>9__0_0"" + IL_001f: call ""void Program.Report(System.Delegate)"" + IL_0024: ldsfld ""<>F{00000005} Program.<>c.<>9__0_1"" + IL_0029: dup + IL_002a: brtrue.s IL_0043 + IL_002c: pop + IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0032: ldftn ""ref int Program.<>c.
b__0_1(ref int)"" + IL_0038: newobj ""<>F{00000005}..ctor(object, System.IntPtr)"" + IL_003d: dup + IL_003e: stsfld ""<>F{00000005} Program.<>c.<>9__0_1"" + IL_0043: call ""void Program.Report(System.Delegate)"" + IL_0048: ldsfld ""<>F{0000000d} Program.<>c.<>9__0_2"" + IL_004d: dup + IL_004e: brtrue.s IL_0067 + IL_0050: pop + IL_0051: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0056: ldftn ""ref readonly int Program.<>c.
b__0_2(ref int)"" + IL_005c: newobj ""<>F{0000000d}..ctor(object, System.IntPtr)"" + IL_0061: dup + IL_0062: stsfld ""<>F{0000000d} Program.<>c.<>9__0_2"" + IL_0067: call ""void Program.Report(System.Delegate)"" + IL_006c: ret +}"); + } + + [Fact] + public void SynthesizedDelegateTypes_08() + { + var source = +@"#pragma warning disable 414 +using System; +class Program +{ + static int i = 0; + static void Main() + { + Report((int i) => { }); + Report((out int i) => { i = 0; }); + Report((ref int i) => { }); + Report((in int i) => { }); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"System.Action`1[System.Int32] +<>A{00000002}`1[System.Int32] +<>A{00000001}`1[System.Int32] +<>A{00000003}`1[System.Int32] +"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 145 (0x91) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.<>c.<>9__1_0"" + IL_0005: dup + IL_0006: brtrue.s IL_001f + IL_0008: pop + IL_0009: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_000e: ldftn ""void Program.<>c.
b__1_0(int)"" + IL_0014: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0019: dup + IL_001a: stsfld ""System.Action Program.<>c.<>9__1_0"" + IL_001f: call ""void Program.Report(System.Delegate)"" + IL_0024: ldsfld ""<>A{00000002} Program.<>c.<>9__1_1"" + IL_0029: dup + IL_002a: brtrue.s IL_0043 + IL_002c: pop + IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0032: ldftn ""void Program.<>c.
b__1_1(out int)"" + IL_0038: newobj ""<>A{00000002}..ctor(object, System.IntPtr)"" + IL_003d: dup + IL_003e: stsfld ""<>A{00000002} Program.<>c.<>9__1_1"" + IL_0043: call ""void Program.Report(System.Delegate)"" + IL_0048: ldsfld ""<>A{00000001} Program.<>c.<>9__1_2"" + IL_004d: dup + IL_004e: brtrue.s IL_0067 + IL_0050: pop + IL_0051: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0056: ldftn ""void Program.<>c.
b__1_2(ref int)"" + IL_005c: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_0061: dup + IL_0062: stsfld ""<>A{00000001} Program.<>c.<>9__1_2"" + IL_0067: call ""void Program.Report(System.Delegate)"" + IL_006c: ldsfld ""<>A{00000003} Program.<>c.<>9__1_3"" + IL_0071: dup + IL_0072: brtrue.s IL_008b + IL_0074: pop + IL_0075: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_007a: ldftn ""void Program.<>c.
b__1_3(in int)"" + IL_0080: newobj ""<>A{00000003}..ctor(object, System.IntPtr)"" + IL_0085: dup + IL_0086: stsfld ""<>A{00000003} Program.<>c.<>9__1_3"" + IL_008b: call ""void Program.Report(System.Delegate)"" + IL_0090: ret +}"); + } + + [Fact] + public void SynthesizedDelegateTypes_09() + { + var source = +@"#pragma warning disable 414 +using System; +class Program +{ + static void M1(int i) { } + static void M2(out int i) { i = 0; } + static void M3(ref int i) { } + static void M4(in int i) { } + static void Main() + { + Report(M1); + Report(M2); + Report(M3); + Report(M4); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"System.Action`1[System.Int32] +<>A{00000002}`1[System.Int32] +<>A{00000001}`1[System.Int32] +<>A{00000003}`1[System.Int32] +"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 69 (0x45) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void Program.M1(int)"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: call ""void Program.Report(System.Delegate)"" + IL_0011: ldnull + IL_0012: ldftn ""void Program.M2(out int)"" + IL_0018: newobj ""<>A{00000002}..ctor(object, System.IntPtr)"" + IL_001d: call ""void Program.Report(System.Delegate)"" + IL_0022: ldnull + IL_0023: ldftn ""void Program.M3(ref int)"" + IL_0029: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_002e: call ""void Program.Report(System.Delegate)"" + IL_0033: ldnull + IL_0034: ldftn ""void Program.M4(in int)"" + IL_003a: newobj ""<>A{00000003}..ctor(object, System.IntPtr)"" + IL_003f: call ""void Program.Report(System.Delegate)"" + IL_0044: ret +}"); + } + + [Fact] + public void SynthesizedDelegateTypes_10() + { + var source = +@"#pragma warning disable 414 +using System; +class Program +{ + static void M1(int i) { } + static void M2(out int i) { i = 0; } + static void M3(ref int i) { } + static void M4(in int i) { } + static void Main() + { + var d1 = M1; + var d2 = M2; + var d3 = M3; + var d4 = M4; + Report(d1); + Report(d2); + Report(d3); + Report(d4); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + CompileAndVerify(comp, expectedOutput: +@"System.Action`1[System.Int32] +<>A{00000002}`1[System.Int32] +<>A{00000001}`1[System.Int32] +<>A{00000003}`1[System.Int32] +"); + } + + [WorkItem(55217, "https://github.com/dotnet/roslyn/issues/55217")] + [Fact] + public void SynthesizedDelegateTypes_11() + { + var source = +@"class Program +{ + unsafe static void Main() + { + var d1 = int* () => (int*)42; + var d2 = (int* p) => { }; + var d3 = delegate* () => default; + var d4 = (delegate* d) => { }; + } +}"; + + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); + comp.VerifyDiagnostics( + // (5,18): error CS8917: The delegate type could not be inferred. + // var d1 = int* () => (int*)42; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "int* () => (int*)42").WithLocation(5, 18), + // (6,18): error CS8917: The delegate type could not be inferred. + // var d2 = (int* p) => { }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(int* p) => { }").WithLocation(6, 18), + // (7,18): error CS8917: The delegate type could not be inferred. + // var d3 = delegate* () => default; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "delegate* () => default").WithLocation(7, 18), + // (8,18): error CS8917: The delegate type could not be inferred. + // var d4 = (delegate* d) => { }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(delegate* d) => { }").WithLocation(8, 18)); + } + + [WorkItem(55217, "https://github.com/dotnet/roslyn/issues/55217")] + [ConditionalFact(typeof(DesktopOnly))] + public void SynthesizedDelegateTypes_12() + { + var source = +@"using System; +class Program +{ + static void Main() + { + var d1 = (TypedReference x) => { }; + var d2 = (int x, RuntimeArgumentHandle y) => { }; + var d3 = (ArgIterator x) => { }; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (6,18): error CS8917: The delegate type could not be inferred. + // var d1 = (TypedReference x) => { }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(TypedReference x) => { }").WithLocation(6, 18), + // (7,18): error CS8917: The delegate type could not be inferred. + // var d2 = (int x, RuntimeArgumentHandle y) => { }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(int x, RuntimeArgumentHandle y) => { }").WithLocation(7, 18), + // (8,18): error CS8917: The delegate type could not be inferred. + // var d3 = (ArgIterator x) => { }; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "(ArgIterator x) => { }").WithLocation(8, 18)); + } + + [WorkItem(55217, "https://github.com/dotnet/roslyn/issues/55217")] + [Fact] + public void SynthesizedDelegateTypes_13() + { + var source = +@"ref struct S { } +class Program +{ + static void F1(int x, S y) { } + static S F2() => throw null; + static void Main() + { + var d1 = F1; + var d2 = F2; + } +}"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (8,18): error CS8917: The delegate type could not be inferred. + // var d1 = F1; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "F1").WithLocation(8, 18), + // (9,18): error CS8917: The delegate type could not be inferred. + // var d2 = F2; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "F2").WithLocation(9, 18)); + } + + [Fact] + public void SynthesizedDelegateTypes_14() + { + var source = +@"class Program +{ + static ref void F() { } + static void Main() + { + var d1 = F; + var d2 = (ref void () => { }); + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (3,16): error CS1547: Keyword 'void' cannot be used in this context + // static ref void F() { } + Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(3, 16), + // (6,18): error CS8917: The delegate type could not be inferred. + // var d1 = F; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "F").WithLocation(6, 18), + // (7,19): error CS8917: The delegate type could not be inferred. + // var d2 = (ref void () => { }); + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "ref void () => { }").WithLocation(7, 19), + // (7,23): error CS1547: Keyword 'void' cannot be used in this context + // var d2 = (ref void () => { }); + Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(7, 23)); + } + + [Fact] + public void SynthesizedDelegateTypes_15() + { + var source = +@"using System; +unsafe class Program +{ + static byte*[] F1() => null; + static void F2(byte*[] a) { } + static byte*[] F3(ref int i) => null; + static void F4(ref byte*[] a) { } + static void Main() + { + Report(int*[] () => null); + Report((int*[] a) => { }); + Report(int*[] (ref int i) => null); + Report((ref int*[] a) => { }); + Report(F1); + Report(F2); + Report(F3); + Report(F4); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); + comp.VerifyDiagnostics(); + + CompileAndVerify(comp, expectedOutput: +@"System.Func`1[System.Int32*[]] +System.Action`1[System.Int32*[]] +<>F{00000001}`2[System.Int32,System.Int32*[]] +<>A{00000001}`1[System.Int32*[]] +System.Func`1[System.Byte*[]] +System.Action`1[System.Byte*[]] +<>F{00000001}`2[System.Int32,System.Byte*[]] +<>A{00000001}`1[System.Byte*[]] +"); + } + + [Fact] + public void SynthesizedDelegateTypes_16() + { + var source = +@"using System; +unsafe class Program +{ + static delegate*[] F1() => null; + static void F2(delegate*[] a) { } + static delegate*[] F3(ref int i) => null; + static void F4(ref delegate*[] a) { } + static void Main() + { + Report(delegate*[] () => null); + Report((delegate*[] a) => { }); + Report(delegate*[] (ref int i) => null); + Report((ref delegate*[] a) => { }); + Report(F1); + Report(F2); + Report(F3); + Report(F4); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); + comp.VerifyDiagnostics(); + + CompileAndVerify(comp, expectedOutput: +@"System.Func`1[(fnptr)[]] +System.Action`1[(fnptr)[]] +<>F{00000001}`2[System.Int32,(fnptr)[]] +<>A{00000001}`1[(fnptr)[]] +System.Func`1[(fnptr)[]] +System.Action`1[(fnptr)[]] +<>F{00000001}`2[System.Int32,(fnptr)[]] +<>A{00000001}`1[(fnptr)[]] +"); + } + + [Fact] + public void SynthesizedDelegateTypes_17() + { + var source = +@"#nullable enable +using System; +class Program +{ + static void F1(object x, dynamic y) { } + static void F2(IntPtr x, nint y) { } + static void F3((int x, int y) t) { } + static void F4(object? x, object?[] y) { } + static void F5(ref object x, dynamic y) { } + static void F6(IntPtr x, ref nint y) { } + static void F7(ref (int x, int y) t) { } + static void F8(object? x, ref object?[] y) { } + static void Main() + { + Report(F1); + Report(F2); + Report(F3); + Report(F4); + Report(F5); + Report(F6); + Report(F7); + Report(F8); + } + static void Report(Delegate d) => Console.WriteLine(d.GetType()); +}"; + + var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + comp.VerifyDiagnostics(); + + CompileAndVerify(comp, expectedOutput: +@"System.Action`2[System.Object,System.Object] +System.Action`2[System.IntPtr,System.IntPtr] +System.Action`1[System.ValueTuple`2[System.Int32,System.Int32]] +System.Action`2[System.Object,System.Object[]] +<>A{00000001}`2[System.Object,System.Object] +<>A{00000004}`2[System.IntPtr,System.IntPtr] +<>A{00000001}`1[System.ValueTuple`2[System.Int32,System.Int32]] +<>A{00000004}`2[System.Object,System.Object[]] +"); + } + + private static void VerifyLocalDelegateType(SemanticModel model, VariableDeclaratorSyntax variable, string expectedLocal, string expectedInvokeMethod) + { + var local = (ILocalSymbol)model.GetDeclaredSymbol(variable)!; + Assert.Equal(expectedLocal, local.ToTestDisplayString()); + var delegateType = ((INamedTypeSymbol)local.Type); + Assert.Equal(Accessibility.Internal, delegateType.DeclaredAccessibility); + Assert.Equal(expectedInvokeMethod, delegateType.DelegateInvokeMethod.ToTestDisplayString()); + } + + private static void VerifyExpressionType(SemanticModel model, ExpressionSyntax variable, string expectedSymbol, string expectedType) + { + var symbol = model.GetSymbolInfo(variable).Symbol; + Assert.Equal(expectedSymbol, symbol.ToTestDisplayString()); + var type = model.GetTypeInfo(variable).Type; + Assert.Equal(expectedType, type.ToTestDisplayString()); + } + [Fact] public void TaskRunArgument() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index f6ae783997c10..aba1115334f63 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -11,9 +11,7 @@ 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 @@ -1273,8 +1271,10 @@ static void Main() ); } - [Fact, WorkItem(54702, "https://github.com/dotnet/roslyn/issues/54702")] - public void InterpolatedStringHandler_ConcatPreferencesForAllStringElements() + [Theory, WorkItem(54702, "https://github.com/dotnet/roslyn/issues/54702")] + [InlineData(@"$""{s1}{s2}""", @"$""{s1}{s2}{s3}""", @"$""{s1}{s2}{s3}{s4}""", @"$""{s1}{s2}{s3}{s4}{s5}""")] + [InlineData(@"$""{s1}"" + $""{s2}""", @"$""{s1}"" + $""{s2}"" + $""{s3}""", @"$""{s1}"" + $""{s2}"" + $""{s3}"" + $""{s4}""", @"$""{s1}"" + $""{s2}"" + $""{s3}"" + $""{s4}"" + $""{s5}""")] + public void InterpolatedStringHandler_ConcatPreferencesForAllStringElements(string twoComponents, string threeComponents, string fourComponents, string fiveComponents) { var code = @" using System; @@ -1287,7 +1287,7 @@ string TwoComponents() { string s1 = ""1""; string s2 = ""2""; - return $""{s1}{s2}""; + return " + twoComponents + @"; } string ThreeComponents() @@ -1295,7 +1295,7 @@ string ThreeComponents() string s1 = ""1""; string s2 = ""2""; string s3 = ""3""; - return $""{s1}{s2}{s3}""; + return " + threeComponents + @"; } string FourComponents() @@ -1304,7 +1304,7 @@ string FourComponents() string s2 = ""2""; string s3 = ""3""; string s4 = ""4""; - return $""{s1}{s2}{s3}{s4}""; + return " + fourComponents + @"; } string FiveComponents() @@ -1314,7 +1314,7 @@ string FiveComponents() string s3 = ""3""; string s4 = ""4""; string s5 = ""5""; - return $""{s1}{s2}{s3}{s4}{s5}""; + return " + fiveComponents + @"; } "; @@ -1433,11 +1433,15 @@ .locals init (string V_0, //s1 [Theory] [CombinatorialData] - public void InterpolatedStringHandler_OverloadsAndBoolReturns(bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg) + public void InterpolatedStringHandler_OverloadsAndBoolReturns( + bool useDefaultParameters, + bool useBoolReturns, + bool constructorBoolArg, + [CombinatorialValues(@"$""base{a}{a,1}{a:X}{a,2:Y}""", @"$""base"" + $""{a}"" + $""{a,1}"" + $""{a:X}"" + $""{a,2:Y}""")] string expression) { var source = @"int a = 1; -System.Console.WriteLine($""base{a}{a,1}{a:X}{a,2:Y}"");"; +System.Console.WriteLine(" + expression + @");"; string interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters, useBoolReturns, constructorBoolArg: constructorBoolArg); @@ -1854,13 +1858,14 @@ public void UseOfSpanInInterpolationHole_CSharp9() [ConditionalTheory(typeof(MonoOrCoreClrOnly))] [CombinatorialData] - public void UseOfSpanInInterpolationHole(bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg) + public void UseOfSpanInInterpolationHole(bool useDefaultParameters, bool useBoolReturns, bool constructorBoolArg, + [CombinatorialValues(@"$""base{a}{a,1}{a:X}{a,2:Y}""", @"$""base"" + $""{a}"" + $""{a,1}"" + $""{a:X}"" + $""{a,2:Y}""")] string expression) { var source = @" using System; ReadOnlySpan a = ""1""; -System.Console.WriteLine($""base{a}{a,1}{a:X}{a,2:Y}"");"; +System.Console.WriteLine(" + expression + ");"; string interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: true, useDefaultParameters, useBoolReturns, constructorBoolArg: constructorBoolArg); @@ -2267,13 +2272,15 @@ .locals init (System.ReadOnlySpan V_0, //a }; } - [Fact] - public void BoolReturns_ShortCircuit() + [Theory] + [InlineData(@"$""base{Throw()}{a = 2}""")] + [InlineData(@"$""base"" + $""{Throw()}"" + $""{a = 2}""")] + public void BoolReturns_ShortCircuit(string expression) { var source = @" using System; int a = 1; -Console.Write($""base{Throw()}{a = 2}""); +Console.Write(" + expression + @"); Console.WriteLine(a); string Throw() => throw new Exception();"; @@ -2286,13 +2293,14 @@ public void BoolReturns_ShortCircuit() [Theory] [CombinatorialData] - public void BoolOutParameter_ShortCircuits(bool useBoolReturns) + public void BoolOutParameter_ShortCircuits(bool useBoolReturns, + [CombinatorialValues(@"$""{Throw()}{a = 2}""", @"$""{Throw()}"" + $""{a = 2}""")] string expression) { var source = @" using System; int a = 1; Console.WriteLine(a); -Console.WriteLine($""{Throw()}{a = 2}""); +Console.WriteLine(" + expression + @"); Console.WriteLine(a); string Throw() => throw new Exception(); "; @@ -2305,21 +2313,23 @@ public void BoolOutParameter_ShortCircuits(bool useBoolReturns) 1"); } - [Fact] - public void AwaitInHoles_UsesFormat() + [Theory] + [InlineData(@"$""base{await Hole()}""")] + [InlineData(@"$""base"" + $""{await Hole()}""")] + public void AwaitInHoles_UsesFormat(string expression) { var source = @" using System; using System.Threading.Tasks; -Console.WriteLine($""base{await Hole()}""); +Console.WriteLine(" + expression + @"); 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", @" + verifier.VerifyIL("$.<
$>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext", !expression.Contains("+") ? @" { // Code size 164 (0xa4) .maxstack 3 @@ -2395,18 +2405,98 @@ .locals init (int V_0, IL_009e: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" IL_00a3: ret } -"); +" +: @" +{ + // Code size 174 (0xae) + .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_00ad + 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"" + IL_0067: ldstr ""{0}"" + IL_006c: ldloc.1 + IL_006d: box ""int"" + IL_0072: call ""string string.Format(string, object)"" + IL_0077: call ""string string.Concat(string, string)"" + IL_007c: call ""void System.Console.WriteLine(string)"" + IL_0081: leave.s IL_009a + } + catch System.Exception + { + IL_0083: stloc.3 + IL_0084: ldarg.0 + IL_0085: ldc.i4.s -2 + IL_0087: stfld ""int $.<
$>d__0.<>1__state"" + IL_008c: ldarg.0 + IL_008d: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_0092: ldloc.3 + IL_0093: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)"" + IL_0098: leave.s IL_00ad + } + IL_009a: ldarg.0 + IL_009b: ldc.i4.s -2 + IL_009d: stfld ""int $.<
$>d__0.<>1__state"" + IL_00a2: ldarg.0 + IL_00a3: ldflda ""System.Runtime.CompilerServices.AsyncTaskMethodBuilder $.<
$>d__0.<>t__builder"" + IL_00a8: call ""void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()"" + IL_00ad: ret +}"); } - [Fact] - public void NoAwaitInHoles_UsesBuilder() + [Theory] + [InlineData(@"$""base{hole}""")] + [InlineData(@"$""base"" + $""{hole}""")] + public void NoAwaitInHoles_UsesBuilder(string expression) { var source = @" using System; using System.Threading.Tasks; var hole = await Hole(); -Console.WriteLine($""base{hole}""); +Console.WriteLine(" + expression + @"); Task Hole() => Task.FromResult(1);"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); @@ -2503,15 +2593,17 @@ .locals init (int V_0, "); } - [Fact] - public void NoAwaitInHoles_AwaitInExpression_UsesBuilder() + [Theory] + [InlineData(@"$""base{hole}""")] + [InlineData(@"$""base"" + $""{hole}""")] + public void NoAwaitInHoles_AwaitInExpression_UsesBuilder(string expression) { var source = @" using System; using System.Threading.Tasks; var hole = 2; -Test(await M(1), $""base{hole}"", await M(3)); +Test(await M(1), " + expression + @", await M(3)); void Test(int i1, string s, int i2) => Console.WriteLine(s); Task M(int i) { @@ -3047,15 +3139,17 @@ [opt] string format ); } - [Fact] - public void UnsupportedArgumentType() + [Theory] + [InlineData(@"$""{i}{s}""")] + [InlineData(@"$""{i}"" + $""{s}""")] + public void UnsupportedArgumentType(string expression) { var source = @" unsafe { int* i = null; var s = new S(); - _ = $""{i}{s}""; + _ = " + expression + @"; } ref struct S { @@ -3070,16 +3164,18 @@ ref struct 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) + Diagnostic(ErrorCode.ERR_BadTypeArgument, "{s}").WithArguments("S").WithLocation(6, 5 + expression.Length) ); } - [Fact] - public void TargetTypedInterpolationHoles() + [Theory] + [InlineData(@"$""{b switch { true => 1, false => null }}{(!b ? null : 2)}{default}{null}""")] + [InlineData(@"$""{b switch { true => 1, false => null }}"" + $""{(!b ? null : 2)}"" + $""{default}"" + $""{null}""")] + public void TargetTypedInterpolationHoles(string expression) { var source = @" bool b = true; -System.Console.WriteLine($""{b switch { true => 1, false => null }}{(!b ? null : 2)}{default}{null}"");"; +System.Console.WriteLine(" + expression + @");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); @@ -3135,10 +3231,12 @@ .locals init (bool V_0, //b "); } - [Fact] - public void TargetTypedInterpolationHoles_Errors() + [Theory] + [InlineData(@"$""{(null, default)}{new()}""")] + [InlineData(@"$""{(null, default)}"" + $""{new()}""")] + public void TargetTypedInterpolationHoles_Errors(string expression) { - var source = @"System.Console.WriteLine($""{(null, default)}{new()}"");"; + var source = @"System.Console.WriteLine(" + expression + @");"; var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); var comp = CreateCompilation(new[] { source, interpolatedStringBuilder }, parseOptions: TestOptions.Regular9); @@ -3151,7 +3249,7 @@ public void TargetTypedInterpolationHoles_Errors() 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) + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "new()").WithArguments("string", "0").WithLocation(1, 19 + expression.Length) ); } @@ -3169,7 +3267,7 @@ public void RefTernary() } [Fact] - public void NestedInterpolatedStrings() + public void NestedInterpolatedStrings_01() { var source = @" int i = 1; @@ -3202,6 +3300,58 @@ .locals init (int V_0, //i "); } + [Theory] + [InlineData(@"$""{$""{i1}""}{$""{i2}""}""")] + [InlineData(@"$""{$""{i1}""}"" + $""{$""{i2}""}""")] + public void NestedInterpolatedStrings_02(string expression) + { + var source = @" +int i1 = 1; +int i2 = 2; +System.Console.WriteLine(" + expression + @");"; + + var interpolatedStringBuilder = GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false); + + var verifier = CompileAndVerify(new[] { source, interpolatedStringBuilder }, expectedOutput: @" +value:1 +value:2"); + + verifier.VerifyIL("", @" +{ + // Code size 63 (0x3f) + .maxstack 4 + .locals init (int V_0, //i1 + int V_1, //i2 + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_2) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.2 + IL_0003: stloc.1 + IL_0004: ldloca.s V_2 + IL_0006: ldc.i4.0 + IL_0007: ldc.i4.1 + IL_0008: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_000d: ldloca.s V_2 + IL_000f: ldloc.0 + IL_0010: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0015: ldloca.s V_2 + IL_0017: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_001c: ldloca.s V_2 + IL_001e: ldc.i4.0 + IL_001f: ldc.i4.1 + IL_0020: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0025: ldloca.s V_2 + IL_0027: ldloc.1 + IL_0028: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_002d: ldloca.s V_2 + IL_002f: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0034: call ""string string.Concat(string, string)"" + IL_0039: call ""void System.Console.WriteLine(string)"" + IL_003e: ret +} +"); + } + [Fact] public void ExceptionFilter_01() { @@ -3377,15 +3527,17 @@ .locals init (System.ReadOnlySpan V_0, //s "); } - [ConditionalFact(typeof(MonoOrCoreClrOnly), typeof(NoIOperationValidation))] - public void ImplicitUserDefinedConversionInHole() + [ConditionalTheory(typeof(MonoOrCoreClrOnly), typeof(NoIOperationValidation))] + [InlineData(@"$""{s}{c}""")] + [InlineData(@"$""{s}"" + $""{c}""")] + public void ImplicitUserDefinedConversionInHole(string expression) { var source = @" using System; S s = default; C c = new C(); -Console.WriteLine($""{s}{c}""); +Console.WriteLine(" + expression + @"); ref struct S { @@ -3458,13 +3610,15 @@ ref struct S ); } - [Fact] - public void ImplicitUserDefinedConversionInLiteral() + [Theory] + [InlineData(@"$""Text{1}""")] + [InlineData(@"$""Text"" + $""{1}""")] + public void ImplicitUserDefinedConversionInLiteral(string expression) { var source = @" using System; -Console.WriteLine($""Text{1}""); +Console.WriteLine(" + expression + @"); public struct CustomStruct { @@ -3517,13 +3671,15 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V "); } - [Fact] - public void ExplicitUserDefinedConversionInLiteral() + [Theory] + [InlineData(@"$""Text{1}""")] + [InlineData(@"$""Text"" + $""{1}""")] + public void ExplicitUserDefinedConversionInLiteral(string expression) { var source = @" using System; -Console.WriteLine($""Text{1}""); +Console.WriteLine(" + expression + @"); public struct CustomStruct { @@ -3556,13 +3712,15 @@ public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) ); } - [Fact] - public void InvalidBuilderReturnType() + [Theory] + [InlineData(@"$""Text{1}""")] + [InlineData(@"$""Text"" + $""{1}""")] + public void InvalidBuilderReturnType(string expression) { var source = @" using System; -Console.WriteLine($""Text{1}""); +Console.WriteLine(" + expression + @"); namespace System.Runtime.CompilerServices { @@ -3587,18 +3745,20 @@ public DefaultInterpolatedStringHandler(int literalLength, int formattedCount) 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) + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)").WithLocation(4, 15 + expression.Length) ); } - [Fact] - public void MixedBuilderReturnTypes_01() + [Theory] + [InlineData(@"$""Text{1}""", @"$""{1}Text""")] + [InlineData(@"$""Text"" + $""{1}""", @"$""{1}"" + $""Text""")] + public void MixedBuilderReturnTypes_01(string expression1, string expression2) { var source = @" using System; -Console.WriteLine($""Text{1}""); -Console.WriteLine($""{1}Text""); +Console.WriteLine(" + expression1 + @"); +Console.WriteLine(" + expression2 + @"); namespace System.Runtime.CompilerServices { @@ -3620,21 +3780,23 @@ public void AppendFormatted(object o) { } 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)", "bool").WithLocation(4, 15 + expression1.Length), // (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) + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "void").WithLocation(5, 14 + expression2.Length) ); } - [Fact] - public void MixedBuilderReturnTypes_02() + [Theory] + [InlineData(@"$""Text{1}""", @"$""{1}Text""")] + [InlineData(@"$""Text"" + $""{1}""", @"$""{1}"" + $""Text""")] + public void MixedBuilderReturnTypes_02(string expression1, string expression2) { var source = @" using System; -Console.WriteLine($""Text{1}""); -Console.WriteLine($""{1}Text""); +Console.WriteLine(" + expression1 + @"); +Console.WriteLine(" + expression2 + @"); namespace System.Runtime.CompilerServices { @@ -3656,10 +3818,10 @@ public void AppendLiteral(string s) { } 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "{1}").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(object)", "void").WithLocation(4, 15 + expression1.Length), // (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) + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnInconsistent, "Text").WithArguments("System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendLiteral(string)", "bool").WithLocation(5, 14 + expression2.Length) ); } @@ -3727,7 +3889,12 @@ private static void VerifyInterpolatedStringExpression(CSharpCompilation comp, s { var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); - var interpolatedString = tree.GetRoot().DescendantNodes().OfType().Single(); + var descendentNodes = tree.GetRoot().DescendantNodes(); + var interpolatedString = + (ExpressionSyntax)descendentNodes.OfType() + .Where(b => b.DescendantNodes().OfType().Any()) + .FirstOrDefault() + ?? descendentNodes.OfType().Single(); var semanticInfo = model.GetSemanticInfoSummary(interpolatedString); Assert.Equal(SpecialType.System_String, semanticInfo.Type.SpecialType); @@ -3738,6 +3905,12 @@ private static void VerifyInterpolatedStringExpression(CSharpCompilation comp, s Assert.True(semanticInfo.ImplicitConversion.IsInterpolatedStringHandler); Assert.Null(semanticInfo.ImplicitConversion.Method); + if (interpolatedString is BinaryExpressionSyntax) + { + Assert.False(semanticInfo.ConstantValue.HasValue); + AssertEx.Equal("System.String System.String.op_Addition(System.String left, System.String right)", semanticInfo.Symbol.ToTestDisplayString()); + } + // https://github.com/dotnet/roslyn/issues/54505 Assert IConversionOperation.IsImplicit when IOperation is implemented for interpolated strings. } @@ -3749,10 +3922,11 @@ private CompilationVerifier CompileAndVerifyOnCorrectPlatforms(CSharpCompilation [Theory] [CombinatorialData] - public void CustomHandlerLocal([CombinatorialValues("class", "struct")] string type, bool useBoolReturns) + public void CustomHandlerLocal([CombinatorialValues("class", "struct")] string type, bool useBoolReturns, + [CombinatorialValues(@"$""Literal{1,2:f}""", @"$""Literal"" + $""{1,2:f}""")] string expression) { var code = @" -CustomHandler builder = $""Literal{1,2:f}""; +CustomHandler builder = " + expression + @"; System.Console.WriteLine(builder.ToString());"; var builder = GetInterpolatedStringCustomHandlerType("CustomHandler", type, useBoolReturns); @@ -3882,11 +4056,13 @@ .maxstack 5 }; } - [Fact] - public void CustomHandlerMethodArgument() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void CustomHandlerMethodArgument(string expression) { var code = @" -M($""{1,2:f}Literal""); +M(" + expression + @"); void M(CustomHandler b) { System.Console.WriteLine(b.ToString()); @@ -3929,10 +4105,12 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void ExplicitHandlerCast_InCode() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"($""{1,2:f}"" + $""Literal"")")] + public void ExplicitHandlerCast_InCode(string expression) { - var code = @"System.Console.WriteLine((CustomHandler)$""{1,2:f}Literal"");"; + var code = @"System.Console.WriteLine((CustomHandler)" + expression + @");"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false) }); var tree = comp.SyntaxTrees[0]; @@ -3944,7 +4122,7 @@ public void ExplicitHandlerCast_InCode() Assert.Equal(ConversionKind.ImplicitReference, semanticInfo.ImplicitConversion.Kind); syntax = ((CastExpressionSyntax)syntax).Expression; - Assert.IsType(syntax); + Assert.Equal(expression, syntax.ToString()); semanticInfo = model.GetSemanticInfoSummary(syntax); Assert.Equal(SpecialType.System_String, semanticInfo.Type.SpecialType); Assert.Equal(SpecialType.System_String, semanticInfo.ConvertedType.SpecialType); @@ -3980,11 +4158,13 @@ .maxstack 5 "); } - [Fact] - public void HandlerConversionPreferredOverStringForNonConstant() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void HandlerConversionPreferredOverStringForNonConstant(string expression) { var code = @" -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { public static void M(CustomHandler b) @@ -4035,11 +4215,13 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void StringPreferredOverHandlerConversionForConstant() + [Theory] + [InlineData(@"$""{""Literal""}""")] + [InlineData(@"$""{""Lit""}"" + $""{""eral""}""")] + public void StringPreferredOverHandlerConversionForConstant(string expression) { var code = @" -C.M($""{""Literal""}""); +C.M(" + expression + @"); class C { public static void M(CustomHandler b) @@ -4067,13 +4249,15 @@ .maxstack 1 "); } - [Fact] - public void HandlerConversionPreferredOverStringForNonConstant_AttributeConstructor() + [Theory] + [InlineData(@"$""{1}{2}""")] + [InlineData(@"$""{1}"" + $""{2}""")] + public void HandlerConversionPreferredOverStringForNonConstant_AttributeConstructor(string expression) { var code = @" using System; -[Attr($""{1}"")] +[Attr(" + expression + @")] class Attr : Attribute { public Attr(string s) {} @@ -4084,7 +4268,7 @@ 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}")] + // [Attr($"{1}{2}")] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "Attr").WithArguments("c", "CustomHandler").WithLocation(4, 2) ); VerifyInterpolatedStringExpression(comp); @@ -4093,13 +4277,15 @@ public Attr(CustomHandler c) {} Assert.Equal("Attr..ctor(CustomHandler c)", attr.GetAttributes().Single().AttributeConstructor.ToTestDisplayString()); } - [Fact] - public void StringPreferredOverHandlerConversionForConstant_AttributeConstructor() + [Theory] + [InlineData(@"$""{""Literal""}""")] + [InlineData(@"$""{""Lit""}"" + $""{""eral""}""")] + public void StringPreferredOverHandlerConversionForConstant_AttributeConstructor(string expression) { var code = @" using System; -[Attr($""{""Literal""}"")] +[Attr(" + expression + @")] class Attr : Attribute { public Attr(string s) {} @@ -4117,11 +4303,13 @@ void validate(ModuleSymbol m) } } - [Fact] - public void MultipleBuilderTypes() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void MultipleBuilderTypes(string expression) { var code = @" -C.M($""""); +C.M(" + expression + @"); class C { @@ -4143,13 +4331,15 @@ class C ); } - [Fact] - public void GenericOverloadResolution_01() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericOverloadResolution_01(string expression) { var code = @" using System; -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { @@ -4195,13 +4385,15 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void GenericOverloadResolution_02() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericOverloadResolution_02(string expression) { var code = @" using System; -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { @@ -4247,11 +4439,13 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void GenericOverloadResolution_03() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericOverloadResolution_03(string expression) { var code = @" -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { @@ -4267,12 +4461,14 @@ class C ); } - [Fact] - public void GenericInference_01() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericInference_01(string expression) { var code = @" -C.M($""{1,2:f}Literal"", default(CustomHandler)); -C.M(default(CustomHandler), $""{1,2:f}Literal""); +C.M(" + expression + @", default(CustomHandler)); +C.M(default(CustomHandler), " + expression + @"); class C { @@ -4291,12 +4487,14 @@ class C ); } - [Fact] - public void GenericInference_02() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericInference_02(string expression) { var code = @" using System; -C.M(default(CustomHandler), () => $""{1,2:f}Literal""); +C.M(default(CustomHandler), () => " + expression + @"); class C { @@ -4312,12 +4510,14 @@ class C ); } - [Fact] - public void GenericInference_03() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericInference_03(string expression) { var code = @" using System; -C.M($""{1,2:f}Literal"", default(CustomHandler)); +C.M(" + expression + @", default(CustomHandler)); class C { @@ -4368,12 +4568,14 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void GenericInference_04() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void GenericInference_04(string expression) { var code = @" using System; -C.M(default(CustomHandler), () => $""{1,2:f}Literal""); +C.M(default(CustomHandler), () => " + expression + @"); class C { @@ -4422,12 +4624,14 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void LambdaReturnInference_01() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void LambdaReturnInference_01(string expression) { var code = @" using System; -Func f = () => $""{1,2:f}Literal""; +Func f = () => " + expression + @"; Console.WriteLine(f()); "; @@ -4467,13 +4671,15 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void LambdaReturnInference_02() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void LambdaReturnInference_02(string expression) { var code = @" using System; CultureInfoNormalizer.Normalize(); -C.M(() => $""{1,2:f}Literal""); +C.M(() => " + expression + @"); class C { @@ -4488,7 +4694,7 @@ class C 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()", @" + verifier.VerifyIL(@"$.<>c.<
$>b__0_0()", !expression.Contains('+') ? @" { // Code size 17 (0x11) .maxstack 2 @@ -4498,11 +4704,26 @@ .maxstack 2 IL_000b: call ""string string.Format(string, object)"" IL_0010: ret } +" +: @" +{ + // Code size 27 (0x1b) + .maxstack 2 + IL_0000: ldstr ""{0,2:f}"" + IL_0005: ldc.i4.1 + IL_0006: box ""int"" + IL_000b: call ""string string.Format(string, object)"" + IL_0010: ldstr ""Literal"" + IL_0015: call ""string string.Concat(string, string)"" + IL_001a: ret +} "); } - [Fact] - public void LambdaReturnInference_03() + [Theory] + [InlineData(@"$""{new S { Field = ""Field"" }}""")] + [InlineData(@"$""{new S { Field = ""Field"" }}"" + $""""")] + public void LambdaReturnInference_03(string expression) { // 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 @@ -4510,7 +4731,7 @@ public void LambdaReturnInference_03() var code = @" using System; -C.M(() => $""{new S { Field = ""Field"" }}""); +C.M(() => " + expression + @"); static class C { @@ -4553,14 +4774,16 @@ .locals init (S V_0) "); } - [Fact] - public void LambdaReturnInference_04() + [Theory] + [InlineData(@"$""{new S { Field = ""Field"" }}""")] + [InlineData(@"$""{new S { Field = ""Field"" }}"" + $""""")] + public void LambdaReturnInference_04(string expression) { // 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"" }}""); +C.M(() => " + expression + @"); static class C { @@ -4595,7 +4818,7 @@ public ref partial struct DefaultInterpolatedStringHandler 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), + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, expression).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) @@ -4629,15 +4852,17 @@ .locals init (System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V "); } - [Fact] - public void LambdaReturnInference_05() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void LambdaReturnInference_05(string expression) { var code = @" using System; C.M(b => { if (b) return default(CustomHandler); - else return $""{1,2:f}Literal""; + else return " + expression + @"; }); static class C @@ -4685,8 +4910,10 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void LambdaReturnInference_06() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void LambdaReturnInference_06(string expression) { // 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) @@ -4698,7 +4925,7 @@ public void LambdaReturnInference_06() C.M(b => { if (b) return default(CustomHandler); - else return $""{1,2:f}Literal""; + else return " + expression + @"; }); static class C @@ -4715,7 +4942,7 @@ public partial struct CustomHandler 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)", @" + verifier.VerifyIL(@"$.<>c.<
$>b__0_0(bool)", !expression.Contains('+') ? @" { // Code size 35 (0x23) .maxstack 2 @@ -4733,11 +4960,34 @@ .locals init (CustomHandler V_0) IL_001d: call ""string string.Format(string, object)"" IL_0022: ret } +" +: @" +{ + // Code size 45 (0x2d) + .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}"" + IL_0017: ldc.i4.1 + IL_0018: box ""int"" + IL_001d: call ""string string.Format(string, object)"" + IL_0022: ldstr ""Literal"" + IL_0027: call ""string string.Concat(string, string)"" + IL_002c: ret +} "); } - [Fact] - public void LambdaReturnInference_07() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void LambdaReturnInference_07(string expression) { // Same as 5, but with an implicit conversion from string to the builder type. var code = @" @@ -4745,7 +4995,7 @@ public void LambdaReturnInference_07() C.M(b => { if (b) return default(CustomHandler); - else return $""{1,2:f}Literal""; + else return " + expression + @"; }); static class C @@ -4796,8 +5046,10 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void LambdaReturnInference_08() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void LambdaReturnInference_08(string expression) { // Same as 5, but with an implicit conversion from the builder type to string and from string to the builder type. var code = @" @@ -4805,7 +5057,7 @@ public void LambdaReturnInference_08() C.M(b => { if (b) return default(CustomHandler); - else return $""{1,2:f}Literal""; + else return " + expression + @"; }); static class C @@ -4828,14 +5080,16 @@ public partial struct CustomHandler ); } - [Fact] - public void LambdaInference_AmbiguousInOlderLangVersions() + [Theory] + [InlineData(@"$""{1}""")] + [InlineData(@"$""{1}"" + $""{2}""")] + public void LambdaInference_AmbiguousInOlderLangVersions(string expression) { var code = @" using System; C.M(param => { - param = $""{1}""; + param = " + expression + @"; }); static class C @@ -4860,13 +5114,15 @@ static class C ); } - [Fact] - public void TernaryTypes_01() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void TernaryTypes_01(string expression) { var code = @" using System; -var x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); "; @@ -4912,15 +5168,17 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void TernaryTypes_02() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void TernaryTypes_02(string expression) { // 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""; +var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler @@ -4932,7 +5190,7 @@ public partial struct CustomHandler var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); - verifier.VerifyIL("", @" + verifier.VerifyIL("", !expression.Contains('+') ? @" { // Code size 56 (0x38) .maxstack 2 @@ -4954,17 +5212,44 @@ .locals init (CustomHandler V_0) IL_0032: call ""void System.Console.WriteLine(string)"" IL_0037: ret } +" +: @" +{ + // Code size 66 (0x42) + .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_002e + IL_0012: ldstr ""{0,2:f}"" + IL_0017: ldc.i4.1 + IL_0018: box ""int"" + IL_001d: call ""string string.Format(string, object)"" + IL_0022: ldstr ""Literal"" + IL_0027: call ""string string.Concat(string, string)"" + IL_002c: br.s IL_003c + IL_002e: ldloca.s V_0 + IL_0030: initobj ""CustomHandler"" + IL_0036: ldloc.0 + IL_0037: call ""string CustomHandler.op_Implicit(CustomHandler)"" + IL_003c: call ""void System.Console.WriteLine(string)"" + IL_0041: ret +} "); } - [Fact] - public void TernaryTypes_03() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void TernaryTypes_03(string expression) { // Same as 02, but with a target-type var code = @" using System; -CustomHandler x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +CustomHandler x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler @@ -4977,18 +5262,20 @@ public partial struct CustomHandler 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) + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"(bool)(object)false ? default(CustomHandler) : " + expression).WithArguments("string", "CustomHandler").WithLocation(4, 19) ); } - [Fact] - public void TernaryTypes_04() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void TernaryTypes_04(string expression) { // 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""; +var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler @@ -5038,14 +5325,16 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void TernaryTypes_05() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void TernaryTypes_05(string expression) { // 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""; +var x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler @@ -5059,18 +5348,20 @@ public partial struct CustomHandler 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) + Diagnostic(ErrorCode.ERR_AmbigQM, @"(bool)(object)false ? default(CustomHandler) : " + expression).WithArguments("CustomHandler", "string").WithLocation(4, 9) ); } - [Fact] - public void TernaryTypes_06() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void TernaryTypes_06(string expression) { // Same 05, but with a target type var code = @" using System; -CustomHandler x = (bool)(object)false ? default(CustomHandler) : $""{1,2:f}Literal""; +CustomHandler x = (bool)(object)false ? default(CustomHandler) : " + expression + @"; Console.WriteLine(x); public partial struct CustomHandler @@ -5122,8 +5413,10 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void SwitchTypes_01() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void SwitchTypes_01(string expression) { // 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. @@ -5131,7 +5424,7 @@ public void SwitchTypes_01() var code = @" using System; -var x = (bool)(object)false switch { true => default(CustomHandler), false => $""{1,2:f}Literal"" }; +var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); "; @@ -5143,15 +5436,17 @@ public void SwitchTypes_01() ); } - [Fact] - public void SwitchTypes_02() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void SwitchTypes_02(string expression) { // 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"" }; +var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler @@ -5163,7 +5458,7 @@ public partial struct CustomHandler var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: false) }, targetFramework: TargetFramework.NetCoreApp); var verifier = CompileAndVerifyOnCorrectPlatforms(comp, expectedOutput: @"1.00Literal"); - verifier.VerifyIL("", @" + verifier.VerifyIL("", !expression.Contains('+') ? @" { // Code size 59 (0x3b) .maxstack 2 @@ -5189,17 +5484,48 @@ .locals init (string V_0, IL_0035: call ""void System.Console.WriteLine(string)"" IL_003a: ret } +" +: @" +{ + // Code size 69 (0x45) + .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_003e + IL_0023: ldstr ""{0,2:f}"" + IL_0028: ldc.i4.1 + IL_0029: box ""int"" + IL_002e: call ""string string.Format(string, object)"" + IL_0033: ldstr ""Literal"" + IL_0038: call ""string string.Concat(string, string)"" + IL_003d: stloc.0 + IL_003e: ldloc.0 + IL_003f: call ""void System.Console.WriteLine(string)"" + IL_0044: ret +} "); } - [Fact] - public void SwitchTypes_03() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void SwitchTypes_03(string expression) { // 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"" }; +CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler @@ -5254,14 +5580,16 @@ .locals init (CustomHandler V_0, "); } - [Fact] - public void SwitchTypes_04() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void SwitchTypes_04(string expression) { // 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"" }; +var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler @@ -5316,14 +5644,16 @@ .locals init (CustomHandler V_0, "); } - [Fact] - public void SwitchTypes_05() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void SwitchTypes_05(string expression) { // 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"" }; +var x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler @@ -5341,14 +5671,16 @@ public partial struct CustomHandler ); } - [Fact] - public void SwitchTypes_06() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void SwitchTypes_06(string expression) { // 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"" }; +CustomHandler x = (bool)(object)false switch { true => default(CustomHandler), false => " + expression + @" }; Console.WriteLine(x); public partial struct CustomHandler @@ -5404,11 +5736,13 @@ .locals init (CustomHandler V_0, "); } - [Fact] - public void PassAsRefWithoutKeyword_01() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void PassAsRefWithoutKeyword_01(string expression) { var code = @" -M($""{1,2:f}Literal""); +M(" + expression + @"); void M(ref CustomHandler c) => System.Console.WriteLine(c);"; @@ -5445,12 +5779,14 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void PassAsRefWithoutKeyword_02() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void PassAsRefWithoutKeyword_02(string expression) { var code = @" -M($""{1,2:f}Literal""); -M(ref $""{1,2:f}Literal""); +M(" + expression + @"); +M(ref " + expression + @"); void M(ref CustomHandler c) => System.Console.WriteLine(c);"; @@ -5458,18 +5794,20 @@ public void PassAsRefWithoutKeyword_02() 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), + Diagnostic(ErrorCode.ERR_BadArgRef, expression).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) + Diagnostic(ErrorCode.ERR_RefLvalueExpected, expression).WithLocation(3, 7) ); } - [Fact] - public void PassAsRefWithoutKeyword_03() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void PassAsRefWithoutKeyword_03(string expression) { var code = @" -M($""{1,2:f}Literal""); +M(" + expression + @"); void M(in CustomHandler c) => System.Console.WriteLine(c);"; @@ -5506,11 +5844,13 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void PassAsRefWithoutKeyword_04() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void PassAsRefWithoutKeyword_04(string expression) { var code = @" -M($""{1,2:f}Literal""); +M(" + expression + @"); void M(in CustomHandler c) => System.Console.WriteLine(c);"; @@ -5549,10 +5889,10 @@ .locals init (CustomHandler V_0) [Theory] [CombinatorialData] - public void RefOverloadResolution_Struct([CombinatorialValues("in", "ref")] string refKind) + public void RefOverloadResolution_Struct([CombinatorialValues("in", "ref")] string refKind, [CombinatorialValues(@"$""{1,2:f}Literal""", @"$""{1,2:f}"" + $""Literal""")] string expression) { var code = @" -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { @@ -5595,10 +5935,10 @@ .locals init (CustomHandler V_0) [Theory] [CombinatorialData] - public void RefOverloadResolution_Class([CombinatorialValues("in", "ref")] string refKind) + public void RefOverloadResolution_Class([CombinatorialValues("in", "ref")] string refKind, [CombinatorialValues(@"$""{1,2:f}Literal""", @"$""{1,2:f}"" + $""Literal""")] string expression) { var code = @" -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { @@ -5639,11 +5979,13 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void RefOverloadResolution_MultipleBuilderTypes() + [Theory] + [InlineData(@"$""{1,2:f}Literal""")] + [InlineData(@"$""{1,2:f}"" + $""Literal""")] + public void RefOverloadResolution_MultipleBuilderTypes(string expression) { var code = @" -C.M($""{1,2:f}Literal""); +C.M(" + expression + @"); class C { @@ -5712,13 +6054,15 @@ End Class End Namespace "; - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_NonHandlerType() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_NonHandlerType(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); class C { @@ -5766,13 +6110,15 @@ End Class Assert.True(sParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_InvalidArgument() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_InvalidArgument(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); class C { @@ -5796,13 +6142,15 @@ public static void M([InterpolatedStringHandlerArgumentAttribute(1)] CustomHandl Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_01() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_01(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); class C { @@ -5816,7 +6164,7 @@ public static void M([InterpolatedStringHandlerArgumentAttribute(""NonExistant"" 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -5869,13 +6217,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_02() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_02(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M(1, $""""); +C.M(1, " + expression + @"); class C { @@ -5889,7 +6239,7 @@ public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"", " 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -5942,13 +6292,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_03() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_UnknownName_03(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M(1, $""""); +C.M(1, " + expression + @"); class C { @@ -5962,7 +6314,7 @@ public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""NonExi 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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), @@ -6018,13 +6370,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_ReferenceSelf() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_ReferenceSelf(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M(1, $""""); +C.M(1, " + expression + @"); class C { @@ -6038,7 +6392,7 @@ public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""c"")] 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -6091,13 +6445,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_NullConstant() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_NullConstant(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M(1, $""""); +C.M(1, " + expression + @"); class C { @@ -6111,7 +6467,7 @@ public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(new stri 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -6244,13 +6600,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_ThisOnStaticMethod() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_ThisOnStaticMethod(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); class C { @@ -6264,7 +6622,7 @@ public static void M([InterpolatedStringHandlerArgumentAttribute("""")] CustomHa 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -6319,13 +6677,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_ThisOnConstructor() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_ThisOnConstructor(string expression) { var code = @" using System.Runtime.CompilerServices; -_ = new C($""""); +_ = new C(" + expression + @"); class C { @@ -6339,7 +6699,7 @@ public C([InterpolatedStringHandlerArgumentAttribute("""")] CustomHandler c) {} 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -6394,13 +6754,15 @@ End Structure Assert.True(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerAttributeArgumentError_SubstitutedTypeSymbol() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerAttributeArgumentError_SubstitutedTypeSymbol(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); public class C { @@ -6414,7 +6776,7 @@ public static void M([InterpolatedStringHandlerArgumentAttribute] T t) { } 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentAttributeMalformed, expression).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) @@ -6474,9 +6836,9 @@ End Structure } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttributeWarn_ParameterAfterHandler(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttributeWarn_ParameterAfterHandler([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""text""", @"$""text"" + $""""")] string expression) { var code = @" using System; @@ -6500,7 +6862,7 @@ public CustomHandler(int literalLength, int formattedCount, int i" + extraConstr var goodCode = @" int i = 10; -C.M(i: i, c: $""text""); +C.M(i: i, c: " + expression + @"); "; var comp = CreateCompilation(new[] { code, goodCode, InterpolatedStringHandlerArgumentAttribute, handler }); @@ -6518,13 +6880,13 @@ public CustomHandler(int literalLength, int formattedCount, int i" + extraConstr verifyIL(verifier); - var badCode = @"C.M($"""", 1);"; + var badCode = @"C.M(" + expression + @", 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentLocatedAfterInterpolatedString, "1").WithArguments("i", "c").WithLocation(1, 7 + expression.Length), // (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()); @@ -6634,13 +6996,15 @@ End Structure Assert.False(cParam.HasInterpolatedStringHandlerArgumentError); } - [Fact] - public void InterpolatedStringHandlerArgumentAttributeError_OptionalNotSpecifiedAtCallsite() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_OptionalNotSpecifiedAtCallsite(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); public class C { @@ -6661,20 +7025,22 @@ public CustomHandler(int literalLength, int formattedCount, int i) : this(litera 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, expression).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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttributeError_ParamsNotSpecifiedAtCallsite(string expression) { var code = @" using System.Runtime.CompilerServices; -C.M($""""); +C.M(" + expression + @"); public class C { @@ -6695,15 +7061,17 @@ public CustomHandler(int literalLength, int formattedCount, int[] i) : this(lite 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), + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerArgumentOptionalNotSpecified, expression).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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MissingConstructor(string expression) { var code = @" using System.Runtime.CompilerServices; @@ -6719,13 +7087,13 @@ public static void M(int i, [InterpolatedStringHandlerArgumentAttribute(""i"")] var comp = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); CompileAndVerify(comp, sourceSymbolValidator: validate, symbolValidator: validate).VerifyDiagnostics(); - CreateCompilation(@"C.M(1, $"""");", new[] { comp.ToMetadataReference() }).VerifyDiagnostics( + CreateCompilation(@"C.M(1, " + expression + @");", 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), + Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).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) + Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).WithArguments("CustomHandler", "4").WithLocation(1, 8) ); static void validate(ModuleSymbol module) @@ -6738,8 +7106,10 @@ static void validate(ModuleSymbol module) } } - [Fact] - public void InterpolatedStringHandlerArgumentAttribute_InaccessibleConstructor_01() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_InaccessibleConstructor_01(string expression) { var code = @" using System.Runtime.CompilerServices; @@ -6754,12 +7124,12 @@ private CustomHandler(int literalLength, int formattedCount, int i) : this() {} static void InCustomHandler() { - C.M(1, $""""); + C.M(1, " + expression + @"); } } "; - var executableCode = @"C.M(1, $"""");"; + var executableCode = @"C.M(1, " + expression + @");"; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); @@ -6767,7 +7137,7 @@ static void InCustomHandler() 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) + Diagnostic(ErrorCode.ERR_BadAccess, expression).WithArguments("CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 8) ); var dependency = CreateCompilation(new[] { code, InterpolatedStringHandlerArgumentAttribute, handler }); @@ -6779,17 +7149,17 @@ static void InCustomHandler() 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), + Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).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) + Diagnostic(ErrorCode.ERR_BadCtorArgCount, expression).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) + Diagnostic(ErrorCode.ERR_BadAccess, expression).WithArguments("CustomHandler.CustomHandler(int, int, int)").WithLocation(1, 8) ); static void validate(ModuleSymbol module) @@ -6801,13 +7171,13 @@ static void validate(ModuleSymbol module) } } - private void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(string mRef, string customHandlerRef, params DiagnosticDescription[] expectedDiagnostics) + private void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes(string mRef, string customHandlerRef, string expression, params DiagnosticDescription[] expectedDiagnostics) { var code = @" using System.Runtime.CompilerServices; int i = 0; -C.M(" + mRef + @" i, $""""); +C.M(" + mRef + @" i, " + expression + @"); public class C { @@ -6830,100 +7200,120 @@ public partial struct CustomHandler Assert.Equal(0, cParam.InterpolatedStringHandlerArgumentIndexes.Single()); } - [Fact] - public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefNone() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefNone(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefOut(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "out", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "out", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_RefIn(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "in", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("ref", "in", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InNone(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InOut(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "out", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "out", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_InRef(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "ref", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("in", "ref", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_OutNone(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("out", "", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("out", "", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_OutRef(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("out", "ref", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("out", "ref", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_NoneRef(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("", "ref", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("", "ref", expression, // (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() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes_NoneOut(string expression) { - InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("", "out", + InterpolatedStringHandlerArgumentAttribute_MismatchedRefTypes("", "out", expression, // (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) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_MismatchedType([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; @@ -6941,7 +7331,7 @@ public CustomHandler(int literalLength, int formattedCount, string s" + extraCon } "; - var executableCode = @"C.M(1, $"""");"; + var executableCode = @"C.M(1, " + expression + @");"; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); @@ -6959,7 +7349,7 @@ public CustomHandler(int literalLength, int formattedCount, string s" + extraCon 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) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("success", "CustomHandler.CustomHandler(int, int, string, out bool)").WithLocation(1, 8) }; var comp = CreateCompilation(new[] { code, executableCode, InterpolatedStringHandlerArgumentAttribute, handler }); @@ -6985,9 +7375,9 @@ static void validate(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_SingleArg(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_SingleArg([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""2""", @"$""2"" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; @@ -7011,7 +7401,7 @@ public CustomHandler(int literalLength, int formattedCount, int i" + extraConstr using System; int i = 10; -Console.WriteLine(C.M(i, $""2"")); +Console.WriteLine(C.M(i, " + expression + @")); "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); @@ -7104,9 +7494,9 @@ .locals init (int V_0, } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_MultipleArgs(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_MultipleArgs([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; @@ -7130,7 +7520,7 @@ public CustomHandler(int literalLength, int formattedCount, int i, string s" + e var executableCode = @" int i = 10; string s = ""arg""; -C.M(i, s, $""literal""); +C.M(i, s, " + expression + @"); "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns: true); @@ -7238,9 +7628,9 @@ .locals init (string V_0, //s } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_RefKindsMatch(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_RefKindsMatch([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; @@ -7249,7 +7639,7 @@ public void InterpolatedStringHandlerArgumentAttribute_RefKindsMatch(string extr int i = 1; string s = null; object o; -C.M(i, ref s, out o, $""literal""); +C.M(i, ref s, out o, " + expression + @"); Console.WriteLine(s); Console.WriteLine(o); @@ -7395,15 +7785,15 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_ReorderedAttributePositions(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_ReorderedAttributePositions([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -C.M(GetInt(), GetString(), $""literal""); +C.M(GetInt(), GetString(), " + expression + @"); int GetInt() { @@ -7520,15 +7910,15 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_ParametersReordered(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_ParametersReordered([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -GetC().M(s: GetString(), i: GetInt(), c: $""literal""); +GetC().M(s: GetString(), i: GetInt(), c: " + expression + @"); C GetC() { @@ -7671,15 +8061,15 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_Duplicated(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_Duplicated([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -C.M(GetInt(), """", $""literal""); +C.M(GetInt(), """", " + expression + @"); int GetInt() { @@ -7782,15 +8172,15 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_EmptyWithMatchingConstructor(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_EmptyWithMatchingConstructor([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -C.M(1, """", $""""); +C.M(1, """", " + expression + @"); public class C { @@ -7849,9 +8239,9 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_EmptyWithoutMatchingConstructor(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_EmptyWithoutMatchingConstructor([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; @@ -7873,25 +8263,25 @@ public CustomHandler(int literalLength, int formattedCount, int i" + extraConstr // 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( + CreateCompilation(@"C.M(1, """", " + expression + @");", 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), + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).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) + Diagnostic(ErrorCode.ERR_BadArgExtraRef, expression).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), + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).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) + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, expression).WithArguments("success", "CustomHandler.CustomHandler(int, int, int, out bool)").WithLocation(1, 12) } ); @@ -7905,16 +8295,16 @@ static void validate(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_OnIndexerRvalue(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_OnIndexerRvalue([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; var c = new C(); -Console.WriteLine(c[10, ""str"", $""literal""]); +Console.WriteLine(c[10, ""str"", " + expression + @"]); public class C { @@ -8021,16 +8411,16 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_OnIndexerLvalue(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_OnIndexerLvalue([CombinatorialValues("", ", out bool success")] string extraConstructorArg, + [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; var c = new C(); -c[10, ""str"", $""literal""] = """"; +c[10, ""str"", " + expression + @"] = """"; public class C { @@ -8137,15 +8527,14 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void InterpolatedStringHandlerArgumentAttribute_ThisParameter(string extraConstructorArg) + [CombinatorialData] + public void InterpolatedStringHandlerArgumentAttribute_ThisParameter([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -(new C(5)).M((int)10, ""str"", $""literal""); +(new C(5)).M((int)10, ""str"", " + expression + @"); public class C { @@ -8262,15 +8651,17 @@ static void validator(ModuleSymbol module) } } - [Fact] - public void InterpolatedStringHandlerArgumentAttribute_OnConstructor() + [Theory] + [InlineData(@"$""literal""")] + [InlineData(@"$"""" + $""literal""")] + public void InterpolatedStringHandlerArgumentAttribute_OnConstructor(string expression) { var code = @" using System; using System.Runtime.CompilerServices; -_ = new C(5, $""literal""); +_ = new C(5, " + expression + @"); public class C { @@ -8323,16 +8714,15 @@ .locals init (int V_0, } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void RefReturningMethodAsReceiver_Success(string extraConstructorArg) + [CombinatorialData] + public void RefReturningMethodAsReceiver_Success([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C c = new C(1); -GetC(ref c).M($""literal""); +GetC(ref c).M(" + expression + @"); Console.WriteLine(c.I); ref C GetC(ref C c) @@ -8455,16 +8845,15 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("")] - [InlineData(", out bool success")] - public void RefReturningMethodAsReceiver_Success_StructReceiver(string extraConstructorArg) + [CombinatorialData] + public void RefReturningMethodAsReceiver_Success_StructReceiver([CombinatorialValues("", ", out bool success")] string extraConstructorArg, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; C c = new C(1); -GetC(ref c).M($""literal""); +GetC(ref c).M(" + expression + @"); Console.WriteLine(c.I); ref C GetC(ref C c) @@ -8585,15 +8974,14 @@ static void validator(ModuleSymbol module) } [Theory] - [InlineData("ref readonly")] - [InlineData("")] - public void RefReturningMethodAsReceiver_MismatchedRefness_01(string refness) + [CombinatorialData] + public void RefReturningMethodAsReceiver_MismatchedRefness_01([CombinatorialValues("ref readonly", "")] string refness, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; C c = new C(1); -GetC().M($""literal""); +GetC().M(" + expression + @"); " + refness + @" C GetC() => throw null; @@ -8615,20 +9003,19 @@ public CustomHandler(int literalLength, int formattedCount, ref C c) : this(lite 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) + Diagnostic(ErrorCode.ERR_BadArgRef, expression).WithArguments("3", "ref").WithLocation(5, 10) ); } [Theory] - [InlineData("in")] - [InlineData("")] - public void RefReturningMethodAsReceiver_MismatchedRefness_02(string refness) + [CombinatorialData] + public void RefReturningMethodAsReceiver_MismatchedRefness_02([CombinatorialValues("in", "")] string refness, [CombinatorialValues(@"$""literal""", @"$""literal"" + $""""")] string expression) { var code = @" using System.Runtime.CompilerServices; C c = new C(1); -GetC(ref c).M($""literal""); +GetC(ref c).M(" + expression + @"); ref C GetC(ref C c) => ref c; @@ -8650,12 +9037,14 @@ public CustomHandler(int literalLength, int formattedCount," + refness + @" C c) 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) + Diagnostic(ErrorCode.ERR_BadArgExtraRef, expression).WithArguments("3", "ref").WithLocation(5, 15) ); } - [Fact] - public void StructReceiver_Rvalue() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void StructReceiver_Rvalue(string expression) { var code = @" using System; @@ -8664,7 +9053,7 @@ public void StructReceiver_Rvalue() S s1 = new S { I = 1 }; S s2 = new S { I = 2 }; -s1.M(s2, $""""); +s1.M(s2, " + expression + @"); public struct S { @@ -8732,8 +9121,10 @@ .locals init (S V_0, //s2 "); } - [Fact] - public void StructReceiver_Lvalue() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void StructReceiver_Lvalue(string expression) { var code = @" using System; @@ -8742,7 +9133,7 @@ public void StructReceiver_Lvalue() S s1 = new S { I = 1 }; S s2 = new S { I = 2 }; -s1.M(ref s2, $""""); +s1.M(ref s2, " + expression + @"); public struct S { @@ -8770,12 +9161,14 @@ public CustomHandler(int literalLength, int formattedCount, ref S s1, ref S s2) 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) + Diagnostic(ErrorCode.ERR_BadArgRef, expression).WithArguments("3", "ref").WithLocation(8, 14) ); } - [Fact] - public void StructParameter_ByVal() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void StructParameter_ByVal(string expression) { var code = @" using System; @@ -8783,7 +9176,7 @@ public void StructParameter_ByVal() S s = new S { I = 1 }; -S.M(s, $""""); +S.M(s, " + expression + @"); public struct S { @@ -8834,8 +9227,10 @@ .locals init (S V_0) "); } - [Fact] - public void StructParameter_ByRef() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void StructParameter_ByRef(string expression) { var code = @" using System; @@ -8843,7 +9238,7 @@ public void StructParameter_ByRef() S s = new S { I = 1 }; -S.M(ref s, $""""); +S.M(ref s, " + expression + @"); public struct S { @@ -8900,7 +9295,7 @@ .locals init (S V_0, //s [Theory] [CombinatorialData] - public void SideEffects(bool useBoolReturns, bool validityParameter) + public void SideEffects(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal""", @"$"""" + $""literal""")] string expression) { var code = @" using System; @@ -8911,7 +9306,7 @@ public void SideEffects(bool useBoolReturns, bool validityParameter) GetArg(""Second value""), GetArg(""Unrelated parameter 2""), GetArg(""First value""), - $""literal"", + " + expression + @", GetArg(""Unrelated parameter 4"")); C GetReceiver() @@ -8965,8 +9360,10 @@ Unrelated parameter 4 verifier.VerifyDiagnostics(); } - [Fact] - public void InterpolatedStringHandlerArgumentsAttribute_ConversionFromArgumentType() + [Theory] + [InlineData(@"$""literal""")] + [InlineData(@"$""literal"" + $""""")] + public void InterpolatedStringHandlerArgumentsAttribute_ConversionFromArgumentType(string expression) { var code = @" using System; @@ -8974,7 +9371,7 @@ public void InterpolatedStringHandlerArgumentsAttribute_ConversionFromArgumentTy using System.Runtime.CompilerServices; int i = 1; -C.M(i, $""literal""); +C.M(i, " + expression + @"); public class C { @@ -9041,14 +9438,14 @@ static void validator(ModuleSymbol module) [Theory] [CombinatorialData] - public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_01(bool useBoolReturns, bool validityParameter) + public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_01(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal{i}""", @"$""literal"" + $""{i}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 3; -GetC()[GetInt(1), $""literal{i}""] += GetInt(2); +GetC()[GetInt(1), " + expression + @"] += GetInt(2); static C GetC() { @@ -9340,14 +9737,14 @@ .locals init (int V_0, //i [Theory] [CombinatorialData] - public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_02(bool useBoolReturns, bool validityParameter) + public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_Indexer_02(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal{i}""", @"$""literal"" + $""{i}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 3; -GetC()[GetInt(1), $""literal{i}""] += GetInt(2); +GetC()[GetInt(1), " + expression + @"] += GetInt(2); static C GetC() { @@ -9608,14 +10005,14 @@ .locals init (int V_0, //i [Theory] [CombinatorialData] - public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_RefReturningMethod(bool useBoolReturns, bool validityParameter) + public void InterpolatedStringHandlerArgumentsAttribute_CompoundAssignment_RefReturningMethod(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""literal{i}""", @"$""literal"" + $""{i}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 3; -GetC().M(GetInt(1), $""literal{i}"") += GetInt(2); +GetC().M(GetInt(1), " + expression + @") += GetInt(2); static C GetC() { @@ -9871,8 +10268,10 @@ .locals init (int V_0, //i }; } - [Fact] - public void InterpolatedStringHandlerArgumentsAttribute_CollectionInitializerAdd() + [Theory] + [InlineData(@"$""literal""")] + [InlineData(@"$"""" + $""literal""")] + public void InterpolatedStringHandlerArgumentsAttribute_CollectionInitializerAdd(string expression) { var code = @" using System; @@ -9880,7 +10279,7 @@ public void InterpolatedStringHandlerArgumentsAttribute_CollectionInitializerAdd using System.Collections.Generic; using System.Runtime.CompilerServices; -_ = new C(1) { $""literal"" }; +_ = new C(1) { " + expression + @" }; public class C : IEnumerable { @@ -9945,13 +10344,14 @@ .locals init (C V_0, [Theory] [CombinatorialData] - public void InterpolatedStringHandlerArgumentAttribute_AttributeOnAppendFormatCall(bool useBoolReturns, bool validityParameter) + public void InterpolatedStringHandlerArgumentAttribute_AttributeOnAppendFormatCall(bool useBoolReturns, bool validityParameter, + [CombinatorialValues(@"$""{$""Inner string""}{2}""", @"$""{$""Inner string""}"" + $""{2}""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -C.M(1, $""{$""Inner string""}{2}""); +C.M(1, " + expression + @"); class C { @@ -10196,13 +10596,15 @@ .locals init (int V_0, }; } - [Fact] - public void DiscardsUsedAsParameters() + [Theory] + [InlineData(@"$""literal""")] + [InlineData(@"$"""" + $""literal""")] + public void DiscardsUsedAsParameters(string expression) { var code = @" using System; using System.Runtime.CompilerServices; -C.M(out _, $""literal""); +C.M(out _, " + expression + @"); public class C { @@ -10296,14 +10698,16 @@ .locals init (int V_0) "); } - [Fact] - public void DisallowedInExpressionTrees() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void DisallowedInExpressionTrees(string expression) { var code = @" using System; using System.Linq.Expressions; -Expression> expr = () => $""""; +Expression> expr = () => " + expression + @"; "; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false); @@ -10312,7 +10716,7 @@ public void DisallowedInExpressionTrees() 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) + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsInterpolatedStringHandlerConversion, expression).WithLocation(5, 46) ); } @@ -10559,15 +10963,83 @@ .locals init (System.Linq.Expressions.ParameterExpression V_0) "); } + [Fact, WorkItem(55114, "https://github.com/dotnet/roslyn/issues/55114")] + public void AsStringInExpressionTrees_05() + { + var code = @" +using System; +using System.Linq.Expressions; + +Expression> e = o => $""{o.Length}"" + $""literal"";"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); + var verifier = CompileAndVerify(comp); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", @" +{ + // Code size 167 (0xa7) + .maxstack 7 + .locals init (System.Linq.Expressions.ParameterExpression V_0) + IL_0000: ldtoken ""string"" + IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_000a: ldstr ""o"" + IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" + IL_0014: stloc.0 + IL_0015: ldnull + IL_0016: ldtoken ""string string.Format(string, object)"" + IL_001b: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_0020: castclass ""System.Reflection.MethodInfo"" + IL_0025: ldc.i4.2 + IL_0026: newarr ""System.Linq.Expressions.Expression"" + IL_002b: dup + IL_002c: ldc.i4.0 + IL_002d: ldstr ""{0}"" + IL_0032: ldtoken ""string"" + IL_0037: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_003c: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0041: stelem.ref + IL_0042: dup + IL_0043: ldc.i4.1 + IL_0044: ldloc.0 + IL_0045: ldtoken ""int string.Length.get"" + IL_004a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_004f: castclass ""System.Reflection.MethodInfo"" + IL_0054: call ""System.Linq.Expressions.MemberExpression System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" + IL_0059: ldtoken ""object"" + IL_005e: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0063: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" + IL_0068: stelem.ref + IL_0069: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" + IL_006e: ldstr ""literal"" + IL_0073: ldtoken ""string"" + IL_0078: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_007d: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0082: ldtoken ""string string.Concat(string, string)"" + IL_0087: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_008c: castclass ""System.Reflection.MethodInfo"" + IL_0091: call ""System.Linq.Expressions.BinaryExpression System.Linq.Expressions.Expression.Add(System.Linq.Expressions.Expression, System.Linq.Expressions.Expression, System.Reflection.MethodInfo)"" + IL_0096: ldc.i4.1 + IL_0097: newarr ""System.Linq.Expressions.ParameterExpression"" + IL_009c: dup + IL_009d: ldc.i4.0 + IL_009e: ldloc.0 + IL_009f: stelem.ref + IL_00a0: call ""System.Linq.Expressions.Expression> System.Linq.Expressions.Expression.Lambda>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" + IL_00a5: pop + IL_00a6: ret +} +"); + } + [Theory] [CombinatorialData] - public void CustomHandlerUsedAsArgumentToCustomHandler(bool useBoolReturns, bool validityParameter) + public void CustomHandlerUsedAsArgumentToCustomHandler(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { var code = @" using System; using System.Runtime.CompilerServices; -C.M(1, $"""", $""""); +C.M(1, " + expression + @", " + expression + @"); public class C { @@ -10702,13 +11174,14 @@ .locals init (int V_0, [Theory] [CombinatorialData] - public void DefiniteAssignment_01(bool useBoolReturns, bool trailingOutParameter) + public void DefiniteAssignment_01(bool useBoolReturns, bool trailingOutParameter, + [CombinatorialValues(@"$""{i = 1}{M(out var o)}{s = o.ToString()}""", @"$""{i = 1}"" + $""{M(out var o)}"" + $""{s = o.ToString()}""")] string expression) { var code = @" int i; string s; -CustomHandler c = $""{i = 1}{M(out var o)}{s = o.ToString()}""; +CustomHandler c = " + expression + @"; _ = i.ToString(); _ = o.ToString(); _ = s.ToString(); @@ -10756,12 +11229,12 @@ string M(out object o) [Theory] [CombinatorialData] - public void DefiniteAssignment_02(bool useBoolReturns, bool trailingOutParameter) + public void DefiniteAssignment_02(bool useBoolReturns, bool trailingOutParameter, [CombinatorialValues(@"$""{i = 1}""", @"$"""" + $""{i = 1}""", @"$""{i = 1}"" + $""""")] string expression) { var code = @" int i; -CustomHandler c = $""{i = 1}""; +CustomHandler c = " + expression + @"; _ = i.ToString(); "; @@ -10782,13 +11255,15 @@ public void DefiniteAssignment_02(bool useBoolReturns, bool trailingOutParameter } } - [Fact] - public void DynamicConstruction_01() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void DynamicConstruction_01(string expression) { var code = @" using System.Runtime.CompilerServices; dynamic d = 1; -M(d, $""""); +M(d, " + expression + @"); void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} @@ -10804,17 +11279,19 @@ public CustomHandler(int literalLength, int formattedCount, dynamic d) : this() 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) + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, expression).WithArguments("CustomHandler").WithLocation(4, 6) ); } - [Fact] - public void DynamicConstruction_02() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void DynamicConstruction_02(string expression) { var code = @" using System.Runtime.CompilerServices; int i = 1; -M(i, $""""); +M(i, " + expression + @"); void M(dynamic d, [InterpolatedStringHandlerArgument(""d"")]CustomHandler c) {} @@ -10830,18 +11307,20 @@ public CustomHandler(int literalLength, int formattedCount, dynamic d) : this() 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) + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerCreationCannotUseDynamic, expression).WithArguments("CustomHandler").WithLocation(4, 6) ); } - [Fact] - public void DynamicConstruction_03() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void DynamicConstruction_03(string expression) { var code = @" using System; using System.Runtime.CompilerServices; int i = 1; -M(i, $""""); +M(i, " + expression + @"); void M(int i, [InterpolatedStringHandlerArgument(""i"")]CustomHandler c) {} @@ -10879,13 +11358,15 @@ .locals init (int V_0) "); } - [Fact] - public void DynamicConstruction_04() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void DynamicConstruction_04(string expression) { var code = @" using System; using System.Runtime.CompilerServices; -M($""""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -10917,13 +11398,15 @@ .maxstack 2 "); } - [Fact] - public void DynamicConstruction_05() + [Theory] + [InlineData(@"$""""")] + [InlineData(@"$"""" + $""""")] + public void DynamicConstruction_05(string expression) { var code = @" using System; using System.Runtime.CompilerServices; -M($""""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -10959,13 +11442,15 @@ .maxstack 2 "); } - [Fact] - public void DynamicConstruction_06() + [Theory] + [InlineData(@"$""Literal""")] + [InlineData(@"$"""" + $""Literal""")] + public void DynamicConstruction_06(string expression) { var code = @" using System; using System.Runtime.CompilerServices; -M($""Literal""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -11006,13 +11491,15 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void DynamicConstruction_07() + [Theory] + [InlineData(@"$""{1}""")] + [InlineData(@"$""{1}"" + $""""")] + public void DynamicConstruction_07(string expression) { var code = @" using System; using System.Runtime.CompilerServices; -M($""{1}""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -11054,14 +11541,16 @@ .locals init (CustomHandler V_0) "); } - [Fact] - public void DynamicConstruction_08() + [Theory] + [InlineData(@"$""literal{d}""")] + [InlineData(@"$""literal"" + $""{d}""")] + public void DynamicConstruction_08(string expression) { var code = @" using System; using System.Runtime.CompilerServices; dynamic d = 1; -M($""literal{d}""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -11106,7 +11595,7 @@ .locals init (object V_0, //d 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_001c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> $.<>o__0.<>p__0"" IL_0021: brtrue.s IL_0062 IL_0023: ldc.i4 0x100 IL_0028: ldstr ""AppendFormatted"" @@ -11128,14 +11617,14 @@ .locals init (object V_0, //d 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_0058: call ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_005d: stsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> $.<>o__0.<>p__0"" + IL_0062: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> $.<>o__0.<>p__0"" + IL_0067: ldfld ""<>A{00000004} System.Runtime.CompilerServices.CallSite<<>A{00000004}>.Target"" + IL_006c: ldsfld ""System.Runtime.CompilerServices.CallSite<<>A{00000004}> $.<>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_0074: callvirt ""void <>A{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, ref CustomHandler, dynamic)"" IL_0079: ldloc.1 IL_007a: call ""void $.<
$>g__M|0_0(CustomHandler)"" IL_007f: ret @@ -11143,14 +11632,16 @@ .locals init (object V_0, //d "); } - [Fact] - public void DynamicConstruction_09() + [Theory] + [InlineData(@"$""literal{d}""")] + [InlineData(@"$""literal"" + $""{d}""")] + public void DynamicConstruction_09(string expression) { var code = @" using System; using System.Runtime.CompilerServices; dynamic d = 1; -M($""literal{d}""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -11211,7 +11702,7 @@ .locals init (object V_0, //d 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_005b: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> $.<>o__0.<>p__0"" IL_0060: brtrue.s IL_009d IL_0062: ldc.i4.0 IL_0063: ldstr ""AppendFormatted"" @@ -11233,14 +11724,14 @@ .locals init (object V_0, //d 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_0093: call ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0098: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> $.<>o__0.<>p__0"" + IL_009d: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> $.<>o__0.<>p__0"" + IL_00a2: ldfld ""<>F{00000004} System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Target"" + IL_00a7: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> $.<>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_00af: callvirt ""dynamic <>F{00000004}.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 @@ -11252,8 +11743,10 @@ .locals init (object V_0, //d "); } - [Fact] - public void RefEscape_01() + [Theory] + [InlineData(@"$""{s}""")] + [InlineData(@"$""{s}"" + $""""")] + public void RefEscape_01(string expression) { var code = @" using System; @@ -11271,7 +11764,7 @@ public CustomHandler(int literalLength, int formattedCount) : this() {} public static CustomHandler M() { Span s = stackalloc char[10]; - return $""{s}""; + return " + expression + @"; } } "; @@ -11284,8 +11777,10 @@ public static CustomHandler M() ); } - [Fact] - public void RefEscape_02() + [Theory] + [InlineData(@"$""{s}""")] + [InlineData(@"$""{s}"" + $""""")] + public void RefEscape_02(string expression) { var code = @" using System; @@ -11303,7 +11798,7 @@ public CustomHandler(int literalLength, int formattedCount) : this() {} public static ref CustomHandler M() { Span s = stackalloc char[10]; - return $""{s}""; + return " + expression + @"; } } "; @@ -11316,8 +11811,10 @@ public static ref CustomHandler M() ); } - [Fact] - public void RefEscape_03() + [Theory] + [InlineData(@"$""{s}""")] + [InlineData(@"$""{s}"" + $""""")] + public void RefEscape_03(string expression) { var code = @" using System; @@ -11335,7 +11832,7 @@ public CustomHandler(int literalLength, int formattedCount) : this() {} public static ref CustomHandler M() { Span s = stackalloc char[10]; - return ref $""{s}""; + return ref " + expression + @"; } } "; @@ -11344,12 +11841,14 @@ public static ref CustomHandler M() 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) + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, expression).WithLocation(17, 20) ); } - [Fact] - public void RefEscape_04() + [Theory] + [InlineData(@"$""{s}""")] + [InlineData(@"$""{s}"" + $""""")] + public void RefEscape_04(string expression) { var code = @" using System; @@ -11367,7 +11866,7 @@ public ref struct CustomHandler public static void M(ref S1 s1) { Span s = stackalloc char[10]; - M2(ref s1, $""{s}""); + M2(ref s1, " + expression + @"); } public static void M2(ref S1 s1, [InterpolatedStringHandlerArgument(""s1"")] ref CustomHandler handler) {} @@ -11383,15 +11882,17 @@ public ref struct S1 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), + Diagnostic(ErrorCode.ERR_CallArgMixing, @"M2(ref s1, " + expression + @")").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() + [Theory] + [InlineData(@"$""{s1}""")] + [InlineData(@"$""{s1}"" + $""""")] + public void RefEscape_05(string expression) { var code = @" using System; @@ -11409,7 +11910,7 @@ public ref struct CustomHandler public static void M(ref S1 s1) { Span s = stackalloc char[10]; - M2(ref s, $""{s1}""); + M2(ref s, " + expression + @"); } public static void M2(ref Span s, [InterpolatedStringHandlerArgument(""s"")] CustomHandler handler) {} @@ -11425,8 +11926,10 @@ public ref struct S1 comp.VerifyDiagnostics(); } - [Fact] - public void RefEscape_06() + [Theory] + [InlineData(@"$""{s2}""")] + [InlineData(@"$""{s2}"" + $""""")] + public void RefEscape_06(string expression) { var code = @" using System; @@ -11434,7 +11937,7 @@ public void RefEscape_06() Span s = stackalloc char[5]; Span s2 = stackalloc char[10]; -s.TryWrite($""{s2}""); +s.TryWrite(" + expression + @"); public static class MemoryExtensions { @@ -11454,8 +11957,10 @@ public CustomHandler(int literalLength, int formattedCount, Span s) : this comp.VerifyDiagnostics(); } - [Fact] - public void RefEscape_07() + [Theory] + [InlineData(@"$""{s2}""")] + [InlineData(@"$""{s2}"" + $""""")] + public void RefEscape_07(string expression) { var code = @" using System; @@ -11463,7 +11968,7 @@ public void RefEscape_07() Span s = stackalloc char[5]; Span s2 = stackalloc char[10]; -s.TryWrite($""{s2}""); +s.TryWrite(" + expression + @"); public static class MemoryExtensions { @@ -11483,12 +11988,14 @@ public CustomHandler(int literalLength, int formattedCount, Span s) : this comp.VerifyDiagnostics(); } - [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] - public void BracesAreEscaped_01() + [Theory, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + [InlineData(@"$""{{ {i} }}""")] + [InlineData(@"$""{{ "" + $""{i}"" + $"" }}""")] + public void BracesAreEscaped_01(string expression) { var code = @" int i = 1; -System.Console.WriteLine($""{{ {i} }}"");"; +System.Console.WriteLine(" + expression + @");"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); @@ -11526,12 +12033,14 @@ .locals init (int V_0, //i "); } - [Fact, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] - public void BracesAreEscaped_02() + [Theory, WorkItem(54703, "https://github.com/dotnet/roslyn/issues/54703")] + [InlineData(@"$""{{ {i} }}""")] + [InlineData(@"$""{{ "" + $""{i}"" + $"" }}""")] + public void BracesAreEscaped_02(string expression) { var code = @" int i = 1; -CustomHandler c = $""{{ {i} }}""; +CustomHandler c = " + expression + @"; System.Console.WriteLine(c.ToString());"; var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); @@ -11576,6 +12085,313 @@ .locals init (int V_0, //i IL_0041: call ""void System.Console.WriteLine(string)"" IL_0046: ret } +"); + } + + [Fact] + public void InterpolatedStringsAddedUnderObjectAddition() + { + var code = @" +int i1 = 1; +int i2 = 2; +int i3 = 3; +int i4 = 4; +System.Console.WriteLine($""{i1}"" + $""{i2}"" + $""{i3}"" + i4);"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +value:2 +value:3 +4 +"); + + verifier.VerifyIL("", @" +{ + // Code size 66 (0x42) + .maxstack 3 + .locals init (int V_0, //i1 + int V_1, //i2 + int V_2, //i3 + int V_3, //i4 + System.Runtime.CompilerServices.DefaultInterpolatedStringHandler V_4) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.2 + IL_0003: stloc.1 + IL_0004: ldc.i4.3 + IL_0005: stloc.2 + IL_0006: ldc.i4.4 + IL_0007: stloc.3 + IL_0008: ldloca.s V_4 + IL_000a: ldc.i4.0 + IL_000b: ldc.i4.3 + IL_000c: call ""System.Runtime.CompilerServices.DefaultInterpolatedStringHandler..ctor(int, int)"" + IL_0011: ldloca.s V_4 + IL_0013: ldloc.0 + IL_0014: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0019: ldloca.s V_4 + IL_001b: ldloc.1 + IL_001c: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0021: ldloca.s V_4 + IL_0023: ldloc.2 + IL_0024: call ""void System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.AppendFormatted(int)"" + IL_0029: ldloca.s V_4 + IL_002b: call ""string System.Runtime.CompilerServices.DefaultInterpolatedStringHandler.ToStringAndClear()"" + IL_0030: ldloca.s V_3 + IL_0032: call ""string int.ToString()"" + IL_0037: call ""string string.Concat(string, string)"" + IL_003c: call ""void System.Console.WriteLine(string)"" + IL_0041: ret +} +"); + } + + [Fact] + public void InterpolatedStringsAddedUnderObjectAddition_DefiniteAssignment() + { + var code = @" +object o1; +object o2; +object o3; +_ = $""{o1 = null}"" + $""{o2 = null}"" + $""{o3 = null}"" + 1; +o1.ToString(); +o2.ToString(); +o3.ToString(); +"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringHandlerDefinition(includeSpanOverloads: false, useDefaultParameters: false, useBoolReturns: true) }); + comp.VerifyDiagnostics( + // (7,1): error CS0165: Use of unassigned local variable 'o2' + // o2.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "o2").WithArguments("o2").WithLocation(7, 1), + // (8,1): error CS0165: Use of unassigned local variable 'o3' + // o3.ToString(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "o3").WithArguments("o3").WithLocation(8, 1) + ); + } + + [Fact] + public void ParenthesizedAdditiveExpression_01() + { + var code = @" +int i1 = 1; +int i2 = 2; +int i3 = 3; + +CustomHandler c = ($""{i1}"" + $""{i2}"") + $""{i3}""; +System.Console.WriteLine(c.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:0 +format: +value:2 +alignment:0 +format: +value:3 +alignment:0 +format: +"); + + verifier.VerifyIL("", @" +{ + // Code size 82 (0x52) + .maxstack 4 + .locals init (int V_0, //i1 + int V_1, //i2 + int V_2, //i3 + CustomHandler V_3, //c + CustomHandler V_4) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.2 + IL_0003: stloc.1 + IL_0004: ldc.i4.3 + IL_0005: stloc.2 + IL_0006: ldloca.s V_4 + IL_0008: ldc.i4.0 + IL_0009: ldc.i4.3 + IL_000a: call ""CustomHandler..ctor(int, int)"" + IL_000f: ldloca.s V_4 + IL_0011: ldloc.0 + IL_0012: box ""int"" + IL_0017: ldc.i4.0 + IL_0018: ldnull + IL_0019: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_001e: ldloca.s V_4 + IL_0020: ldloc.1 + IL_0021: box ""int"" + IL_0026: ldc.i4.0 + IL_0027: ldnull + IL_0028: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_002d: ldloca.s V_4 + IL_002f: ldloc.2 + IL_0030: box ""int"" + IL_0035: ldc.i4.0 + IL_0036: ldnull + IL_0037: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_003c: ldloc.s V_4 + IL_003e: stloc.3 + IL_003f: ldloca.s V_3 + IL_0041: constrained. ""CustomHandler"" + IL_0047: callvirt ""string object.ToString()"" + IL_004c: call ""void System.Console.WriteLine(string)"" + IL_0051: ret +}"); + } + + [Fact] + public void ParenthesizedAdditiveExpression_02() + { + var code = @" +int i1 = 1; +int i2 = 2; +int i3 = 3; + +CustomHandler c = $""{i1}"" + ($""{i2}"" + $""{i3}""); +System.Console.WriteLine(c.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + comp.VerifyDiagnostics( + // (6,19): error CS0029: Cannot implicitly convert type 'string' to 'CustomHandler' + // CustomHandler c = $"{i1}" + ($"{i2}" + $"{i3}"); + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"$""{i1}"" + ($""{i2}"" + $""{i3}"")").WithArguments("string", "CustomHandler").WithLocation(6, 19) + ); + } + + [Theory] + [InlineData(@"$""{1}"", $""{2}""")] + [InlineData(@"$""{1}"" + $"""", $""{2}"" + $""""")] + public void TupleDeclaration_01(string initializer) + { + var code = @" +(CustomHandler c1, CustomHandler c2) = (" + initializer + @"); +System.Console.Write(c1.ToString()); +System.Console.WriteLine(c2.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:0 +format: +value:2 +alignment:0 +format: +"); + + verifier.VerifyIL("", @" +{ + // Code size 91 (0x5b) + .maxstack 4 + .locals init (CustomHandler V_0, //c1 + CustomHandler V_1, //c2 + CustomHandler V_2, + CustomHandler V_3) + IL_0000: ldloca.s V_3 + IL_0002: ldc.i4.0 + IL_0003: ldc.i4.1 + IL_0004: call ""CustomHandler..ctor(int, int)"" + IL_0009: ldloca.s V_3 + IL_000b: ldc.i4.1 + IL_000c: box ""int"" + IL_0011: ldc.i4.0 + IL_0012: ldnull + IL_0013: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0018: ldloc.3 + IL_0019: stloc.2 + IL_001a: ldloca.s V_3 + IL_001c: ldc.i4.0 + IL_001d: ldc.i4.1 + IL_001e: call ""CustomHandler..ctor(int, int)"" + IL_0023: ldloca.s V_3 + IL_0025: ldc.i4.2 + IL_0026: box ""int"" + IL_002b: ldc.i4.0 + IL_002c: ldnull + IL_002d: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0032: ldloc.3 + IL_0033: ldloc.2 + IL_0034: stloc.0 + IL_0035: stloc.1 + IL_0036: ldloca.s V_0 + IL_0038: constrained. ""CustomHandler"" + IL_003e: callvirt ""string object.ToString()"" + IL_0043: call ""void System.Console.Write(string)"" + IL_0048: ldloca.s V_1 + IL_004a: constrained. ""CustomHandler"" + IL_0050: callvirt ""string object.ToString()"" + IL_0055: call ""void System.Console.WriteLine(string)"" + IL_005a: ret +} +"); + } + + [Theory] + [InlineData(@"$""{1}"", $""{2}""")] + [InlineData(@"$""{1}"" + $"""", $""{2}"" + $""""")] + public void TupleDeclaration_02(string initializer) + { + var code = @" +(CustomHandler c1, CustomHandler c2) t = (" + initializer + @"); +System.Console.Write(t.c1.ToString()); +System.Console.WriteLine(t.c2.ToString());"; + + var comp = CreateCompilation(new[] { code, GetInterpolatedStringCustomHandlerType("CustomHandler", "struct", useBoolReturns: false) }); + var verifier = CompileAndVerify(comp, expectedOutput: @" +value:1 +alignment:0 +format: +value:2 +alignment:0 +format: +"); + + verifier.VerifyIL("", @" +{ + // Code size 104 (0x68) + .maxstack 6 + .locals init (System.ValueTuple V_0, //t + CustomHandler V_1) + IL_0000: ldloca.s V_0 + IL_0002: ldloca.s V_1 + IL_0004: ldc.i4.0 + IL_0005: ldc.i4.1 + IL_0006: call ""CustomHandler..ctor(int, int)"" + IL_000b: ldloca.s V_1 + IL_000d: ldc.i4.1 + IL_000e: box ""int"" + IL_0013: ldc.i4.0 + IL_0014: ldnull + IL_0015: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_001a: ldloc.1 + IL_001b: ldloca.s V_1 + IL_001d: ldc.i4.0 + IL_001e: ldc.i4.1 + IL_001f: call ""CustomHandler..ctor(int, int)"" + IL_0024: ldloca.s V_1 + IL_0026: ldc.i4.2 + IL_0027: box ""int"" + IL_002c: ldc.i4.0 + IL_002d: ldnull + IL_002e: call ""void CustomHandler.AppendFormatted(object, int, string)"" + IL_0033: ldloc.1 + IL_0034: call ""System.ValueTuple..ctor(CustomHandler, CustomHandler)"" + IL_0039: ldloca.s V_0 + IL_003b: ldflda ""CustomHandler System.ValueTuple.Item1"" + IL_0040: constrained. ""CustomHandler"" + IL_0046: callvirt ""string object.ToString()"" + IL_004b: call ""void System.Console.Write(string)"" + IL_0050: ldloca.s V_0 + IL_0052: ldflda ""CustomHandler System.ValueTuple.Item2"" + IL_0057: constrained. ""CustomHandler"" + IL_005d: callvirt ""string object.ToString()"" + IL_0062: call ""void System.Console.WriteLine(string)"" + IL_0067: ret +} "); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs index f7a9f8b630490..24dbeed579f19 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LambdaTests.cs @@ -4690,6 +4690,7 @@ static void Main() Diagnostic(ErrorCode.ERR_NoVoidHere, "void").WithLocation(7, 18)); } + [WorkItem(55217, "https://github.com/dotnet/roslyn/issues/55217")] [ConditionalFact(typeof(DesktopOnly))] public void LambdaReturnType_12() { @@ -4814,9 +4815,6 @@ static void Main() }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (8,23): error CS8917: The delegate type could not be inferred. - // Delegate d1 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "async ref Task (string s) => { _ = s.Length; await Task.Yield(); }").WithLocation(8, 23), // (8,29): error CS1073: Unexpected token 'ref' // Delegate d1 = async ref Task (string s) => { _ = s.Length; await Task.Yield(); }; Diagnostic(ErrorCode.ERR_UnexpectedToken, "ref").WithArguments("ref").WithLocation(8, 29), @@ -4967,9 +4965,6 @@ static void Main() }"; var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); comp.VerifyDiagnostics( - // (8,23): error CS8917: The delegate type could not be inferred. - // Delegate d1 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "async (ref string s) => { _ = s.Length; await Task.Yield(); }").WithLocation(8, 23), // (8,41): error CS1988: Async methods cannot have ref, in or out parameters // Delegate d1 = async (ref string s) => { _ = s.Length; await Task.Yield(); }; Diagnostic(ErrorCode.ERR_BadAsyncArgType, "s").WithLocation(8, 41), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 005c61294515e..b2ddeff0cda89 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -1949,9 +1949,9 @@ static void M(A a) // (8,9): error CS1656: Cannot assign to 'E' because it is a 'method group' // a.E! += a.E!; // 1 Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "a.E").WithArguments("E", "method group").WithLocation(8, 9), - // (9,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (9,13): error CS0019: Operator '!=' cannot be applied to operands of type 'method group' and '' // if (a.E! != null) // 2 - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "a.E").WithArguments("inferred delegate type", "10.0").WithLocation(9, 13), + Diagnostic(ErrorCode.ERR_BadBinaryOps, "a.E! != null").WithArguments("!=", "method group", "").WithLocation(9, 13), // (11,15): error CS1503: Argument 1: cannot convert from 'method group' to 'A' // M(a.E!); // 3 Diagnostic(ErrorCode.ERR_BadArgType, "a.E").WithArguments("1", "method group", "A").WithLocation(11, 15), @@ -1970,9 +1970,9 @@ static void M(A a) // (17,9): error CS1656: Cannot assign to 'F' because it is a 'method group' // a.F! += a.F!; // 8 Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "a.F").WithArguments("F", "method group").WithLocation(17, 9), - // (18,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (18,13): error CS0019: Operator '!=' cannot be applied to operands of type 'method group' and '' // if (a.F! != null) // 9 - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "a.F").WithArguments("inferred delegate type", "10.0").WithLocation(18, 13), + Diagnostic(ErrorCode.ERR_BadBinaryOps, "a.F! != null").WithArguments("!=", "method group", "").WithLocation(18, 13), // (20,15): error CS1503: Argument 1: cannot convert from 'method group' to 'A' // M(a.F!); // 10 Diagnostic(ErrorCode.ERR_BadArgType, "a.F").WithArguments("1", "method group", "A").WithLocation(20, 15), @@ -63568,13 +63568,13 @@ void Test2(string x2, string? y2) [Theory] [CombinatorialData] - public void StringInterpolation_02(bool useBoolReturns, bool validityParameter) + public void StringInterpolation_02(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""{s = """"}{s.ToString()}""", @"$""{s = """"}"" + $""{s.ToString()}""")] string expression) { CSharpCompilation c = CreateCompilation(new[] { @" #nullable enable string? s = null; -M($""{s = """"}{s.ToString()}"", s); +M(" + expression + @", s); void M(CustomHandler c, string s) {} ", GetInterpolatedStringCustomHandlerType("CustomHandler", "partial struct", useBoolReturns, includeTrailingOutConstructorParameter: validityParameter) }, parseOptions: TestOptions.RegularPreview); @@ -63584,7 +63584,7 @@ void M(CustomHandler c, string s) {} 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) + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "s").WithArguments("s", "void M(CustomHandler c, string s)").WithLocation(5, 19 + expression.IndexOf("s.ToString")) ); } else @@ -63595,7 +63595,7 @@ void M(CustomHandler c, string s) {} [Theory] [CombinatorialData] - public void StringInterpolation_03(bool useBoolReturns, bool validityParameter) + public void StringInterpolation_03(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { CSharpCompilation c = CreateCompilation(new[] { @" using System.Diagnostics.CodeAnalysis; @@ -63607,7 +63607,7 @@ public void StringInterpolation_03(bool useBoolReturns, bool validityParameter) #line 1000 ref s, #line 2000 - $"""", + " + expression + @", #line 3000 s.ToString()); @@ -63634,14 +63634,14 @@ public CustomHandler(int literalLength, int formattedCount, [MaybeNull] ref T t [Theory] [CombinatorialData] - public void StringInterpolation_04(bool useBoolReturns, bool validityParameter) + public void StringInterpolation_04(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""""", @"$"""" + $""""")] string expression) { CSharpCompilation c = CreateCompilation(new[] { @" using System.Runtime.CompilerServices; #nullable enable string? s = null; -M(s, $""""); +M(s, " + expression + @"); void M(string? s1, [InterpolatedStringHandlerArgument(""s1"")] CustomHandler c) {} @@ -63663,14 +63663,14 @@ public CustomHandler(int literalLength, int formattedCount, string s" + (validit [Theory] [CombinatorialData] - public void StringInterpolation_05(bool useBoolReturns, bool validityParameter) + public void StringInterpolation_05(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""{s}""", @"$""{s}"" + $""""")] string expression) { CSharpCompilation c = CreateCompilation(new[] { @" using System.Runtime.CompilerServices; #nullable enable string? s = null; -M($""{s}""); +M(" + expression + @"); void M(CustomHandler c) {} @@ -63699,14 +63699,14 @@ public CustomHandler(int literalLength, int formattedCount" + (validityParameter [Theory] [CombinatorialData] - public void StringInterpolation_06(bool useBoolReturns, bool validityParameter) + public void StringInterpolation_06(bool useBoolReturns, bool validityParameter, [CombinatorialValues(@"$""{s = null}{s = """"}""", @"$""{s = null}"" + $""{s = """"}""")] string expression) { CSharpCompilation c = CreateCompilation(new[] { @" using System.Runtime.CompilerServices; #nullable enable string? s = """"; -M($""{s = null}{s = """"}""); +M(" + expression + @"); _ = s.ToString(); void M(CustomHandler c) {} @@ -63742,6 +63742,62 @@ public CustomHandler(int literalLength, int formattedCount" + (validityParameter } } + [Theory] + [CombinatorialData] + public void StringInterpolation_07(bool useBoolReturns, bool validityParameter, + [CombinatorialValues(@"$""{s1 = null}{s2 = null}{s3 = null}{s1 = """"}{s2 = """"}{s3 = """"}""", + @"$""{s1 = null}"" + $""{s2 = null}"" + $""{s3 = null}"" + $""{s1 = """"}"" + $""{s2 = """"}"" + $""{s3 = """"}""")] string expression) + { + CSharpCompilation c = CreateCompilation(new[] { @" +using System.Runtime.CompilerServices; +#nullable enable + +string? s1 = """"; +string? s2 = """"; +string? s3 = """"; +M(" + expression + @"); +_ = s1.ToString(); +_ = s2.ToString(); +_ = s3.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( + // (9,5): warning CS8602: Dereference of a possibly null reference. + // _ = s1.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(9, 5), + // (10,5): warning CS8602: Dereference of a possibly null reference. + // _ = s2.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(10, 5), + // (11,5): warning CS8602: Dereference of a possibly null reference. + // _ = s3.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s3").WithLocation(11, 5) + ); + } + else + { + c.VerifyDiagnostics(); + } + } + [Fact] public void DelegateCreation_01() { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs index 99740a7cd5898..28fa51a51b7ed 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs @@ -20066,7 +20066,7 @@ .locals init (object V_0, //d int V_1) //z IL_0000: ldnull IL_0001: stloc.0 - IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" + IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" IL_0007: brtrue.s IL_003e IL_0009: ldc.i4.0 IL_000a: ldtoken ""Cls"" @@ -20086,14 +20086,14 @@ .locals init (object V_0, //d IL_0029: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_002e: stelem.ref IL_002f: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0034: call ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0039: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" - IL_003e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" - IL_0043: ldfld ""<>F{00000004} System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Target"" - IL_0048: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" + IL_0034: call ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> System.Runtime.CompilerServices.CallSite<<>F{00000010}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0039: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" + IL_003e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" + IL_0043: ldfld ""<>F{00000010} System.Runtime.CompilerServices.CallSite<<>F{00000010}>.Target"" + IL_0048: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" IL_004d: ldloc.0 IL_004e: ldloca.s V_1 - IL_0050: callvirt ""dynamic <>F{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref int)"" + IL_0050: callvirt ""dynamic <>F{00000010}.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref int)"" IL_0055: pop IL_0056: ret }"); @@ -20121,7 +20121,7 @@ .locals init (object V_0, //d int V_1) IL_0000: ldnull IL_0001: stloc.0 - IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" + IL_0002: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" IL_0007: brtrue.s IL_003e IL_0009: ldc.i4.0 IL_000a: ldtoken ""Cls"" @@ -20141,14 +20141,14 @@ .locals init (object V_0, //d IL_0029: call ""Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)"" IL_002e: stelem.ref IL_002f: call ""System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.GetIndex(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Collections.Generic.IEnumerable)"" - IL_0034: call ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" - IL_0039: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" - IL_003e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" - IL_0043: ldfld ""<>F{00000004} System.Runtime.CompilerServices.CallSite<<>F{00000004}>.Target"" - IL_0048: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000004}> Cls.<>o__0.<>p__0"" + IL_0034: call ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> System.Runtime.CompilerServices.CallSite<<>F{00000010}>.Create(System.Runtime.CompilerServices.CallSiteBinder)"" + IL_0039: stsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" + IL_003e: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" + IL_0043: ldfld ""<>F{00000010} System.Runtime.CompilerServices.CallSite<<>F{00000010}>.Target"" + IL_0048: ldsfld ""System.Runtime.CompilerServices.CallSite<<>F{00000010}> Cls.<>o__0.<>p__0"" IL_004d: ldloc.0 IL_004e: ldloca.s V_1 - IL_0050: callvirt ""dynamic <>F{00000004}.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref int)"" + IL_0050: callvirt ""dynamic <>F{00000010}.Invoke(System.Runtime.CompilerServices.CallSite, dynamic, ref int)"" IL_0055: pop IL_0056: ret } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs index 2fb1dd4436933..fac1ac5cc44f4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests5.cs @@ -1832,6 +1832,34 @@ class C ); } + [Fact, WorkItem(55184, "https://github.com/dotnet/roslyn/issues/55184")] + public void Repro55184() + { + var source = @" +var x = """"; + +_ = x is { Error: { Length: > 0 } }; +_ = x is { Error.Length: > 0 }; +_ = x is { Length: { Error: > 0 } }; +_ = x is { Length.Error: > 0 }; +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (4,12): error CS0117: 'string' does not contain a definition for 'Error' + // _ = x is { Error: { Length: > 0 } }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Error").WithArguments("string", "Error").WithLocation(4, 12), + // (5,12): error CS0117: 'string' does not contain a definition for 'Error' + // _ = x is { Error.Length: > 0 }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Error").WithArguments("string", "Error").WithLocation(5, 12), + // (6,22): error CS0117: 'int' does not contain a definition for 'Error' + // _ = x is { Length: { Error: > 0 } }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Error").WithArguments("int", "Error").WithLocation(6, 22), + // (7,19): error CS0117: 'int' does not contain a definition for 'Error' + // _ = x is { Length.Error: > 0 }; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Error").WithArguments("int", "Error").WithLocation(7, 19) + ); + } + public class FlowAnalysisTests : FlowTestBase { [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index 14146f5916534..2a7eda482e594 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -1140,9 +1140,9 @@ static class S // (5,9): error CS1656: Cannot assign to 'E' because it is a 'method group' // o.E += o.E; Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "o.E").WithArguments("E", "method group").WithLocation(5, 9), - // (6,13): error CS8773: Feature 'inferred delegate type' is not available in C# 9.0. Please use language version 10.0 or greater. + // (6,13): error CS0019: Operator '!=' cannot be applied to operands of type 'method group' and '' // if (o.E != null) - Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "o.E").WithArguments("inferred delegate type", "10.0").WithLocation(6, 13), + Diagnostic(ErrorCode.ERR_BadBinaryOps, "o.E != null").WithArguments("!=", "method group", "").WithLocation(6, 13), // (8,15): error CS1503: Argument 1: cannot convert from 'method group' to 'object' // M(o.E); Diagnostic(ErrorCode.ERR_BadArgType, "o.E").WithArguments("1", "method group", "object").WithLocation(8, 15), diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.Managed.Core.targets b/src/Compilers/Core/MSBuildTask/Microsoft.Managed.Core.targets index d9e01cfec3bcd..91aa0e86cd598 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.Managed.Core.targets +++ b/src/Compilers/Core/MSBuildTask/Microsoft.Managed.Core.targets @@ -81,7 +81,7 @@ diff --git a/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs b/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs index 8cabe33d65fc8..8d84de8f36628 100644 --- a/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/TargetTests.cs @@ -762,6 +762,10 @@ public void TestImplicitlySkipAnalyzers( var expectedSkipAnalyzersValue = !analyzersEnabled || expectedImplicitlySkippedAnalyzers ? "true" : ""; var actualSkipAnalyzersValue = instance.GetPropertyValue("_SkipAnalyzers"); Assert.Equal(expectedSkipAnalyzersValue, actualSkipAnalyzersValue); + + var expectedFeaturesValue = expectedImplicitlySkippedAnalyzers ? "run-nullable-analysis=never;" : ""; + var actualFeaturesValue = instance.GetPropertyValue("Features"); + Assert.Equal(expectedFeaturesValue, actualFeaturesValue); return; static string getPropertyGroup(string propertyName, bool? propertyValue) diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 18553a1efba64..39c48103e30c2 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -195,6 +195,7 @@ public static class Features public const string CodeActionsUseNamedArguments = "CodeActions.UseNamedArguments"; public const string CodeActionsUseNotPattern = "CodeActions.UseNotPattern"; public const string CodeActionsUsePatternCombinators = "CodeActions.UsePatternCombinators"; + public const string CodeActionsUseRecursivePatterns = "CodeActions.UseRecursivePatterns"; public const string CodeActionsUseNullPropagation = "CodeActions.UseNullPropagation"; public const string CodeActionsUseObjectInitializer = "CodeActions.UseObjectInitializer"; public const string CodeActionsUseRangeOperator = "CodeActions.UseRangeOperator"; diff --git a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs index 8e4de95a1439a..0f9cb26957128 100644 --- a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs +++ b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs @@ -58,29 +58,29 @@ protected override void UpdateOptions(AnalyzerConfigOptions editorConfigOptions, AddRange(unusedValueSettings); } - private static IEnumerable GetVarCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetVarCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.VarForBuiltInTypes, description: CSharpEditorResources.For_built_in_types, trueValueDescription: CSharpEditorResources.Prefer_var, falseValueDescription: CSharpEditorResources.Prefer_explicit_type, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.VarWhenTypeIsApparent, description: CSharpEditorResources.When_variable_type_is_apparent, trueValueDescription: CSharpEditorResources.Prefer_var, falseValueDescription: CSharpEditorResources.Prefer_explicit_type, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.VarElsewhere, description: CSharpEditorResources.Elsewhere, trueValueDescription: CSharpEditorResources.Prefer_var, falseValueDescription: CSharpEditorResources.Prefer_explicit_type, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); } - private static IEnumerable GetUsingsCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetUsingsCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { yield return CodeStyleSetting.Create( option: CSharpCodeStyleOptions.PreferredUsingDirectivePlacement, @@ -88,65 +88,65 @@ private static IEnumerable GetUsingsCodeStyleOptions(AnalyzerC enumValues: new[] { AddImportPlacement.InsideNamespace, AddImportPlacement.OutsideNamespace }, valueDescriptions: new[] { CSharpEditorResources.Inside_namespace, CSharpEditorResources.Outside_namespace }, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); } - private static IEnumerable GetNullCheckingCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetNullCheckingCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferThrowExpression, description: CSharpEditorResources.Prefer_throw_expression, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferConditionalDelegateCall, description: CSharpEditorResources.Prefer_conditional_delegate_call, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferNullCheckOverTypeCheck, description: CSharpEditorResources.Prefer_null_check_over_type_check, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); } - private static IEnumerable GetModifierCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetModifierCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferStaticLocalFunction, description: CSharpEditorResources.Prefer_static_local_functions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); } - private static IEnumerable GetCodeBlockCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetCodeBlockCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferSimpleUsingStatement, description: CSharpEditorResources.Prefer_simple_using_statement, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); } - private static IEnumerable GetExpressionCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetExpressionCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferSwitchExpression, description: CSharpEditorResources.Prefer_switch_expression, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferSimpleDefaultExpression, description: CSharpEditorResources.Prefer_simple_default_expression, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferLocalOverAnonymousFunction, description: CSharpEditorResources.Prefer_local_function_over_anonymous_function, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferIndexOperator, description: CSharpEditorResources.Prefer_index_operator, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, description: CSharpEditorResources.Prefer_range_operator, editorConfigOptions, visualStudioOptions, updaterService); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferSwitchExpression, description: CSharpEditorResources.Prefer_switch_expression, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferSimpleDefaultExpression, description: CSharpEditorResources.Prefer_simple_default_expression, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferLocalOverAnonymousFunction, description: CSharpEditorResources.Prefer_local_function_over_anonymous_function, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferIndexOperator, description: CSharpEditorResources.Prefer_index_operator, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, description: CSharpEditorResources.Prefer_range_operator, editorConfigOptions, visualStudioOptions, updaterService, FileName); } - private static IEnumerable GetPatternMatchingCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetPatternMatchingCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPatternMatching, description: CSharpEditorResources.Prefer_pattern_matching, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck, description: CSharpEditorResources.Prefer_pattern_matching_over_is_with_cast_check, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck, description: CSharpEditorResources.Prefer_pattern_matching_over_as_with_null_check, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferNotPattern, description: CSharpEditorResources.Prefer_pattern_matching_over_mixed_type_check, editorConfigOptions, visualStudioOptions, updaterService); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPatternMatching, description: CSharpEditorResources.Prefer_pattern_matching, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPatternMatchingOverIsWithCastCheck, description: CSharpEditorResources.Prefer_pattern_matching_over_is_with_cast_check, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferPatternMatchingOverAsWithNullCheck, description: CSharpEditorResources.Prefer_pattern_matching_over_as_with_null_check, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferNotPattern, description: CSharpEditorResources.Prefer_pattern_matching_over_mixed_type_check, editorConfigOptions, visualStudioOptions, updaterService, FileName); } - private static IEnumerable GetVariableCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetVariableCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferInlinedVariableDeclaration, description: CSharpEditorResources.Prefer_inlined_variable_declaration, editorConfigOptions, visualStudioOptions, updaterService); - yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferDeconstructedVariableDeclaration, description: CSharpEditorResources.Prefer_deconstructed_variable_declaration, editorConfigOptions, visualStudioOptions, updaterService); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferInlinedVariableDeclaration, description: CSharpEditorResources.Prefer_inlined_variable_declaration, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferDeconstructedVariableDeclaration, description: CSharpEditorResources.Prefer_deconstructed_variable_declaration, editorConfigOptions, visualStudioOptions, updaterService, FileName); } - private static IEnumerable GetExpressionBodyCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetExpressionBodyCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { var enumValues = new[] { ExpressionBodyPreference.Never, ExpressionBodyPreference.WhenPossible, ExpressionBodyPreference.WhenOnSingleLine }; var valueDescriptions = new[] { CSharpEditorResources.Never, CSharpEditorResources.When_possible, CSharpEditorResources.When_on_single_line }; @@ -155,52 +155,52 @@ private static IEnumerable GetExpressionBodyCodeStyleOptions(A enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, description: CSharpEditorResources.Use_expression_body_for_constructors, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedOperators, description: CSharpEditorResources.Use_expression_body_for_operators, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedProperties, description: CSharpEditorResources.Use_expression_body_for_properties, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, description: CSharpEditorResources.Use_expression_body_for_indexers, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, description: CSharpEditorResources.Use_expression_body_for_accessors, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedLambdas, description: CSharpEditorResources.Use_expression_body_for_lambdas, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); yield return CodeStyleSetting.Create(option: CSharpCodeStyleOptions.PreferExpressionBodiedLocalFunctions, description: CSharpEditorResources.Use_expression_body_for_local_functions, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: editorConfigOptions, - visualStudioOptions: visualStudioOptions, updater: updaterService); + visualStudioOptions: visualStudioOptions, updater: updaterService, fileName: FileName); } - private static IEnumerable GetUnusedValueCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetUnusedValueCodeStyleOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { var enumValues = new[] { @@ -214,7 +214,7 @@ private static IEnumerable GetUnusedValueCodeStyleOptions(Anal new[] { CSharpEditorResources.Unused_local, CSharpEditorResources.Discard }, editorConfigOptions, visualStudioOptions, - updaterService); + updaterService, FileName); yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.UnusedValueExpressionStatement, description: CSharpEditorResources.Avoid_expression_statements_that_implicitly_ignore_value, @@ -222,7 +222,7 @@ private static IEnumerable GetUnusedValueCodeStyleOptions(Anal new[] { CSharpEditorResources.Unused_local, CSharpEditorResources.Discard }, editorConfigOptions, visualStudioOptions, - updaterService); + updaterService, FileName); } } } diff --git a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsLanguageServiceFactory.cs b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsLanguageServiceFactory.cs similarity index 77% rename from src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsLanguageServiceFactory.cs rename to src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsLanguageServiceFactory.cs index 550c7bf77b32a..c25632bc19171 100644 --- a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsLanguageServiceFactory.cs +++ b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsLanguageServiceFactory.cs @@ -10,21 +10,21 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.EditorConfigSettings.DataProvider.Formatting +namespace Microsoft.VisualStudio.LanguageServices.CSharp.EditorConfigSettings.DataProvider.Whitespace { - [ExportLanguageServiceFactory(typeof(ILanguageSettingsProviderFactory), LanguageNames.CSharp), Shared] - internal class CSharpFormattingSettingsLanguageServiceFactory : ILanguageServiceFactory + [ExportLanguageServiceFactory(typeof(ILanguageSettingsProviderFactory), LanguageNames.CSharp), Shared] + internal class CSharpWhitespaceSettingsLanguageServiceFactory : ILanguageServiceFactory { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpFormattingSettingsLanguageServiceFactory() + public CSharpWhitespaceSettingsLanguageServiceFactory() { } public ILanguageService CreateLanguageService(HostLanguageServices languageServices) { var workspace = languageServices.WorkspaceServices.Workspace; - return new CSharpFormattingSettingsProviderFactory(workspace); + return new CSharpWhitespaceSettingsProviderFactory(workspace); } } } diff --git a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsProvider.cs b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProvider.cs similarity index 56% rename from src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsProvider.cs rename to src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProvider.cs index 00341120f9c72..73d2b4ad29481 100644 --- a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsProvider.cs +++ b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProvider.cs @@ -14,11 +14,11 @@ using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; using Microsoft.CodeAnalysis.Options; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.EditorConfigSettings.DataProvider.Formatting +namespace Microsoft.VisualStudio.LanguageServices.CSharp.EditorConfigSettings.DataProvider.Whitespace { - internal class CSharpFormattingSettingsProvider : SettingsProviderBase + internal class CSharpWhitespaceSettingsProvider : SettingsProviderBase { - public CSharpFormattingSettingsProvider(string filePath, OptionUpdater updaterService, Workspace workspace) + public CSharpWhitespaceSettingsProvider(string filePath, OptionUpdater updaterService, Workspace workspace) : base(filePath, updaterService, workspace) { Update(); @@ -36,71 +36,71 @@ protected override void UpdateOptions(AnalyzerConfigOptions editorConfigOptions, AddRange(wrappingOptions.ToImmutableArray()); } - private static IEnumerable GetSpacingOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetSpacingOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpacingAfterMethodDeclarationName, CSharpEditorResources.Insert_space_between_method_name_and_its_opening_parenthesis2, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceWithinMethodDeclarationParenthesis, CSharpEditorResources.Insert_space_within_parameter_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBetweenEmptyMethodDeclarationParentheses, CSharpEditorResources.Insert_space_within_empty_parameter_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterMethodCallName, CSharpEditorResources.Insert_space_between_method_name_and_its_opening_parenthesis1, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, CSharpEditorResources.Insert_space_within_argument_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, CSharpEditorResources.Insert_space_within_empty_argument_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpacingAfterMethodDeclarationName, CSharpEditorResources.Insert_space_between_method_name_and_its_opening_parenthesis2, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceWithinMethodDeclarationParenthesis, CSharpEditorResources.Insert_space_within_parameter_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBetweenEmptyMethodDeclarationParentheses, CSharpEditorResources.Insert_space_within_empty_parameter_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterMethodCallName, CSharpEditorResources.Insert_space_between_method_name_and_its_opening_parenthesis1, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, CSharpEditorResources.Insert_space_within_argument_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, CSharpEditorResources.Insert_space_within_empty_argument_list_parentheses, editorConfigOptions, visualStudioOptions, updaterService, FileName); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterControlFlowStatementKeyword, CSharpEditorResources.Insert_space_after_keywords_in_control_flow_statements, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceWithinExpressionParentheses, CSharpEditorResources.Insert_space_within_parentheses_of_expressions, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceWithinCastParentheses, CSharpEditorResources.Insert_space_within_parentheses_of_type_casts, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceWithinOtherParentheses, CSharpEditorResources.Insert_spaces_within_parentheses_of_control_flow_statements, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterCast, CSharpEditorResources.Insert_space_after_cast, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, CSharpEditorResources.Ignore_spaces_in_declaration_statements, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterControlFlowStatementKeyword, CSharpEditorResources.Insert_space_after_keywords_in_control_flow_statements, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceWithinExpressionParentheses, CSharpEditorResources.Insert_space_within_parentheses_of_expressions, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceWithinCastParentheses, CSharpEditorResources.Insert_space_within_parentheses_of_type_casts, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceWithinOtherParentheses, CSharpEditorResources.Insert_spaces_within_parentheses_of_control_flow_statements, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterCast, CSharpEditorResources.Insert_space_after_cast, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, CSharpEditorResources.Ignore_spaces_in_declaration_statements, editorConfigOptions, visualStudioOptions, updaterService, FileName); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBeforeOpenSquareBracket, CSharpEditorResources.Insert_space_before_open_square_bracket, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBetweenEmptySquareBrackets, CSharpEditorResources.Insert_space_within_empty_square_brackets, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceWithinSquareBrackets, CSharpEditorResources.Insert_spaces_within_square_brackets, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBeforeOpenSquareBracket, CSharpEditorResources.Insert_space_before_open_square_bracket, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBetweenEmptySquareBrackets, CSharpEditorResources.Insert_space_within_empty_square_brackets, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceWithinSquareBrackets, CSharpEditorResources.Insert_spaces_within_square_brackets, editorConfigOptions, visualStudioOptions, updaterService, FileName); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterColonInBaseTypeDeclaration, CSharpEditorResources.Insert_space_after_colon_for_base_or_interface_in_type_declaration, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterComma, CSharpEditorResources.Insert_space_after_comma, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterDot, CSharpEditorResources.Insert_space_after_dot, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceAfterSemicolonsInForStatement, CSharpEditorResources.Insert_space_after_semicolon_in_for_statement, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBeforeColonInBaseTypeDeclaration, CSharpEditorResources.Insert_space_before_colon_for_base_or_interface_in_type_declaration, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBeforeComma, CSharpEditorResources.Insert_space_before_comma, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBeforeDot, CSharpEditorResources.Insert_space_before_dot, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpaceBeforeSemicolonsInForStatement, CSharpEditorResources.Insert_space_before_semicolon_in_for_statement, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterColonInBaseTypeDeclaration, CSharpEditorResources.Insert_space_after_colon_for_base_or_interface_in_type_declaration, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterComma, CSharpEditorResources.Insert_space_after_comma, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterDot, CSharpEditorResources.Insert_space_after_dot, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceAfterSemicolonsInForStatement, CSharpEditorResources.Insert_space_after_semicolon_in_for_statement, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBeforeColonInBaseTypeDeclaration, CSharpEditorResources.Insert_space_before_colon_for_base_or_interface_in_type_declaration, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBeforeComma, CSharpEditorResources.Insert_space_before_comma, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBeforeDot, CSharpEditorResources.Insert_space_before_dot, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpaceBeforeSemicolonsInForStatement, CSharpEditorResources.Insert_space_before_semicolon_in_for_statement, editorConfigOptions, visualStudioOptions, updaterService, FileName); - yield return FormattingSetting.Create(CSharpFormattingOptions2.SpacingAroundBinaryOperator, CSharpEditorResources.Set_spacing_for_operators, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.SpacingAroundBinaryOperator, CSharpEditorResources.Set_spacing_for_operators, editorConfigOptions, visualStudioOptions, updaterService, FileName); } - private static IEnumerable GetNewLineOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetNewLineOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInTypes, CSharpEditorResources.Place_open_brace_on_new_line_for_types, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInMethods, CSharpEditorResources.Place_open_brace_on_new_line_for_methods_local_functions, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInProperties, CSharpEditorResources.Place_open_brace_on_new_line_for_properties_indexers_and_events, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInAccessors, CSharpEditorResources.Place_open_brace_on_new_line_for_property_indexer_and_event_accessors, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInAnonymousMethods, CSharpEditorResources.Place_open_brace_on_new_line_for_anonymous_methods, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInControlBlocks, CSharpEditorResources.Place_open_brace_on_new_line_for_control_blocks, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInAnonymousTypes, CSharpEditorResources.Place_open_brace_on_new_line_for_anonymous_types, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInObjectCollectionArrayInitializers, CSharpEditorResources.Place_open_brace_on_new_line_for_object_collection_array_and_with_initializers, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInLambdaExpressionBody, CSharpEditorResources.Place_open_brace_on_new_line_for_lambda_expression, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLineForElse, CSharpEditorResources.Place_else_on_new_line, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLineForCatch, CSharpEditorResources.Place_catch_on_new_line, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLineForFinally, CSharpEditorResources.Place_finally_on_new_line, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLineForMembersInObjectInit, CSharpEditorResources.Place_members_in_object_initializers_on_new_line, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLineForMembersInAnonymousTypes, CSharpEditorResources.Place_members_in_anonymous_types_on_new_line, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.NewLineForClausesInQuery, CSharpEditorResources.Place_query_expression_clauses_on_new_line, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInTypes, CSharpEditorResources.Place_open_brace_on_new_line_for_types, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInMethods, CSharpEditorResources.Place_open_brace_on_new_line_for_methods_local_functions, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInProperties, CSharpEditorResources.Place_open_brace_on_new_line_for_properties_indexers_and_events, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInAccessors, CSharpEditorResources.Place_open_brace_on_new_line_for_property_indexer_and_event_accessors, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInAnonymousMethods, CSharpEditorResources.Place_open_brace_on_new_line_for_anonymous_methods, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInControlBlocks, CSharpEditorResources.Place_open_brace_on_new_line_for_control_blocks, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInAnonymousTypes, CSharpEditorResources.Place_open_brace_on_new_line_for_anonymous_types, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInObjectCollectionArrayInitializers, CSharpEditorResources.Place_open_brace_on_new_line_for_object_collection_array_and_with_initializers, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLinesForBracesInLambdaExpressionBody, CSharpEditorResources.Place_open_brace_on_new_line_for_lambda_expression, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForElse, CSharpEditorResources.Place_else_on_new_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForCatch, CSharpEditorResources.Place_catch_on_new_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForFinally, CSharpEditorResources.Place_finally_on_new_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForMembersInObjectInit, CSharpEditorResources.Place_members_in_object_initializers_on_new_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForMembersInAnonymousTypes, CSharpEditorResources.Place_members_in_anonymous_types_on_new_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForClausesInQuery, CSharpEditorResources.Place_query_expression_clauses_on_new_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); } - private static IEnumerable GetIndentationOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetIndentationOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return FormattingSetting.Create(CSharpFormattingOptions2.IndentBlock, CSharpEditorResources.Indent_block_contents, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.IndentBraces, CSharpEditorResources.Indent_open_and_close_braces, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.IndentSwitchCaseSection, CSharpEditorResources.Indent_case_contents, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.IndentSwitchCaseSectionWhenBlock, CSharpEditorResources.Indent_case_contents_when_block, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.IndentSwitchSection, CSharpEditorResources.Indent_case_labels, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.LabelPositioning, CSharpEditorResources.Label_Indentation, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.IndentBlock, CSharpEditorResources.Indent_block_contents, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.IndentBraces, CSharpEditorResources.Indent_open_and_close_braces, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.IndentSwitchCaseSection, CSharpEditorResources.Indent_case_contents, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.IndentSwitchCaseSectionWhenBlock, CSharpEditorResources.Indent_case_contents_when_block, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.IndentSwitchSection, CSharpEditorResources.Indent_case_labels, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.LabelPositioning, CSharpEditorResources.Label_Indentation, editorConfigOptions, visualStudioOptions, updaterService, FileName); } - private static IEnumerable GetWrappingOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) + private IEnumerable GetWrappingOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updaterService) { - yield return FormattingSetting.Create(CSharpFormattingOptions2.WrappingPreserveSingleLine, CSharpEditorResources.Leave_block_on_single_line, editorConfigOptions, visualStudioOptions, updaterService); - yield return FormattingSetting.Create(CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, CSharpEditorResources.Leave_statements_and_member_declarations_on_the_same_line, editorConfigOptions, visualStudioOptions, updaterService); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.WrappingPreserveSingleLine, CSharpEditorResources.Leave_block_on_single_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); + yield return WhitespaceSetting.Create(CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, CSharpEditorResources.Leave_statements_and_member_declarations_on_the_same_line, editorConfigOptions, visualStudioOptions, updaterService, FileName); } } } diff --git a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsProviderFactory.cs b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProviderFactory.cs similarity index 69% rename from src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsProviderFactory.cs rename to src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProviderFactory.cs index 543617276493a..d8bf344b4f9a7 100644 --- a/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Formatting/CSharpFormattingSettingsProviderFactory.cs +++ b/src/EditorFeatures/CSharp/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProviderFactory.cs @@ -7,21 +7,21 @@ using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; -namespace Microsoft.VisualStudio.LanguageServices.CSharp.EditorConfigSettings.DataProvider.Formatting +namespace Microsoft.VisualStudio.LanguageServices.CSharp.EditorConfigSettings.DataProvider.Whitespace { - internal class CSharpFormattingSettingsProviderFactory : ILanguageSettingsProviderFactory + internal class CSharpWhitespaceSettingsProviderFactory : ILanguageSettingsProviderFactory { private readonly Workspace _workspace; - public CSharpFormattingSettingsProviderFactory(Workspace workspace) + public CSharpWhitespaceSettingsProviderFactory(Workspace workspace) { _workspace = workspace; } - public ISettingsProvider GetForFile(string filePath) + public ISettingsProvider GetForFile(string filePath) { var updaterService = new OptionUpdater(_workspace, filePath); - return new CSharpFormattingSettingsProvider(filePath, updaterService, _workspace); + return new CSharpWhitespaceSettingsProvider(filePath, updaterService, _workspace); } } } diff --git a/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs new file mode 100644 index 0000000000000..d72e97c3e8ab5 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsRefactoringTests.cs @@ -0,0 +1,237 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseRecursivePatterns; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.UseRecursivePatterns +{ + using VerifyCS = CSharpCodeRefactoringVerifier; + + [Trait(Traits.Feature, Traits.Features.CodeActionsUseRecursivePatterns)] + public class UseRecursivePatternsRefactoringTests + { + private static Task VerifyAsync(string initialMarkup, string expectedMarkup, bool skipCodeActionValidation = false) + { + return new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp9, + TestCode = initialMarkup, + FixedCode = expectedMarkup, + CodeActionValidationMode = skipCodeActionValidation + ? CodeActionValidationMode.None + : CodeActionValidationMode.SemanticStructure, + }.RunAsync(); + } + + private static Task VerifyMissingAsync(string initialMarkup) + { + return VerifyAsync(initialMarkup, initialMarkup); + } + + [Theory] + [InlineData("a.b.c.d && a.b.c.a", "a.b.c is { d: n, a: n }")] + [InlineData("a?.b.c.d && a.b.c.a", "a?.b.c is { d: n, a: n }")] + [InlineData("a.b?.c.d && a.b.c.a", "a.b?.c is { d: n, a: n }")] + [InlineData("a.b.c?.d && a.b.c.a", "a.b.c is { d: n, a: n }")] + [InlineData("a.b?.c?.d && a.b.c.a", "a.b?.c is { d: n, a: n }")] + [InlineData("a?.b.c?.d && a.b.c.a", "a?.b.c is { d: n, a: n }")] + [InlineData("a?.b?.c.d && a.b.c.a", "a?.b?.c is { d: n, a: n }")] + [InlineData("a?.b?.c?.d && a.b.c.a", "a?.b?.c is { d: n, a: n }")] + + [InlineData("a.b.c.d && a.b.a", "a.b is { c: { d: n }, a: n }")] + [InlineData("a?.b.c.d && a.b.a", "a?.b is { c: { d: n }, a: n }")] + [InlineData("a.b?.c.d && a.b.a", "a.b is { c: { d: n }, a: n }")] + [InlineData("a.b.c?.d && a.b.a", "a.b is { c: { d: n }, a: n }")] + [InlineData("a.b?.c?.d && a.b.a", "a.b is { c: { d: n }, a: n }")] + [InlineData("a?.b.c?.d && a.b.a", "a?.b is { c: { d: n }, a: n }")] + [InlineData("a?.b?.c.d && a.b.a", "a?.b is { c: { d: n }, a: n }")] + [InlineData("a?.b?.c?.d && a.b.a", "a?.b is { c: { d: n }, a: n }")] + + [InlineData("a.b.c.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a?.b.c.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a.b?.c.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a.b.c?.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a.b?.c?.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a?.b.c?.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a?.b?.c.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + [InlineData("a?.b?.c?.d && a.a", "a is { b: { c: { d: n } }, a: n }")] + + [InlineData("a.b.c.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a?.b.c.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a.b?.c.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a.b.c?.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a.b?.c?.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a?.b.c?.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a?.b?.c.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + [InlineData("a?.b?.c?.d && b", "this is { a: { b: { c: { d: n } } }, b: n }")] + + [InlineData("a.b.m().d && a.b.m().a", "a.b.m() is { d: n, a: n }")] + [InlineData("a.m().c.d && a.m().a", "a.m() is { c: { d: n }, a: n }")] + [InlineData("a?.m().c.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")] + [InlineData("a.m()?.c.d && a.m().a", "a.m() is { c: { d: n }, a: n }")] + [InlineData("a.m().c?.d && a.m().a", "a.m() is { c: { d: n }, a: n }")] + [InlineData("a.m()?.c?.d && a.m().a", "a.m() is { c: { d: n }, a: n }")] + [InlineData("a?.m().c?.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")] + [InlineData("a?.m()?.c.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")] + [InlineData("a?.m()?.c?.d && a?.m().a", "a?.m() is { c: { d: n }, a: n }")] + public async Task TestLogicalAndExpression_Receiver(string actual, string expected) + { + await VerifyAsync(WrapInIfStatement("n == " + actual + " == n", "&&"), WrapInIfStatement(expected)); + } + + [Theory] + [InlineData("this.P1 < 1 && 2 >= this.P2", "this is { P1: < 1, P2: <= 2 }")] + [InlineData("this.P1 > 1 && 2 <= this.P2", "this is { P1: > 1, P2: >= 2 }")] + [InlineData("this.P1 <= 1 && 2 > this.P2", "this is { P1: <= 1, P2: < 2 }")] + [InlineData("this.P1 >= 1 && 2 < this.P2", "this is { P1: >= 1, P2: > 2 }")] + // Nested + [InlineData("this.CP1?.P1 < 1 && 2 >= this.CP2.P2", "this is { CP1: { P1: < 1 }, CP2: { P2: <= 2 } }")] + [InlineData("this.CP1?.P1 > 1 && 2 <= this.CP2.P2", "this is { CP1: { P1: > 1 }, CP2: { P2: >= 2 } }")] + [InlineData("this.CP1.P1 <= 1 && 2 > this.CP2?.P2", "this is { CP1: { P1: <= 1 }, CP2: { P2: < 2 } }")] + [InlineData("this.CP1.P1 >= 1 && 2 < this.CP2?.P2", "this is { CP1: { P1: >= 1 }, CP2: { P2: > 2 } }")] + public async Task TestLogicalAndExpression_Relational(string actual, string expected) + { + await VerifyAsync(WrapInIfStatement(actual, "&&"), WrapInIfStatement(expected)); + } + + [Theory] + [InlineData("!B1 && B2", "this is { B1: false, B2: true }")] + [InlineData("CP1.B1 && !CP2.B2", "this is { CP1: { B1: true }, CP2: { B2: false } }")] + public async Task TestLogicalAndExpression_Boolean(string actual, string expected) + { + await VerifyAsync(WrapInIfStatement(actual, "&&"), WrapInIfStatement(expected, "&&")); + } + + [Theory] + [InlineData("this.P1 == 1 && 2 == this.P2", "this is { P1: 1, P2: 2 }")] + [InlineData("this.P1 != 1 && 2 != this.P2", "this is { P1: not 1, P2: not 2 }")] + [InlineData("this.CP1.P1 == 1 && 2 == this.CP2.P2", "this is { CP1: { P1: 1 }, CP2: { P2: 2 } }")] + [InlineData("this.CP1.P1 != 1 && 2 != this.CP2.P2", "this is { CP1: { P1: not 1 }, CP2: { P2: not 2 } }")] + public async Task TestLogicalAndExpression_Equality(string actual, string expected) + { + await VerifyAsync(WrapInIfStatement(actual, "&&"), WrapInIfStatement(expected, "&&")); + } + + [Theory] + [InlineData("NS.C.SCP1.P1 == 1 && NS.C.SCP1.P2 == 2", "NS.C.SCP1 is { P1: 1, P2: 2 }")] + [InlineData("NS.C.SCP1.CP1.P1 == 1 && NS.C.SCP1.CP2.P2 == 2", "NS.C.SCP1 is { CP1: { P1: 1 }, CP2: { P2: 2 } }")] + public async Task TestLogicalAndExpression_StaticMembers(string actual, string expected) + { + await VerifyAsync(WrapInIfStatement(actual, "&&"), WrapInIfStatement(expected, "&&")); + } + + [Theory] + [InlineData("this.B1 && this.CP1.P1 == 1 [||]&& this.CP1.CP2.P3 == 3 && B2", "this.B1 && this.CP1 is { P1: 1, CP2: { P3: 3 } } && B2")] + [InlineData("this.B1 || this.CP1.P1 == 1 [||]&& this.CP1.CP2.P3 == 3 || B2", "this.B1 || this.CP1 is { P1: 1, CP2: { P3: 3 } } || B2")] + public async Task TestLogicalAndExpression_Chain(string actual, string expected) + { + await VerifyAsync(WrapInIfStatement(actual, entry: null), WrapInIfStatement(expected, entry: null)); + } + + [Theory] + [InlineData("NS.C.SCP1 == null && NS.C.SCP2 == null")] + [InlineData("NS.C.SCP1.P1 == 1 && NS.C.SCP2.P1 == 2")] + [InlineData("base.P1 == 1 && base.P2 == 2")] + [InlineData("base.B1 && base.B2")] + public async Task TestLogicalAndExpression_Invalid(string actual) + { + await VerifyMissingAsync(WrapInIfStatement(actual, "&&")); + } + + [Theory] + [InlineData("{ a: var x }", "x is { b: n }", "{ a: { b: n } x }")] + [InlineData("{ a: C x }", "x is { b: n }", "{ a: C { b: n } x }")] + [InlineData("{ a: { b: n } x }", "x is C", "{ a: C { b: n } x }")] + [InlineData("{ a: { b: n } x }", "x is { a: n }", "{ a: { b: n, a: n } x }")] + [InlineData("{ a: var x }", "x.c is { b: n }", "{ a: { c: { b: n } } x }")] + [InlineData("{ a: C x }", "x.c is { b: n }", "{ a: C { c: { b: n } } x }")] + [InlineData("{ a: { b: n } x }", "x.c is C", "{ a: { b: n, c: C } x }", true)] + [InlineData("{ a: { b: n } x }", "x.c is { a: n }", "{ a: { b: n, c: { a: n } } x }")] + [InlineData("{ a: var x }", "x == null", "{ a: var x and null }")] + public async Task TestVariableDesignation(string pattern, string expression, string expected, bool skipCodeActionValidation = false) + { + await ValidateAsync(WrapInSwitchArm($"{pattern} when {expression}", "when"), WrapInSwitchArm($"{expected}")); + await ValidateAsync(WrapInSwitchArm($"{pattern} when {expression}", "=>"), WrapInSwitchArm($"{expected}")); + await ValidateAsync(WrapInSwitchLabel($"{pattern} when {expression}", "when"), WrapInSwitchLabel($"{expected}")); + await ValidateAsync(WrapInSwitchLabel($"{pattern} when {expression}", "case"), WrapInSwitchLabel($"{expected}")); + await ValidateAsync(WrapInIfStatement($"this is {pattern} && {expression}", "&&"), WrapInIfStatement($"this is {expected}")); + + await ValidateAsync(WrapInSwitchArm($"{pattern} when {expression} && B1 && B2", "when"), WrapInSwitchArm($"{expected} when B1 && B2")); + await ValidateAsync(WrapInSwitchArm($"{pattern} when {expression} && B1 && B2", "=>"), WrapInSwitchArm($"{expected} when B1 && B2")); + await ValidateAsync(WrapInSwitchLabel($"{pattern} when {expression} && B1 && B2", "when"), WrapInSwitchLabel($"{expected} when B1 && B2")); + await ValidateAsync(WrapInSwitchLabel($"{pattern} when {expression} && B1 && B2", "case"), WrapInSwitchLabel($"{expected} when B1 && B2")); + await ValidateAsync(WrapInIfStatement($"B1 && this is {pattern} [||]&& {expression} && B2"), WrapInIfStatement($"B1 && this is {expected} && B2")); + + Task ValidateAsync(string initialMarkup, string expectedMarkup) + => VerifyAsync(initialMarkup, expectedMarkup, skipCodeActionValidation); + } + + private static string WrapInIfStatement(string actual, string? entry = null) + { + var markup = +@" + if (" + actual + @") {} +"; + return CreateMarkup(markup, entry); + } + + private static string WrapInSwitchArm(string actual, string? entry = null) + { + var markup = +@" + _ = this switch + { + " + actual + @" => 0 + }; +"; + return CreateMarkup(markup, entry); + } + + private static string WrapInSwitchLabel(string actual, string? entry = null) + { + var markup = +@" + switch (this) + { + case " + actual + @": + break; + }; +"; + return CreateMarkup(markup, entry); + } + + private static string CreateMarkup(string actual, string? entry = null) + { + var markup = @" +namespace NS +{ + class C : B + { + void Test() + { + " + actual + @" + } + } + class B + { + public const C n = null; + public C a, b, c, d; + public int P1, P2, P3; + public bool B1, B2; + public C CP1, CP2; + public static C SCP1, SCP2; + public static int SP1, SP2; + public C m() { return null; } + } +}"; + return entry is null ? markup : markup.Replace(entry, "[||]" + entry); + } + } +} diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs index 18a76261af6fc..2f72a34b5aefa 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/AwaitCompletionProviderTests.cs @@ -58,7 +58,7 @@ class C void F() { $$ } -}", LanguageVersion.CSharp9, CSharpFeaturesResources.Make_container_async); +}", LanguageVersion.CSharp9, FeaturesResources.Make_containing_scope_async); } [Fact] @@ -102,7 +102,7 @@ Task F() { var z = $$ } } -", LanguageVersion.CSharp9, CSharpFeaturesResources.Make_container_async); +", LanguageVersion.CSharp9, FeaturesResources.Make_containing_scope_async); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 8124ce39c04a1..e26eb7e97eeca 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -2192,6 +2192,99 @@ void M(String parameter) await VerifyItemIsAbsentAsync(markup, "parameter"); } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + public async Task AfterStaticLocalFunction_TypeOnly() + { + var markup = @" +using System; +class C +{ + void M(String parameter) + { + static $$ + } +} +"; + await VerifyItemExistsAsync(markup, "String"); + await VerifyItemIsAbsentAsync(markup, "parameter"); + } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.Completion)] + [InlineData("extern")] + [InlineData("static extern")] + [InlineData("extern static")] + [InlineData("async")] + [InlineData("static async")] + [InlineData("async static")] + [InlineData("unsafe")] + [InlineData("static unsafe")] + [InlineData("unsafe static")] + [InlineData("async unsafe")] + [InlineData("unsafe async")] + [InlineData("unsafe extern")] + [InlineData("extern unsafe")] + [InlineData("extern unsafe async static")] + public async Task AfterLocalFunction_TypeOnly(string keyword) + { + var markup = $@" +using System; +class C +{{ + void M(String parameter) + {{ + {keyword} $$ + }} +}} +"; + await VerifyItemExistsAsync(markup, "String"); + await VerifyItemIsAbsentAsync(markup, "parameter"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + public async Task NotAfterAsyncLocalFunctionWithTwoAsyncs() + { + var markup = @" +using System; +class C +{ + void M(String parameter) + { + async async $$ + } +} +"; + await VerifyItemIsAbsentAsync(markup, "String"); + await VerifyItemIsAbsentAsync(markup, "parameter"); + } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.Completion)] + [InlineData("void")] + [InlineData("string")] + [InlineData("String")] + [InlineData("(int, int)")] + [InlineData("async void")] + [InlineData("async System.Threading.Tasks.Task")] + [InlineData("int Function")] + public async Task NotAfterReturnTypeInLocalFunction(string returnType) + { + var markup = @$" +using System; +class C +{{ + void M(String parameter) + {{ + static {returnType} $$ + }} +}} +"; + await VerifyItemIsAbsentAsync(markup, "String"); + await VerifyItemIsAbsentAsync(markup, "parameter"); + } + [Fact, Trait(Traits.Feature, Traits.Features.Completion)] [WorkItem(25569, "https://github.com/dotnet/roslyn/issues/25569")] public async Task AfterRefInMemberContext_TypeOnly() diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs index e96646c6dcb36..1f14a3e9dbf15 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/TypeImportCompletionProviderTests.cs @@ -1748,6 +1748,51 @@ public void M() await VerifyProviderCommitAsync(markup, "C", expected, commitChar: commitChar, sourceCodeKind: SourceCodeKind.Regular); } + [InlineData(SourceCodeKind.Regular)] + [InlineData(SourceCodeKind.Script)] + [WpfTheory, Trait(Traits.Feature, Traits.Features.Completion)] + [WorkItem(54493, "https://github.com/dotnet/roslyn/issues/54493")] + public async Task CommitInLocalFunctionContext(SourceCodeKind kind) + { + var markup = @" +namespace Foo +{ + public class MyClass { } +} + +namespace Test +{ + class Program + { + public static void Main() + { + static $$ + } + } +}"; + + var expectedCodeAfterCommit = @" +using Foo; + +namespace Foo +{ + public class MyClass { } +} + +namespace Test +{ + class Program + { + public static void Main() + { + static MyClass + } + } +}"; + + await VerifyProviderCommitAsync(markup, "MyClass", expectedCodeAfterCommit, commitChar: null, sourceCodeKind: kind); + } + private Task VerifyTypeImportItemExistsAsync(string markup, string expectedItem, int glyph, string inlineDescription, string displayTextSuffix = null, string expectedDescriptionOrNull = null, CompletionItemFlags? flags = null) => VerifyItemExistsAsync(markup, expectedItem, displayTextSuffix: displayTextSuffix, glyph: glyph, inlineDescription: inlineDescription, expectedDescriptionOrNull: expectedDescriptionOrNull, isComplexTextEdit: true, flags: flags); diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs index 0a6e961cae651..81f1e446c557c 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateType/GenerateTypeTests.cs @@ -3477,6 +3477,36 @@ static void Main(string[] args) } } +internal class Goo +{ +}", +index: 1); + } + + [WorkItem(54493, "https://github.com/dotnet/roslyn/pull/54493")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateType)] + public async Task TestInLocalFunction() + { + await TestInRegularAndScriptAsync( +@"using System; + +class Program +{ + static void Main(string[] args) + { + static [|Goo|] + } +}", +@"using System; + +class Program +{ + static void Main(string[] args) + { + static Goo + } +} + internal class Goo { }", diff --git a/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs b/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs index 80b636ce7a8ed..c03108654b299 100644 --- a/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs +++ b/src/EditorFeatures/CSharpTest/DocumentationComments/DocumentationCommentTests.cs @@ -165,6 +165,139 @@ public void TypingCharacter_Method() VerifyTypingCharacter(code, expected); } + [WpfFact, Trait(Traits.Feature, Traits.Features.DocumentationComments)] + [WorkItem(54245, "https://github.com/dotnet/roslyn/issues/54245")] + public void TypingCharacter_Method_WithExceptions() + { + var code = +@"class C +{ + //$$ + int M(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + return 0; + } +}"; + + var expected = +@"class C +{ + /// + /// $$ + /// + /// + /// + /// + /// + int M(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + return 0; + } +}"; + + VerifyTypingCharacter(code, expected); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.DocumentationComments)] + [WorkItem(54245, "https://github.com/dotnet/roslyn/issues/54245")] + public void TypingCharacter_Constructor_WithExceptions() + { + var code = +@"class C +{ + //$$ + public C(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + throw null; + throw null; + } +}"; + + var expected = +@"class C +{ + /// + /// $$ + /// + /// + /// + /// + public C(int goo) + { + if (goo < 0) throw new /*leading trivia*/Exception/*trailing trivia*/(); + throw null; + throw null; + } +}"; + + VerifyTypingCharacter(code, expected); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.DocumentationComments)] + [WorkItem(54245, "https://github.com/dotnet/roslyn/issues/54245")] + public void TypingCharacter_Constructor_WithExceptions_Caught() + { + // This result is wrong, but we can't do better as long as we only check syntax. + var code = @" +using System; + +class C +{ + //$$ + public C(int goo) + { + try + { + if (goo == 10) + throw new Exception(); + if (goo == 9) + throw new ArgumentOutOfRangeException(); + } + catch (ArgumentException) + { + } + + throw null; + throw null; + } +}"; + + var expected = @" +using System; + +class C +{ + /// + /// $$ + /// + /// + /// + /// + /// + public C(int goo) + { + try + { + if (goo == 10) + throw new Exception(); + if (goo == 9) + throw new ArgumentOutOfRangeException(); + } + catch (ArgumentException) + { + } + + throw null; + throw null; + } +}"; + + VerifyTypingCharacter(code, expected); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.DocumentationComments)] public void TypingCharacter_Method_WithVerbatimParams() { diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index f1a2f3229de37..2d984a7680230 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -23,7 +23,27 @@ public class TopLevelEditingTests : EditingTestBase #region Usings [Fact] - public void UsingDelete1() + public void Using_Global_Insert() + { + var src1 = @" +using System.Collections.Generic; +"; + var src2 = @" +global using D = System.Diagnostics; +global using System.Collections; +using System.Collections.Generic; +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [global using D = System.Diagnostics;]@2", + "Insert [global using System.Collections;]@40"); + + edits.VerifyRudeDiagnostics(); + } + + [Fact] + public void Using_Delete1() { var src1 = @" using System.Diagnostics; @@ -37,7 +57,7 @@ public void UsingDelete1() } [Fact] - public void UsingDelete2() + public void Using_Delete2() { var src1 = @" using D = System.Diagnostics; @@ -57,7 +77,7 @@ public void UsingDelete2() } [Fact] - public void UsingInsert() + public void Using_Insert() { var src1 = @" using System.Collections.Generic; @@ -77,7 +97,7 @@ public void UsingInsert() } [Fact] - public void UsingUpdate1() + public void Using_Update1() { var src1 = @" using System.Diagnostics; @@ -98,7 +118,7 @@ public void UsingUpdate1() } [Fact] - public void UsingUpdate2() + public void Using_Update2() { var src1 = @" using System.Diagnostics; @@ -119,7 +139,7 @@ public void UsingUpdate2() } [Fact] - public void UsingUpdate3() + public void Using_Update3() { var src1 = @" using System.Diagnostics; @@ -140,7 +160,7 @@ public void UsingUpdate3() } [Fact] - public void UsingReorder1() + public void Using_Reorder1() { var src1 = @" using System.Diagnostics; @@ -159,7 +179,7 @@ public void UsingReorder1() } [Fact] - public void UsingInsertDelete1() + public void Using_InsertDelete1() { var src1 = @" namespace N @@ -189,7 +209,7 @@ namespace M } [Fact] - public void UsingInsertDelete2() + public void Using_InsertDelete2() { var src1 = @" namespace N @@ -5204,8 +5224,8 @@ public void Namespace_Insert() edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "namespace C", FeaturesResources.namespace_)); - } + [Fact] public void Namespace_InsertNested() { @@ -5222,7 +5242,7 @@ public void Namespace_InsertNested() } [Fact] - public void NamespaceDelete() + public void Namespace_DeleteNested() { var src1 = @"namespace C { namespace D { } }"; var src2 = @"namespace C { }"; @@ -5237,7 +5257,7 @@ public void NamespaceDelete() } [Fact] - public void NamespaceMove1() + public void Namespace_Move() { var src1 = @"namespace C { namespace D { } }"; var src2 = @"namespace C { } namespace D { }"; @@ -5252,7 +5272,7 @@ public void NamespaceMove1() } [Fact] - public void NamespaceReorder1() + public void Namespace_Reorder1() { var src1 = @"namespace C { namespace D { } class T { } namespace E { } }"; var src2 = @"namespace C { namespace E { } class T { } namespace D { } }"; @@ -5267,7 +5287,7 @@ public void NamespaceReorder1() } [Fact] - public void NamespaceReorder2() + public void Namespace_Reorder2() { var src1 = @"namespace C { namespace D1 { } namespace D2 { } namespace D3 { } class T { } namespace E { } }"; var src2 = @"namespace C { namespace E { } class T { } namespace D1 { } namespace D2 { } namespace D3 { } }"; @@ -5281,6 +5301,36 @@ public void NamespaceReorder2() edits.VerifyRudeDiagnostics(); } + [Fact] + public void Namespace_FileScoped_Insert() + { + var src1 = @""; + var src2 = @"namespace C;"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [namespace C;]@0"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "namespace C", FeaturesResources.namespace_)); + } + + [Fact] + public void Namespace_FileScoped_Delete() + { + var src1 = @"namespace C;"; + var src2 = @""; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Delete [namespace C;]@0"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, null, FeaturesResources.namespace_)); + } + #endregion #region Members diff --git a/src/EditorFeatures/CSharpTest/EditorConfigSettings/Aggregator/SettingsAggregatorTests.cs b/src/EditorFeatures/CSharpTest/EditorConfigSettings/Aggregator/SettingsAggregatorTests.cs index 42691dca0a0a9..14e5707d42a93 100644 --- a/src/EditorFeatures/CSharpTest/EditorConfigSettings/Aggregator/SettingsAggregatorTests.cs +++ b/src/EditorFeatures/CSharpTest/EditorConfigSettings/Aggregator/SettingsAggregatorTests.cs @@ -48,6 +48,6 @@ private static void TestGettingProvider() public void TestGettingAnalyzerProvider() => TestGettingProvider(); [Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)] - public void TestGettingFormattingProvider() => TestGettingProvider(); + public void TestGettingWhitespaceProvider() => TestGettingProvider(); } } diff --git a/src/EditorFeatures/CSharpTest/EditorConfigSettings/DataProvider/DataProviderTests.cs b/src/EditorFeatures/CSharpTest/EditorConfigSettings/DataProvider/DataProviderTests.cs index 5bc456ab6687f..e28bb02dee185 100644 --- a/src/EditorFeatures/CSharpTest/EditorConfigSettings/DataProvider/DataProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/EditorConfigSettings/DataProvider/DataProviderTests.cs @@ -67,10 +67,10 @@ public void TestGettingCodeStyleSettingsProvider() } [Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)] - public void TestGettingFormattingSettingsProvider() + public void TestGettingWhitespaceSettingsProvider() { - TestGettingSettingsProviderFromWorkspace(); - TestGettingSettingsProviderFromLanguageService(); + TestGettingSettingsProviderFromWorkspace(); + TestGettingSettingsProviderFromLanguageService(); } [Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)] @@ -112,9 +112,9 @@ public void TestGettingCodeStyleSettingsProviderLanguageServiceAsync() } [Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)] - public void TestGettingFormattingSettingProviderWorkspaceServiceAsync() + public void TestGettingWhitespaceSettingProviderWorkspaceServiceAsync() { - var settingsProviderFactory = GettingSettingsProviderFactoryFromWorkspace(); + var settingsProviderFactory = GettingSettingsProviderFactoryFromWorkspace(); var settingsProvider = settingsProviderFactory.GetForFile("/a/b/config"); var model = new TestViewModel(); settingsProvider.RegisterViewModel(model); @@ -123,9 +123,9 @@ public void TestGettingFormattingSettingProviderWorkspaceServiceAsync() } [Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)] - public void TestGettingFormattingSettingProviderLanguageServiceAsync() + public void TestGettingWhitespaceSettingProviderLanguageServiceAsync() { - var settingsProviderFactory = GettingSettingsProviderFactoryFromLanguageService(LanguageNames.CSharp); + var settingsProviderFactory = GettingSettingsProviderFactoryFromLanguageService(LanguageNames.CSharp); var settingsProvider = settingsProviderFactory.GetForFile("/a/b/config"); var model = new TestViewModel(); settingsProvider.RegisterViewModel(model); diff --git a/src/EditorFeatures/CSharpTest/EditorConfigSettings/Updater/SettingsUpdaterTests.cs b/src/EditorFeatures/CSharpTest/EditorConfigSettings/Updater/SettingsUpdaterTests.cs index de0a4df83da3f..97604f9d5d4f5 100644 --- a/src/EditorFeatures/CSharpTest/EditorConfigSettings/Updater/SettingsUpdaterTests.cs +++ b/src/EditorFeatures/CSharpTest/EditorConfigSettings/Updater/SettingsUpdaterTests.cs @@ -129,7 +129,7 @@ internal async Task TestAddNewAnalyzerOptionOptionAsync( var id = "Test001"; var descriptor = new DiagnosticDescriptor(id: id, title: "", messageFormat: "", category: "Naming", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: false); - var analyzerSetting = new AnalyzerSetting(descriptor, ReportDiagnostic.Suppress, null!, language); + var analyzerSetting = new AnalyzerSetting(descriptor, ReportDiagnostic.Suppress, null!, language, new SettingLocation(EditorConfigSettings.LocationKind.VisualStudio, null)); await TestAsync( string.Empty, @@ -328,7 +328,7 @@ public async Task TestAnalyzerSettingsUpdaterService() var updater = new AnalyzerSettingsUpdater(workspace, "/a/b/config"); var id = "Test001"; var descriptor = new DiagnosticDescriptor(id: id, title: "", messageFormat: "", category: "Naming", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: false); - var analyzerSetting = new AnalyzerSetting(descriptor, ReportDiagnostic.Suppress, updater, Language.CSharp); + var analyzerSetting = new AnalyzerSetting(descriptor, ReportDiagnostic.Suppress, updater, Language.CSharp, new SettingLocation(EditorConfigSettings.LocationKind.VisualStudio, null)); analyzerSetting.ChangeSeverity(DiagnosticSeverity.Error); var updates = await updater.GetChangedEditorConfigAsync(default); var update = Assert.Single(updates); @@ -346,7 +346,8 @@ public async Task TestCodeStyleSettingUpdaterService() "", editorOptions, workspace.Options, - updater); + updater, + null!); setting.ChangeSeverity(DiagnosticSeverity.Error); var updates = await updater.GetChangedEditorConfigAsync(default); var update = Assert.Single(updates); @@ -363,11 +364,11 @@ public async Task TestCodeStyleSettingUpdaterService() } [Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)] - public async Task TestFormattingSettingUpdaterService() + public async Task TestWhitespaceSettingUpdaterService() { var workspace = CreateWorkspaceWithProjectAndDocuments(); var updater = new OptionUpdater(workspace, "/a/b/config"); - var setting = FormattingSetting.Create(CSharpFormattingOptions2.NewLineForElse, "", TestAnalyzerConfigOptions.Instance, workspace.Options, updater); + var setting = WhitespaceSetting.Create(CSharpFormattingOptions2.NewLineForElse, "", TestAnalyzerConfigOptions.Instance, workspace.Options, updater, null!); setting.SetValue(false); var updates = await updater.GetChangedEditorConfigAsync(default); var update = Assert.Single(updates); diff --git a/src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyTests.cs b/src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyTests.cs index 576268ba8a189..0972bcbfd8125 100644 --- a/src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyTests.cs +++ b/src/EditorFeatures/CSharpTest/FullyQualify/FullyQualifyTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp.CodeFixes.FullyQualify; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; @@ -1496,5 +1497,155 @@ await TestInRegularAndScriptAsync( @"using M = [|Math|]", @"using M = System.Math"); } + + [Fact] + [WorkItem(54544, "https://github.com/dotnet/roslyn/issues/54544")] + public async Task TestAddUsingsEditorBrowsableNeverSameProject() + { + const string InitialWorkspace = @" + + + +using System.ComponentModel; +namespace ProjectLib +{ + [EditorBrowsable(EditorBrowsableState.Never)] + public class Project + { + } +} + + +class Program +{ + static void Main(string[] args) + { + Project p = new [|Project()|]; + } +} + + +"; + + const string ExpectedDocumentText = @" +class Program +{ + static void Main(string[] args) + { + Project p = new [|ProjectLib.Project()|]; + } +} +"; + + await TestInRegularAndScript1Async(InitialWorkspace, ExpectedDocumentText); + } + + [Fact] + [WorkItem(54544, "https://github.com/dotnet/roslyn/issues/54544")] + public async Task TestAddUsingsEditorBrowsableNeverDifferentProject() + { + const string InitialWorkspace = @" + + + +imports System.ComponentModel +namespace ProjectLib + <EditorBrowsable(EditorBrowsableState.Never)> + public class Project + end class +end namespace + + + + lib + +class Program +{ + static void Main(string[] args) + { + [|Project|] p = new Project(); + } +} + + +"; + await TestMissingAsync(InitialWorkspace); + } + + [Fact] + [WorkItem(54544, "https://github.com/dotnet/roslyn/issues/54544")] + public async Task TestAddUsingsEditorBrowsableAdvancedDifferentProjectOptionOn() + { + const string InitialWorkspace = @" + + + +imports System.ComponentModel +namespace ProjectLib + <EditorBrowsable(EditorBrowsableState.Advanced)> + public class Project + end class +end namespace + + + + lib + +class Program +{ + static void Main(string[] args) + { + [|Project|] p = new Project(); + } +} + + +"; + + const string ExpectedDocumentText = @" +class Program +{ + static void Main(string[] args) + { + ProjectLib.Project p = new Project(); + } +} +"; + await TestInRegularAndScript1Async(InitialWorkspace, ExpectedDocumentText); + } + + [Fact] + [WorkItem(54544, "https://github.com/dotnet/roslyn/issues/54544")] + public async Task TestAddUsingsEditorBrowsableAdvancedDifferentProjectOptionOff() + { + const string InitialWorkspace = @" + + + +imports System.ComponentModel +namespace ProjectLib + <EditorBrowsable(EditorBrowsableState.Advanced)> + public class Project + end class +end namespace + + + + lib + +class Program +{ + static void Main(string[] args) + { + [|Project|] p = new Project(); + } +} + + +"; + + await TestMissingAsync(InitialWorkspace, new TestParameters( + options: Option(CompletionOptions.HideAdvancedMembers, true))); + } } } diff --git a/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs b/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs index b8e700eafc5fb..256db27b5b997 100644 --- a/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs +++ b/src/EditorFeatures/CSharpTest/MakeMemberStatic/MakeMemberStaticTests.cs @@ -2,53 +2,65 @@ // 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.MakeMemberStatic; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.MakeMemberStatic; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Roslyn.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.MakeMemberStatic { - public class MakeMemberStaticTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + using VerifyCS = CSharpCodeFixVerifier< + EmptyDiagnosticAnalyzer, + CSharpMakeMemberStaticCodeFixProvider>; + + public class MakeMemberStaticTests { - public MakeMemberStaticTests(ITestOutputHelper logger) - : base(logger) + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] + public async Task TestField() { + await VerifyCS.VerifyCodeFixAsync( +@" +public static class Foo +{ + int {|CS0708:i|}; +}", +@" +public static class Foo +{ + static int i; +}"); } - internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (null, new CSharpMakeMemberStaticCodeFixProvider()); - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] - public async Task TestField() + [WorkItem(54202, "https://github.com/dotnet/roslyn/issues/54202")] + public async Task TestTrivia() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - int [||]i; + // comment + readonly int {|CS0708:i|}; }", @" public static class Foo { - static int i; + // comment + static readonly int i; }"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task TestMethod() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - void [||]M() { } + void {|CS0708:M|}() { } }", @" public static class Foo @@ -60,11 +72,11 @@ static void M() { } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task TestProperty() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - object [||]P { get; set; } + object {|CS0708:P|} { get; set; } }", @" public static class Foo @@ -76,11 +88,11 @@ public static class Foo [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task TestEventField() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @" public static class Foo { - event System.Action [||]E; + event System.Action {|CS0708:E|}; }", @" public static class Foo @@ -92,15 +104,15 @@ public static class Foo [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMemberStatic)] public async Task FixAll() { - await TestInRegularAndScript1Async( + await VerifyCS.VerifyCodeFixAsync( @"namespace NS { public static class Foo { - int {|FixAllInDocument:|}i; - void M() { } - object P { get; set; } - event System.Action E; + int {|CS0708:i|}; + void {|CS0708:M|}() { } + object {|CS0708:P|} { get; set; } + event System.Action {|CS0708:E|}; } }", @"namespace NS diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs index 13b2c49012e62..af28800b42683 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToSearcherTests.cs @@ -54,8 +54,8 @@ private static IReturnsResult SetupSearchProject( return searchServiceCallback.ReturnsAsync(isFullyLoaded ? NavigateToSearchLocation.Latest : NavigateToSearchLocation.Cache); } - private static ValueTask<(bool projectSystem, bool remoteHost)> IsFullyLoadedAsync(bool projectSystem, bool remoteHost) - => new((projectSystem, remoteHost)); + private static ValueTask IsFullyLoadedAsync(bool projectSystem, bool remoteHost) + => new(projectSystem && remoteHost); [Fact] public async Task NotFullyLoadedOnlyMakesOneSearchProjectCallIfValueReturned() @@ -121,8 +121,8 @@ public async Task NotFullyLoadedMakesTwoSearchProjectCallIfValueNotReturned(bool callbackMock.Setup(c => c.AddItemAsync(It.IsAny(), result, It.IsAny())) .Returns(Task.CompletedTask); - // Because we did a full search, the accuracy is dependent on it the project system was fully loaded or not. - callbackMock.Setup(c => c.Done(projectSystemFullyLoaded)); + // Because the remote host wasn't fully loaded, we still notify that our results may be incomplete. + callbackMock.Setup(c => c.Done(false)); var searcher = NavigateToSearcher.Create( workspace.CurrentSolution, @@ -139,7 +139,7 @@ public async Task NotFullyLoadedMakesTwoSearchProjectCallIfValueNotReturned(bool [Theory] [CombinatorialData] - public async Task NotFullyLoadedStillReportsAsFullyCompletedIfSecondCallReturnsNothing(bool projectIsFullyLoaded) + public async Task NotFullyLoadedStillReportsAsNotCompleteIfRemoteHostIsStillHydrating(bool projectIsFullyLoaded) { using var workspace = TestWorkspace.CreateCSharp(""); @@ -161,8 +161,8 @@ public async Task NotFullyLoadedStillReportsAsFullyCompletedIfSecondCallReturnsN var callbackMock = new Mock(MockBehavior.Strict); callbackMock.Setup(c => c.ReportProgress(It.IsAny(), It.IsAny())); - // Because we did a full search, the accuracy is dependent on it the project system was fully loaded or not. - callbackMock.Setup(c => c.Done(projectIsFullyLoaded)); + // Because the remote host wasn't fully loaded, we still notify that our results may be incomplete. + callbackMock.Setup(c => c.Done(false)); var searcher = NavigateToSearcher.Create( workspace.CurrentSolution, diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs index 67cccdfe229a4..71e5f8aab7b27 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs @@ -783,5 +783,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs index 6b7d46bc2e69f..8686818a987f2 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs @@ -784,5 +784,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs index 338137a0ceb48..fa179c7e93540 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs @@ -813,5 +813,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs index b83e881fec04d..eb98085202702 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs @@ -777,5 +777,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs index 2f132c0a1dc3b..acdb5883a471b 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs @@ -294,10 +294,19 @@ public async Task TestNotAfterSealed() [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterStatic() { - await VerifyAbsenceAsync(SourceCodeKind.Regular, @"static $$"); + await VerifyKeywordAsync(SourceCodeKind.Regular, @"static $$"); await VerifyKeywordAsync(SourceCodeKind.Script, @"static $$"); } + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(SourceCodeKind.Regular, AddInsideMethod(@$"{keyword} $$")); + await VerifyKeywordAsync(SourceCodeKind.Script, AddInsideMethod(@$"{keyword} $$")); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterStaticPublic() { diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs index 1a5b23df05929..bd04fa4a2f3fa 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs @@ -761,5 +761,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs index b5d28f25ecb83..6c52ebf7ee041 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs @@ -734,5 +734,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs index 1085ae1c12661..0dd36b867b29e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs @@ -848,5 +848,14 @@ public async Task TestInMixedDeclarationAndAssignmentInDeconstruction() await VerifyKeywordAsync(AddInsideMethod( @"(x, $$) = (0, 0);")); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs index 503d3a934b18f..cc983fe3ba1b3 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs @@ -761,5 +761,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs index b672501179bb3..6a378ecc87650 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations @@ -299,5 +300,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs index 4d045c5e04894..ff28969aa5903 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs @@ -782,5 +782,14 @@ public async Task TestInMixedDeclarationAndAssignmentInDeconstruction() await VerifyKeywordAsync(AddInsideMethod( @"(x, $$) = (0, 0);")); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs index 4c16c296891da..876c29629d8cc 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs @@ -761,5 +761,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs index 5893d3520dd82..4a9954324184d 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs @@ -757,5 +757,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs index 26f59dc77585e..8db8c3f58ae4f 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs @@ -174,6 +174,15 @@ await VerifyKeywordAsync(AddInsideMethod( @"ref readonly $$ int Function();")); } + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } + [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAfterRefExpression() { diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/TheoryDataKeywordsIndicatingLocalFunction.cs b/src/EditorFeatures/CSharpTest2/Recommendations/TheoryDataKeywordsIndicatingLocalFunction.cs new file mode 100644 index 0000000000000..b2bac2fac98f5 --- /dev/null +++ b/src/EditorFeatures/CSharpTest2/Recommendations/TheoryDataKeywordsIndicatingLocalFunction.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations +{ + internal class TheoryDataKeywordsIndicatingLocalFunction : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { "extern" }; + yield return new object[] { "static extern" }; + yield return new object[] { "extern static" }; + yield return new object[] { "async" }; + yield return new object[] { "static async" }; + yield return new object[] { "async static" }; + yield return new object[] { "unsafe" }; + yield return new object[] { "static unsafe" }; + yield return new object[] { "unsafe static" }; + yield return new object[] { "async unsafe" }; + yield return new object[] { "unsafe async" }; + yield return new object[] { "unsafe extern" }; + yield return new object[] { "extern unsafe" }; + yield return new object[] { "extern unsafe async static" }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs index b1b76c718ca3f..bff4e5363d3cc 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs @@ -761,5 +761,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs index 794801e107d29..c86726fbeb686 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs @@ -761,5 +761,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs index c382f7d9e1f31..24e2874d89e16 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs @@ -761,5 +761,14 @@ class C { delegate*$$"); } + + [WorkItem(53585, "https://github.com/dotnet/roslyn/issues/53585")] + [Theory, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] + [ClassData(typeof(TheoryDataKeywordsIndicatingLocalFunction))] + public async Task TestAfterKeywordIndicatingLocalFunction(string keyword) + { + await VerifyKeywordAsync(AddInsideMethod($@" +{keyword} $$")); + } } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs b/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs index 99cdba9265e4d..b27ae0c543282 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Aggregator/SettingsAggregator.cs @@ -15,14 +15,14 @@ internal partial class SettingsAggregator : ISettingsAggregator { private readonly Workspace _workspace; private readonly ISettingsProviderFactory _analyzerProvider; - private ISettingsProviderFactory _formattingProvider; + private ISettingsProviderFactory _whitespaceProvider; private ISettingsProviderFactory _codeStyleProvider; public SettingsAggregator(Workspace workspace) { _workspace = workspace; _workspace.WorkspaceChanged += UpdateProviders; - _formattingProvider = GetOptionsProviderFactory(_workspace); + _whitespaceProvider = GetOptionsProviderFactory(_workspace); _codeStyleProvider = GetOptionsProviderFactory(_workspace); _analyzerProvider = GetOptionsProviderFactory(_workspace); } @@ -39,7 +39,7 @@ private void UpdateProviders(object? sender, WorkspaceChangeEventArgs e) case WorkspaceChangeKind.ProjectAdded: case WorkspaceChangeKind.ProjectRemoved: case WorkspaceChangeKind.ProjectChanged: - _formattingProvider = GetOptionsProviderFactory(_workspace); + _whitespaceProvider = GetOptionsProviderFactory(_workspace); _codeStyleProvider = GetOptionsProviderFactory(_workspace); break; default: @@ -54,9 +54,9 @@ private void UpdateProviders(object? sender, WorkspaceChangeEventArgs e) return (ISettingsProvider)_analyzerProvider.GetForFile(fileName); } - if (typeof(TData) == typeof(FormattingSetting)) + if (typeof(TData) == typeof(WhitespaceSetting)) { - return (ISettingsProvider)_formattingProvider.GetForFile(fileName); + return (ISettingsProvider)_whitespaceProvider.GetForFile(fileName); } if (typeof(TData) == typeof(CodeStyleSetting)) diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/AnalyzerSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/AnalyzerSetting.cs index 88cf546360002..e9feee04f0465 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/AnalyzerSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/AnalyzerSetting.cs @@ -17,7 +17,8 @@ internal class AnalyzerSetting public AnalyzerSetting(DiagnosticDescriptor descriptor, ReportDiagnostic effectiveSeverity, AnalyzerSettingsUpdater settingsUpdater, - Language language) + Language language, + SettingLocation location) { _descriptor = descriptor; _settingsUpdater = settingsUpdater; @@ -35,6 +36,7 @@ public AnalyzerSetting(DiagnosticDescriptor descriptor, IsEnabled = enabled; Severity = severity; Language = language; + Location = location; } public string Id => _descriptor.Id; @@ -44,6 +46,7 @@ public AnalyzerSetting(DiagnosticDescriptor descriptor, public DiagnosticSeverity Severity { get; private set; } public bool IsEnabled { get; private set; } public Language Language { get; } + public SettingLocation Location { get; } internal void ChangeSeverity(DiagnosticSeverity severity) { diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.BooleanCodeStyleSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.BooleanCodeStyleSetting.cs index 66d5d9392db3b..4cfb39fb8a957 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.BooleanCodeStyleSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.BooleanCodeStyleSetting.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; using Microsoft.CodeAnalysis.Options; +using Microsoft.VisualStudio.OLE.Interop; namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data { @@ -23,19 +24,24 @@ public BooleanCodeStyleSetting(Option2> option, string? falseValueDescription, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) + OptionUpdater updater, + string fileName) : base(description, option.Group.Description, trueValueDescription, falseValueDescription, updater) { _option = option; _editorConfigOptions = editorConfigOptions; _visualStudioOptions = visualStudioOptions; + Location = new SettingLocation(IsDefinedInEditorConfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, fileName); } public override bool IsDefinedInEditorConfig => _editorConfigOptions.TryGetEditorConfigOption>(_option, out _); + public override SettingLocation Location { get; protected set; } + protected override void ChangeSeverity(NotificationOption2 severity) { ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithNotification(severity)); } @@ -43,6 +49,7 @@ public override void ChangeValue(int valueIndex) { var value = valueIndex == 0; ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithValue(value)); } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.EnumCodeStyleSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.EnumCodeStyleSetting.cs index a9888a218e2af..dda0a520aff7e 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.EnumCodeStyleSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.EnumCodeStyleSetting.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; using Microsoft.CodeAnalysis.Options; +using Microsoft.VisualStudio.OLE.Interop; namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data { @@ -25,25 +26,31 @@ public EnumCodeStyleSetting(Option2> option, string[] valueDescriptions, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) + OptionUpdater updater, + string fileName) : base(description, enumValues, valueDescriptions, option.Group.Description, updater) { _option = option; _editorConfigOptions = editorConfigOptions; _visualStudioOptions = visualStudioOptions; + Location = new SettingLocation(IsDefinedInEditorConfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, fileName); } public override bool IsDefinedInEditorConfig => _editorConfigOptions.TryGetEditorConfigOption>(_option, out _); + public override SettingLocation Location { get; protected set; } + protected override void ChangeSeverity(NotificationOption2 severity) { ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithNotification(severity)); } public override void ChangeValue(int valueIndex) { ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithValue(_enumValues[valueIndex])); } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageBooleanCodeStyleSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageBooleanCodeStyleSetting.cs index 878798c60e44c..cfa50eee109cf 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageBooleanCodeStyleSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageBooleanCodeStyleSetting.cs @@ -6,6 +6,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; using Microsoft.CodeAnalysis.Options; +using Microsoft.VisualStudio.OLE.Interop; namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data { @@ -23,19 +24,24 @@ public PerLanguageBooleanCodeStyleSetting(PerLanguageOption2 _editorConfigOptions.TryGetEditorConfigOption>(_option, out _); + public override SettingLocation Location { get; protected set; } + protected override void ChangeSeverity(NotificationOption2 severity) { ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithNotification(severity)); } @@ -43,6 +49,7 @@ public override void ChangeValue(int valueIndex) { var value = valueIndex == 0; ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithValue(value)); } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageEnumCodeStyleSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageEnumCodeStyleSetting.cs index c007ff27961de..e66510f24f026 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageEnumCodeStyleSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.PerLanguageEnumCodeStyleSetting.cs @@ -25,25 +25,32 @@ public PerLanguageEnumCodeStyleSetting(PerLanguageOption2> o string[] valueDescriptions, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) + OptionUpdater updater, + string fileName) : base(description, enumValues, valueDescriptions, option.Group.Description, updater) { _option = option; + _editorConfigOptions = editorConfigOptions; _visualStudioOptions = visualStudioOptions; + Location = new SettingLocation(IsDefinedInEditorConfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, fileName); } public override bool IsDefinedInEditorConfig => _editorConfigOptions.TryGetEditorConfigOption>(_option, out _); + public override SettingLocation Location { get; protected set; } + protected override void ChangeSeverity(NotificationOption2 severity) { ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithNotification(severity)); } public override void ChangeValue(int valueIndex) { ICodeStyleOption option = GetOption(); + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, option.WithValue(_enumValues[valueIndex])); } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.cs index c00bb71b00617..c5828ab43e5df 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/CodeStyle/CodeStyleSetting.cs @@ -23,6 +23,7 @@ internal abstract partial class CodeStyleSetting public abstract string GetCurrentValue(); public abstract DiagnosticSeverity Severity { get; } public abstract bool IsDefinedInEditorConfig { get; } + public abstract SettingLocation Location { get; protected set; } public CodeStyleSetting(string description, OptionUpdater updater) { @@ -52,10 +53,11 @@ internal static CodeStyleSetting Create(Option2> option, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updater, + string fileName, string? trueValueDescription = null, string? falseValueDescription = null) { - return new BooleanCodeStyleSetting(option, description, trueValueDescription, falseValueDescription, editorConfigOptions, visualStudioOptions, updater); + return new BooleanCodeStyleSetting(option, description, trueValueDescription, falseValueDescription, editorConfigOptions, visualStudioOptions, updater, fileName); } internal static CodeStyleSetting Create(PerLanguageOption2> option, @@ -63,10 +65,11 @@ internal static CodeStyleSetting Create(PerLanguageOption2(Option2> option, @@ -75,10 +78,11 @@ internal static CodeStyleSetting Create(Option2> option, string[] valueDescriptions, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) + OptionUpdater updater, + string fileName) where T : Enum { - return new EnumCodeStyleSetting(option, description, enumValues, valueDescriptions, editorConfigOptions, visualStudioOptions, updater); + return new EnumCodeStyleSetting(option, description, enumValues, valueDescriptions, editorConfigOptions, visualStudioOptions, updater, fileName); } internal static CodeStyleSetting Create(PerLanguageOption2> option, @@ -87,10 +91,11 @@ internal static CodeStyleSetting Create(PerLanguageOption2(option, description, enumValues, valueDescriptions, editorConfigOptions, visualStudioOptions, updater); + return new PerLanguageEnumCodeStyleSetting(option, description, enumValues, valueDescriptions, editorConfigOptions, visualStudioOptions, updater, fileName); } } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/PerLanguageFormattingSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/PerLanguageWhitespaceSetting.cs similarity index 87% rename from src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/PerLanguageFormattingSetting.cs rename to src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/PerLanguageWhitespaceSetting.cs index 0e61736243e49..3079c44230617 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/PerLanguageFormattingSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/PerLanguageWhitespaceSetting.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data { - internal sealed class PerLanguageFormattingSetting : FormattingSetting + internal sealed class PerLanguageWhitespaceSetting : WhitespaceSetting where T : notnull { private bool _isValueSet; @@ -46,12 +46,13 @@ private set private readonly AnalyzerConfigOptions _editorConfigOptions; private readonly OptionSet _visualStudioOptions; - public PerLanguageFormattingSetting(PerLanguageOption2 option, + public PerLanguageWhitespaceSetting(PerLanguageOption2 option, string description, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) - : base(description, updater) + OptionUpdater updater, + SettingLocation location) + : base(description, updater, location) { _option = option; _editorConfigOptions = editorConfigOptions; @@ -68,6 +69,7 @@ public PerLanguageFormattingSetting(PerLanguageOption2 option, public override void SetValue(object value) { Value = (T)value; + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, value); } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/FormattingSetting.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/WhitespaceSetting.cs similarity index 60% rename from src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/FormattingSetting.cs rename to src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/WhitespaceSetting.cs index a013f3ad40551..00170ee23bec9 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/FormattingSetting.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/WhitespaceSetting.cs @@ -9,15 +9,16 @@ namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data { - internal abstract class FormattingSetting + internal abstract class WhitespaceSetting { protected OptionUpdater Updater { get; } protected string? Language { get; } - protected FormattingSetting(string description, OptionUpdater updater, string? language = null) + protected WhitespaceSetting(string description, OptionUpdater updater, SettingLocation location, string? language = null) { Description = description ?? throw new ArgumentNullException(nameof(description)); Updater = updater; + Location = location; Language = language; } @@ -28,25 +29,32 @@ protected FormattingSetting(string description, OptionUpdater updater, string? l public abstract void SetValue(object value); public abstract object? GetValue(); public abstract bool IsDefinedInEditorConfig { get; } + public SettingLocation Location { get; protected set; } - public static PerLanguageFormattingSetting Create(PerLanguageOption2 option, + public static PerLanguageWhitespaceSetting Create(PerLanguageOption2 option, string description, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) + OptionUpdater updater, + string fileName) where TOption : notnull { - return new PerLanguageFormattingSetting(option, description, editorConfigOptions, visualStudioOptions, updater); + var isDefinedInEditorConfig = editorConfigOptions.TryGetEditorConfigOption(option, out _); + var location = new SettingLocation(isDefinedInEditorConfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, fileName); + return new PerLanguageWhitespaceSetting(option, description, editorConfigOptions, visualStudioOptions, updater, location); } - public static FormattingSetting Create(Option2 option, + public static WhitespaceSetting Create(Option2 option, string description, AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, - OptionUpdater updater) + OptionUpdater updater, + string fileName) where TOption : struct { - return new FormattingSetting(option, description, editorConfigOptions, visualStudioOptions, updater); + var isDefinedInEditorConfig = editorConfigOptions.TryGetEditorConfigOption(option, out _); + var location = new SettingLocation(isDefinedInEditorConfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, fileName); + return new WhitespaceSetting(option, description, editorConfigOptions, visualStudioOptions, updater, location); } } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/FormattingSetting`1.cs b/src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/WhitespaceSetting`1.cs similarity index 86% rename from src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/FormattingSetting`1.cs rename to src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/WhitespaceSetting`1.cs index d701147cc4dd7..c5e0cb7d2c5ac 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Data/Formatting/FormattingSetting`1.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Data/Whitespace/WhitespaceSetting`1.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data { - internal sealed class FormattingSetting : FormattingSetting + internal sealed class WhitespaceSetting : WhitespaceSetting where T : notnull { public override bool IsDefinedInEditorConfig => _options.TryGetEditorConfigOption(_option, out _); @@ -53,12 +53,13 @@ private set private readonly AnalyzerConfigOptions _options; private readonly OptionSet _visualStudioOptions; - public FormattingSetting(Option2 option, + public WhitespaceSetting(Option2 option, string description, AnalyzerConfigOptions options, OptionSet visualStudioOptions, - OptionUpdater updater) - : base(description, updater) + OptionUpdater updater, + SettingLocation location) + : base(description, updater, location) { _option = option; _options = options; @@ -68,6 +69,7 @@ public FormattingSetting(Option2 option, public override void SetValue(object value) { Value = (T)value; + Location = Location with { LocationKind = LocationKind.EditorConfig }; Updater.QueueUpdate(_option, value); } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs index 61f2157801f12..50e5bcc345121 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs @@ -61,8 +61,10 @@ IEnumerable ToAnalyzerSetting(IEnumerable a .Select(g => { var selectedDiagnostic = g.First(); + var isEditorconfig = selectedDiagnostic.IsDefinedInEditorConfig(editorConfigOptions); + var settingLocation = new SettingLocation(isEditorconfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, FileName); var severity = selectedDiagnostic.GetEffectiveSeverity(editorConfigOptions); - return new AnalyzerSetting(selectedDiagnostic, severity, SettingsUpdater, language); + return new AnalyzerSetting(selectedDiagnostic, severity, SettingsUpdater, language, settingLocation); }); } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/CodeStyle/CommonCodeStyleSettingsProvider.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/CodeStyle/CommonCodeStyleSettingsProvider.cs index adcba7ae06210..e063e36cdd429 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/CodeStyle/CommonCodeStyleSettingsProvider.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/CodeStyle/CommonCodeStyleSettingsProvider.cs @@ -49,100 +49,100 @@ protected override void UpdateOptions(AnalyzerConfigOptions editorConfigOptions, // TODO(jmarolf): set as stable } - private static IEnumerable GetQualifyCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetQualifyCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { yield return CodeStyleSetting.Create(option: CodeStyleOptions2.QualifyFieldAccess, description: EditorFeaturesResources.Qualify_field_access_with_this_or_Me, trueValueDescription: EditorFeaturesResources.Prefer_this_or_Me, falseValueDescription: EditorFeaturesResources.Do_not_prefer_this_or_Me, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.QualifyPropertyAccess, description: EditorFeaturesResources.Qualify_property_access_with_this_or_Me, trueValueDescription: EditorFeaturesResources.Prefer_this_or_Me, falseValueDescription: EditorFeaturesResources.Do_not_prefer_this_or_Me, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.QualifyMethodAccess, description: EditorFeaturesResources.Qualify_method_access_with_this_or_Me, trueValueDescription: EditorFeaturesResources.Prefer_this_or_Me, falseValueDescription: EditorFeaturesResources.Do_not_prefer_this_or_Me, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.QualifyEventAccess, description: EditorFeaturesResources.Qualify_event_access_with_this_or_Me, trueValueDescription: EditorFeaturesResources.Prefer_this_or_Me, falseValueDescription: EditorFeaturesResources.Do_not_prefer_this_or_Me, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } - private static IEnumerable GetPredefinedTypesCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetPredefinedTypesCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration, description: EditorFeaturesResources.For_locals_parameters_and_members, trueValueDescription: EditorFeaturesResources.Prefer_predefined_type, falseValueDescription: EditorFeaturesResources.Prefer_framework_type, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferIntrinsicPredefinedTypeKeywordInDeclaration, description: EditorFeaturesResources.For_member_access_expressions, trueValueDescription: EditorFeaturesResources.Prefer_predefined_type, falseValueDescription: EditorFeaturesResources.Prefer_framework_type, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } - private static IEnumerable GetNullCheckingCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetNullCheckingCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferCoalesceExpression, description: EditorFeaturesResources.Prefer_coalesce_expression, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferNullPropagation, description: EditorFeaturesResources.Prefer_null_propagation, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferIsNullCheckOverReferenceEqualityMethod, description: EditorFeaturesResources.Prefer_is_null_for_reference_equality_checks, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } - private static IEnumerable GetModifierCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetModifierCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferReadonly, description: EditorFeaturesResources.Prefer_readonly_fields, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } - private static IEnumerable GetCodeBlockCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetCodeBlockCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferAutoProperties, description: EditorFeaturesResources.analyzer_Prefer_auto_properties, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.PreferSystemHashCode, description: EditorFeaturesResources.Prefer_System_HashCode_in_GetHashCode, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } - private static IEnumerable GetExpressionCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetExpressionCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferObjectInitializer, description: EditorFeaturesResources.Prefer_object_initializer, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferCollectionInitializer, description: EditorFeaturesResources.Prefer_collection_initializer, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferSimplifiedBooleanExpressions, description: EditorFeaturesResources.Prefer_simplified_boolean_expressions, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferConditionalExpressionOverAssignment, description: EditorFeaturesResources.Prefer_conditional_expression_over_if_with_assignments, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferConditionalExpressionOverReturn, description: EditorFeaturesResources.Prefer_conditional_expression_over_if_with_returns, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferExplicitTupleNames, description: EditorFeaturesResources.Prefer_explicit_tuple_name, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferInferredTupleNames, description: EditorFeaturesResources.Prefer_inferred_tuple_names, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferInferredAnonymousTypeMemberNames, description: EditorFeaturesResources.Prefer_inferred_anonymous_type_member_names, options, visualStudioOptions, updater); - yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferCompoundAssignment, description: EditorFeaturesResources.Prefer_compound_assignments, options, visualStudioOptions, updater); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferObjectInitializer, description: EditorFeaturesResources.Prefer_object_initializer, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferCollectionInitializer, description: EditorFeaturesResources.Prefer_collection_initializer, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferSimplifiedBooleanExpressions, description: EditorFeaturesResources.Prefer_simplified_boolean_expressions, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferConditionalExpressionOverAssignment, description: EditorFeaturesResources.Prefer_conditional_expression_over_if_with_assignments, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferConditionalExpressionOverReturn, description: EditorFeaturesResources.Prefer_conditional_expression_over_if_with_returns, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferExplicitTupleNames, description: EditorFeaturesResources.Prefer_explicit_tuple_name, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferInferredTupleNames, description: EditorFeaturesResources.Prefer_inferred_tuple_names, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferInferredAnonymousTypeMemberNames, description: EditorFeaturesResources.Prefer_inferred_anonymous_type_member_names, options, visualStudioOptions, updater, FileName); + yield return CodeStyleSetting.Create(CodeStyleOptions2.PreferCompoundAssignment, description: EditorFeaturesResources.Prefer_compound_assignments, options, visualStudioOptions, updater, FileName); } - private static IEnumerable GetParenthesesCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetParenthesesCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { var enumValues = new[] { ParenthesesPreference.AlwaysForClarity, ParenthesesPreference.NeverIfUnnecessary }; var valueDescriptions = new[] { EditorFeaturesResources.Always_for_clarity, EditorFeaturesResources.Never_if_unnecessary }; @@ -151,32 +151,32 @@ private static IEnumerable GetParenthesesCodeStyleOptions(Anal enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.OtherBinaryParentheses, description: EditorFeaturesResources.In_other_binary_operators, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.RelationalBinaryParentheses, description: EditorFeaturesResources.In_relational_binary_operators, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); yield return CodeStyleSetting.Create(option: CodeStyleOptions2.OtherParentheses, description: EditorFeaturesResources.In_other_operators, enumValues: enumValues, valueDescriptions: valueDescriptions, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } - private static IEnumerable GetParameterCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetParameterCodeStyleOptions(AnalyzerConfigOptions options, OptionSet visualStudioOptions, OptionUpdater updater) { yield return CodeStyleSetting.Create( option: CodeStyleOptions2.UnusedParameters, @@ -184,7 +184,7 @@ private static IEnumerable GetParameterCodeStyleOptions(Analyz enumValues: new[] { UnusedParametersPreference.NonPublicMethods, UnusedParametersPreference.AllMethods }, new[] { EditorFeaturesResources.Non_public_methods, EditorFeaturesResources.All_methods }, editorConfigOptions: options, - visualStudioOptions: visualStudioOptions, updater: updater); + visualStudioOptions: visualStudioOptions, updater: updater, fileName: FileName); } } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsProvider.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProvider.cs similarity index 58% rename from src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsProvider.cs rename to src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProvider.cs index 4a9a86ad21ada..29707242c85dc 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsProvider.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProvider.cs @@ -10,11 +10,11 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; -namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Formatting +namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Whitespace { - internal class CommonFormattingSettingsProvider : SettingsProviderBase + internal class CommonWhitespaceSettingsProvider : SettingsProviderBase { - public CommonFormattingSettingsProvider(string fileName, OptionUpdater settingsUpdater, Workspace workspace) + public CommonWhitespaceSettingsProvider(string fileName, OptionUpdater settingsUpdater, Workspace workspace) : base(fileName, settingsUpdater, workspace) { Update(); @@ -26,13 +26,13 @@ protected override void UpdateOptions(AnalyzerConfigOptions editorConfigOptions, AddRange(defaultOptions); } - private static IEnumerable GetDefaultOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updater) + private IEnumerable GetDefaultOptions(AnalyzerConfigOptions editorConfigOptions, OptionSet visualStudioOptions, OptionUpdater updater) { - yield return FormattingSetting.Create(FormattingOptions2.UseTabs, EditorFeaturesResources.Use_Tabs, editorConfigOptions, visualStudioOptions, updater); - yield return FormattingSetting.Create(FormattingOptions2.TabSize, EditorFeaturesResources.Tab_Size, editorConfigOptions, visualStudioOptions, updater); - yield return FormattingSetting.Create(FormattingOptions2.IndentationSize, EditorFeaturesResources.Indentation_Size, editorConfigOptions, visualStudioOptions, updater); - yield return FormattingSetting.Create(FormattingOptions2.NewLine, EditorFeaturesResources.New_Line, editorConfigOptions, visualStudioOptions, updater); - yield return FormattingSetting.Create(FormattingOptions2.InsertFinalNewLine, EditorFeaturesResources.Insert_Final_Newline, editorConfigOptions, visualStudioOptions, updater); + yield return WhitespaceSetting.Create(FormattingOptions2.UseTabs, EditorFeaturesResources.Use_Tabs, editorConfigOptions, visualStudioOptions, updater, FileName); + yield return WhitespaceSetting.Create(FormattingOptions2.TabSize, EditorFeaturesResources.Tab_Size, editorConfigOptions, visualStudioOptions, updater, FileName); + yield return WhitespaceSetting.Create(FormattingOptions2.IndentationSize, EditorFeaturesResources.Indentation_Size, editorConfigOptions, visualStudioOptions, updater, FileName); + yield return WhitespaceSetting.Create(FormattingOptions2.NewLine, EditorFeaturesResources.New_Line, editorConfigOptions, visualStudioOptions, updater, FileName); + yield return WhitespaceSetting.Create(FormattingOptions2.InsertFinalNewLine, EditorFeaturesResources.Insert_Final_Newline, editorConfigOptions, visualStudioOptions, updater, FileName); } } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsProviderFactory.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProviderFactory.cs similarity index 63% rename from src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsProviderFactory.cs rename to src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProviderFactory.cs index 21d130c599f42..17fed30b03dbc 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsProviderFactory.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProviderFactory.cs @@ -5,16 +5,16 @@ using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data; using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater; -namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Formatting +namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Whitespace { - internal class CommonFormattingSettingsProviderFactory : IWorkspaceSettingsProviderFactory + internal class CommonWhitespaceSettingsProviderFactory : IWorkspaceSettingsProviderFactory { private readonly Workspace _workspace; - public CommonFormattingSettingsProviderFactory(Workspace workspace) => _workspace = workspace; + public CommonWhitespaceSettingsProviderFactory(Workspace workspace) => _workspace = workspace; - public ISettingsProvider GetForFile(string filePath) - => new CommonFormattingSettingsProvider(filePath, new OptionUpdater(_workspace, filePath), _workspace); + public ISettingsProvider GetForFile(string filePath) + => new CommonWhitespaceSettingsProvider(filePath, new OptionUpdater(_workspace, filePath), _workspace); } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsWorkspaceServiceFactory.cs b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsWorkspaceServiceFactory.cs similarity index 75% rename from src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsWorkspaceServiceFactory.cs rename to src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsWorkspaceServiceFactory.cs index 878bd1198987d..44a776392c08e 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Formatting/CommonFormattingSettingsWorkspaceServiceFactory.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsWorkspaceServiceFactory.cs @@ -8,16 +8,16 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Formatting +namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Whitespace { - [ExportWorkspaceServiceFactory(typeof(IWorkspaceSettingsProviderFactory)), Shared] - internal class CommonFormattingSettingsWorkspaceServiceFactory : IWorkspaceServiceFactory + [ExportWorkspaceServiceFactory(typeof(IWorkspaceSettingsProviderFactory)), Shared] + internal class CommonWhitespaceSettingsWorkspaceServiceFactory : IWorkspaceServiceFactory { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CommonFormattingSettingsWorkspaceServiceFactory() { } + public CommonWhitespaceSettingsWorkspaceServiceFactory() { } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new CommonFormattingSettingsProviderFactory(workspaceServices.Workspace); + => new CommonWhitespaceSettingsProviderFactory(workspaceServices.Workspace); } } diff --git a/src/EditorFeatures/Core/EditorConfigSettings/LocationKind.cs b/src/EditorFeatures/Core/EditorConfigSettings/LocationKind.cs new file mode 100644 index 0000000000000..541d0a7204cae --- /dev/null +++ b/src/EditorFeatures/Core/EditorConfigSettings/LocationKind.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings +{ + [Flags] + internal enum LocationKind + { + EditorConfig, + GlobalConfig, + VisualStudio, + } +} diff --git a/src/EditorFeatures/Core/EditorConfigSettings/SettingLocation.cs b/src/EditorFeatures/Core/EditorConfigSettings/SettingLocation.cs new file mode 100644 index 0000000000000..b39313bfe6db1 --- /dev/null +++ b/src/EditorFeatures/Core/EditorConfigSettings/SettingLocation.cs @@ -0,0 +1,8 @@ +// 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.Editor.EditorConfigSettings +{ + internal record SettingLocation(LocationKind LocationKind, string? Path) { } +} diff --git a/src/EditorFeatures/Core/Implementation/AddImports/AbstractAddImportsPasteCommandHandler.cs b/src/EditorFeatures/Core/Implementation/AddImports/AbstractAddImportsPasteCommandHandler.cs index 8cef7069e3f07..7d6b67e4bb173 100644 --- a/src/EditorFeatures/Core/Implementation/AddImports/AbstractAddImportsPasteCommandHandler.cs +++ b/src/EditorFeatures/Core/Implementation/AddImports/AbstractAddImportsPasteCommandHandler.cs @@ -43,8 +43,8 @@ public void ExecuteCommand(PasteCommandArgs args, Action nextCommandHandler, Com // Check that the feature is enabled before doing any work var optionValue = args.SubjectBuffer.GetOptionalFeatureOnOffOption(FeatureOnOffOptions.AddImportsOnPaste); - // If the feature is explicitly disabled we can exit early - if (optionValue.HasValue && !optionValue.Value) + // If the feature is not explicitly enabled we can exit early + if (optionValue != true) { nextCommandHandler(); return; @@ -71,7 +71,7 @@ public void ExecuteCommand(PasteCommandArgs args, Action nextCommandHandler, Com try { - ExecuteCommandWorker(args, executionContext, optionValue, trackingSpan); + ExecuteCommandWorker(args, executionContext, trackingSpan); } catch (OperationCanceledException) { @@ -84,7 +84,6 @@ public void ExecuteCommand(PasteCommandArgs args, Action nextCommandHandler, Com private void ExecuteCommandWorker( PasteCommandArgs args, CommandExecutionContext executionContext, - bool? optionValue, ITrackingSpan trackingSpan) { if (!args.SubjectBuffer.CanApplyChangeDocumentToWorkspace()) @@ -114,14 +113,6 @@ private void ExecuteCommandWorker( return; } - // Enable by default unless the user has explicitly disabled in the settings - var disabled = optionValue.HasValue && !optionValue.Value; - - if (disabled) - { - return; - } - using var _ = executionContext.OperationContext.AddScope(allowCancellation: true, DialogText); var cancellationToken = executionContext.OperationContext.UserCancellationToken; diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs index c462a42ed9ecc..6df0e66b533e5 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CommitManager.cs @@ -126,10 +126,11 @@ public AsyncCompletionData.CommitResult TryCommit( return new AsyncCompletionData.CommitResult(isHandled: true, AsyncCompletionData.CommitBehavior.None); } - if (!Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation)) + if (!item.Properties.TryGetProperty(CompletionSource.TriggerLocation, out SnapshotPoint triggerLocation)) { // Need the trigger snapshot to calculate the span when the commit changes to be applied. - // They should always be available from VS. Just to be defensive, if it's not found here, Roslyn should not make a commit. + // They should always be available from items provided by Roslyn CompletionSource. + // Just to be defensive, if it's not found here, Roslyn should not make a commit. return CommitResultUnhandled; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs index bf42006821e29..2381615c42cb2 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -36,6 +36,7 @@ internal sealed class CompletionSource : ForegroundThreadAffinitizedObject, IAsy { internal const string RoslynItem = nameof(RoslynItem); internal const string TriggerLocation = nameof(TriggerLocation); + internal const string ExpandedItemTriggerLocation = nameof(ExpandedItemTriggerLocation); internal const string CompletionListSpan = nameof(CompletionListSpan); internal const string InsertionText = nameof(InsertionText); internal const string HasSuggestionItemOptions = nameof(HasSuggestionItemOptions); @@ -220,7 +221,6 @@ private bool TryInvokeSnippetCompletion( if (session is null) throw new ArgumentNullException(nameof(session)); - session.Properties[TriggerLocation] = triggerLocation; return GetCompletionContextWorkerAsync(session, trigger, triggerLocation, isExpanded: false, cancellationToken); } @@ -232,12 +232,9 @@ private bool TryInvokeSnippetCompletion( CancellationToken cancellationToken) { // We only want to provide expanded items for Roslyn's expander. - if ((object)expander == FilterSet.Expander) + if ((object)expander == FilterSet.Expander && session.Properties.TryGetProperty(ExpandedItemTriggerLocation, out SnapshotPoint initialTriggerLocation)) { - if (Helpers.TryGetInitialTriggerLocation(session, out var initialTriggerLocation)) - { - return await GetCompletionContextWorkerAsync(session, intialTrigger, initialTriggerLocation, isExpanded: true, cancellationToken).ConfigureAwait(false); - } + return await GetCompletionContextWorkerAsync(session, intialTrigger, initialTriggerLocation, isExpanded: true, cancellationToken).ConfigureAwait(false); } return AsyncCompletionData.CompletionContext.Empty; @@ -298,7 +295,7 @@ private bool TryInvokeSnippetCompletion( foreach (var roslynItem in completionList.Items) { cancellationToken.ThrowIfCancellationRequested(); - var item = Convert(document, roslynItem, filterSet); + var item = Convert(document, roslynItem, filterSet, triggerLocation); itemsBuilder.Add(item); } @@ -337,6 +334,16 @@ private bool TryInvokeSnippetCompletion( } } + // We need to remember the trigger location for when a completion service claims expanded items are available + // since the initial trigger we are able to get from IAsyncCompletionSession might not be the same (e.g. in projection scenarios) + // so when they are requested via expander later, we can retrieve it. + // Technically we should save the trigger location for each individual service that made such claim, but in reality only Roslyn's + // completion service uses expander, so we can get away with not making such distinction. + if (!isExpanded && expandItemsAvailable) + { + session.Properties[ExpandedItemTriggerLocation] = triggerLocation; + } + // It's possible that some providers can provide expanded items, in which case we will need to show expander as unselected. return new AsyncCompletionData.CompletionContext( items, @@ -355,7 +362,7 @@ private bool TryInvokeSnippetCompletion( throw new ArgumentNullException(nameof(item)); if (!item.Properties.TryGetProperty(RoslynItem, out RoslynCompletionItem roslynItem) || - !Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation)) + !item.Properties.TryGetProperty(TriggerLocation, out SnapshotPoint triggerLocation)) { return null; } @@ -429,7 +436,8 @@ public VSCompletionItemData( private VSCompletionItem Convert( Document document, RoslynCompletionItem roslynItem, - FilterSet filterSet) + FilterSet filterSet, + SnapshotPoint initialTriggerLocation) { VSCompletionItemData itemData; @@ -483,6 +491,7 @@ private VSCompletionItem Convert( attributeIcons: itemData.AttributeIcons); item.Properties.AddProperty(RoslynItem, roslynItem); + item.Properties.AddProperty(TriggerLocation, initialTriggerLocation); return item; } diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/Helpers.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/Helpers.cs index ffce116fbf189..fe24c48890fbe 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/Helpers.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/Helpers.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using Microsoft.CodeAnalysis.Completion; using Microsoft.VisualStudio.Text; using EditorAsyncCompletion = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; @@ -141,9 +142,6 @@ internal static bool TextTypedSoFarMatchesItem(RoslynCompletionItem item, string internal static bool IsStandardCommitCharacter(char c) => c == '\t' || c == '\n' || c == '\0'; - internal static bool TryGetInitialTriggerLocation(EditorAsyncCompletion.IAsyncCompletionSession session, out SnapshotPoint initialTriggerLocation) - => session.Properties.TryGetProperty(CompletionSource.TriggerLocation, out initialTriggerLocation); - // This is a temporarily method to support preference of IntelliCode items comparing to non-IntelliCode items. // We expect that Editor will introduce this support and we will get rid of relying on the "★" then. internal static bool IsPreferredItem(this VSCompletionItem completionItem) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs index c8d8a09ae3939..ab4979414f617 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/AsyncCompletion/ItemManager.cs @@ -152,13 +152,13 @@ private static readonly ObjectPool>> s_listOf var filterReason = Helpers.GetFilterReason(data.Trigger); - // If the session was created/maintained out of Roslyn, e.g. in debugger; no properties are set and we should use data.Snapshot. - // However, we prefer using the original snapshot in some projection scenarios. - var snapshotForDocument = Helpers.TryGetInitialTriggerLocation(session, out var triggerLocation) - ? triggerLocation.Snapshot + // We prefer using the original snapshot, which should always be available from items provided by Roslyn's CompletionSource. + // Only use data.Snapshot in the theoretically possible but rare case when all items we are handling are from some non-Roslyn CompletionSource. + var snapshotForDocument = TryGetInitialTriggerLocation(data, out var intialTriggerLocation) + ? intialTriggerLocation.Snapshot : data.Snapshot; - var document = snapshotForDocument.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); + var document = snapshotForDocument?.TextBuffer.AsTextContainer().GetOpenDocumentInCurrentContext(); var completionService = document?.GetLanguageService(); var completionRules = completionService?.GetRules() ?? CompletionRules.Default; var completionHelper = document != null ? CompletionHelper.GetHelper(document) : _defaultCompletionHelper; @@ -267,6 +267,18 @@ private static readonly ObjectPool>> s_listOf s_listOfMatchResultPool.Free(initialListOfItemsToBeIncluded); } + static bool TryGetInitialTriggerLocation(AsyncCompletionSessionDataSnapshot data, out SnapshotPoint intialTriggerLocation) + { + var firstItem = data.InitialSortedList.FirstOrDefault(static item => item.Properties.ContainsProperty(CompletionSource.TriggerLocation)); + if (firstItem != null) + { + return firstItem.Properties.TryGetProperty(CompletionSource.TriggerLocation, out intialTriggerLocation); + } + + intialTriggerLocation = default; + return false; + } + static bool ShouldBeFilteredOutOfCompletionList(VSCompletionItem item, ImmutableArray activeNonExpanderFilters) { if (item.Filters.Any(filter => activeNonExpanderFilters.Contains(filter))) diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index 134ee1de0e3d7..6164a7bf666d7 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -16,6 +16,9 @@ .NET Compiler Platform ("Roslyn") support for editor features inside the Visual Studio editor. + + + diff --git a/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs b/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs index 96953a315b7da..3f5a35d1e7386 100644 --- a/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/CompileTimeSolutionProviderTests.cs @@ -24,8 +24,9 @@ namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests [UseExportProvider] public class CompileTimeSolutionProviderTests { - [Fact] - public async Task TryGetCompileTimeDocumentAsync() + [Theory] + [CombinatorialData] + public async Task TryGetCompileTimeDocumentAsync([CombinatorialValues(@"_a_X_razor.cs", @"a_X_razor.g.cs")] string generatedHintName) { var workspace = new TestWorkspace(composition: FeaturesTestCompositions.Features); var projectId = ProjectId.CreateNewId(); @@ -33,7 +34,6 @@ public async Task TryGetCompileTimeDocumentAsync() var projectFilePath = Path.Combine(TempRoot.Root, "a.csproj"); var additionalFilePath = Path.Combine(TempRoot.Root, "a", "X.razor"); var designTimeFilePath = Path.Combine(TempRoot.Root, "a", "X.razor.g.cs"); - var generatedHintName = @"_a_X_razor.cs"; var generator = new TestSourceGenerator() { ExecuteImpl = context => context.AddSource(generatedHintName, "") }; var sourceGeneratedPathPrefix = Path.Combine(typeof(TestSourceGenerator).Assembly.GetName().Name, typeof(TestSourceGenerator).FullName); diff --git a/src/EditorFeatures/Test/EditorConfigSettings/Data/CodeStyleSettingsTest.cs b/src/EditorFeatures/Test/EditorConfigSettings/Data/CodeStyleSettingsTest.cs index 12f2987cb8091..47d642b863ade 100644 --- a/src/EditorFeatures/Test/EditorConfigSettings/Data/CodeStyleSettingsTest.cs +++ b/src/EditorFeatures/Test/EditorConfigSettings/Data/CodeStyleSettingsTest.cs @@ -25,7 +25,7 @@ public static void CodeStyleSettingBoolFactory(bool defaultValue) var option = CreateBoolOption(defaultValue); var editorConfigOptions = new TestAnalyzerConfigOptions(); var visualStudioOptions = new TestOptionSet(option.DefaultValue); - var setting = CodeStyleSetting.Create(option, description: "TestDesciption", editorConfigOptions, visualStudioOptions, updater: null!); + var setting = CodeStyleSetting.Create(option, description: "TestDesciption", editorConfigOptions, visualStudioOptions, updater: null!, fileName: null!); Assert.Equal(string.Empty, setting.Category); Assert.Equal("TestDesciption", setting.Description); Assert.False(setting.IsDefinedInEditorConfig); @@ -47,7 +47,8 @@ public static void CodeStyleSettingEnumFactory(DayOfWeek defaultValue) valueDescriptions: Enum.GetNames(typeof(DayOfWeek)), editorConfigOptions, visualStudioOptions, - updater: null!); + updater: null!, + fileName: null!); Assert.Equal(string.Empty, setting.Category); Assert.Equal("TestDesciption", setting.Description); Assert.False(setting.IsDefinedInEditorConfig); diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.LinkedFiles.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.LinkedFiles.vb index 343010328d031..959b19fd361f3 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.LinkedFiles.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.LinkedFiles.vb @@ -227,5 +227,45 @@ namespace {|Definition:System|} Assert.Equal("System", references.ElementAt(1).Definition.ToString()) End Using End Function + + + + Public Async Function TestLinkedFiles_LocalSymbol() As Task + Dim definition = + + + + + + + + + + Using workspace = TestWorkspace.Create(definition) + Dim invocationDocument = workspace.Documents.Single(Function(d) Not d.IsLinkFile) + Dim invocationPosition = invocationDocument.CursorPosition.Value + + Dim document = workspace.CurrentSolution.GetDocument(invocationDocument.Id) + Assert.NotNull(document) + + Dim symbol = Await SymbolFinder.FindSymbolAtPositionAsync(document, invocationPosition) + + ' Should find two definitions, one in each file. + Dim references = (Await SymbolFinder.FindReferencesAsync(symbol, document.Project.Solution, progress:=Nothing, documents:=Nothing)).ToList() + Assert.Equal(2, references.Count) + + Dim documents = references.Select(Function(r) workspace.CurrentSolution.GetDocument(r.Definition.Locations.Single().SourceTree)) + Assert.Equal(2, documents.Count) + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb index 98fa65e7ca75e..6ff2de8d839dd 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests_AwaitCompletion.vb @@ -24,7 +24,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -60,7 +60,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -96,7 +96,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -130,7 +130,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -164,7 +164,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -198,7 +198,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -232,7 +232,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -266,7 +266,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -301,7 +301,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -335,7 +335,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" @@ -368,7 +368,7 @@ public class C ]]> ) state.SendTypeChars("aw") - Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=CSharpFeaturesResources.Make_container_async) + Await state.AssertSelectedCompletionItem(displayText:="await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) state.SendTab() Assert.Equal(" diff --git a/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb new file mode 100644 index 0000000000000..bedc2c7c497e9 --- /dev/null +++ b/src/EditorFeatures/Test2/IntelliSense/VisualBasicCompletionCommandHandlerTests_AwaitCompletion.vb @@ -0,0 +1,367 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.VisualBasic + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense + + + Public Class VisualBasicCompletionCommandHandlerTests_Await + + Public Async Function AwaitCompletionAddsAsync_FunctionDeclaration() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) + + state.SendTab() + Assert.Equal(" +Imports System.Threading.Tasks + +Public Class C + Public Shared Async Function Main() As Task + Await + End Function +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_SubDeclaration() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) + + state.SendTab() + Assert.Equal(" +Public Class C + Public Shared Async Sub Main() + Await + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_MultiLineFunctionLambdaExpression() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) + + state.SendTab() + Assert.Equal(" +Imports System + +Public Class C + Public Shared Sub Main() + Dim x As Func(Of Boolean) = Async Function() + Await + End Function + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_MultiLineSubLambdaExpression() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) + + state.SendTab() + Assert.Equal(" +Imports System + +Public Class C + Public Shared Sub Main() + Dim x As Action = Async Sub() + Await + End Sub + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_SingleLineFunctionLambdaExpression() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) + + state.SendTab() + Assert.Equal(" +Imports System.Threading.Tasks + +Public Class C + Public Shared Sub Main() + Dim x As Func(Of Boolean) = Async Function() Await + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_SingleLineSubLambdaExpression() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:=FeaturesResources.Make_containing_scope_async) + + state.SendTab() + Assert.Equal(" +Imports System.Threading.Tasks + +Public Class C + Public Shared Sub Main() + Dim x As Action = Async Sub() Await + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_FunctionDeclaration_AlreadyAsync() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:="") + + state.SendTab() + Assert.Equal(" +Imports System.Threading.Tasks + +Public Class C + Public Shared Async Function Main() As Task + Await + End Function +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_SubDeclaration_AlreadyAsync() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:="") + + state.SendTab() + Assert.Equal(" +Public Class C + Public Shared Async Sub Main() + Await + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_MultiLineFunctionLambdaExpression_AlreadyAsync() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:="") + + state.SendTab() + Assert.Equal(" +Imports System + +Public Class C + Public Shared Sub Main() + Dim x As Func(Of Boolean) = Async Function() + Await + End Function + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_MultiLineSubLambdaExpression_AlreadyAsync() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:="") + + state.SendTab() + Assert.Equal(" +Imports System + +Public Class C + Public Shared Sub Main() + Dim x As Action = Async Sub() + Await + End Sub + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_SingleLineFunctionLambdaExpression_AlreadyAsync() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:="") + + state.SendTab() + Assert.Equal(" +Imports System.Threading.Tasks + +Public Class C + Public Shared Sub Main() + Dim x As Func(Of Boolean) = Async Function() Await + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + + + Public Async Function AwaitCompletionAddsAsync_SingleLineSubLambdaExpression_AlreadyAsync() As Task + Using state = TestStateFactory.CreateVisualBasicTestState( + + ) + state.SendTypeChars("aw") + Await state.AssertSelectedCompletionItem(displayText:="Await", isHardSelected:=True, inlineDescription:="") + + state.SendTab() + Assert.Equal(" +Imports System.Threading.Tasks + +Public Class C + Public Shared Sub Main() + Dim x As Action = Async Sub() Await + End Sub +End Class +", state.GetDocumentText()) + End Using + End Function + End Class +End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviderOrderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviderOrderTests.vb index 6aebf6204b8e6..86f6e4b3cf05b 100644 --- a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviderOrderTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviderOrderTests.vb @@ -25,6 +25,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion { GetType(FirstBuiltInCompletionProvider), GetType(KeywordCompletionProvider), + GetType(AwaitCompletionProvider), GetType(SymbolCompletionProvider), GetType(PreprocessorCompletionProvider), GetType(ObjectInitializerCompletionProvider), diff --git a/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AwaitCompletionProviderTests.vb b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AwaitCompletionProviderTests.vb new file mode 100644 index 0000000000000..bcc90954bac5e --- /dev/null +++ b/src/EditorFeatures/VisualBasicTest/Completion/CompletionProviders/AwaitCompletionProviderTests.vb @@ -0,0 +1,120 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.VisualBasic.Completion.Providers + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion.CompletionProviders + Public Class AwaitCompletionProviderTests + Inherits AbstractVisualBasicCompletionProviderTests + + Friend Overrides Function GetCompletionProviderType() As Type + Return GetType(AwaitCompletionProvider) + End Function + + + Public Sub InSynchronousMethodTest() + VerifyItemExistsAsync(" +Class C + Sub Goo() + Dim z = $$ + End Sub +End Class +", "Await") + End Sub + + + Public Sub InMethodStatementTest() + VerifyItemExistsAsync(" +Class C + Async Sub Goo() + $$ + End Sub +End Class +", "Await") + End Sub + + + Public Sub InMethodExpressionTest() + VerifyItemExistsAsync(" +Class C + Async Sub Goo() + Dim z = $$ + End Sub +End Class +", "Await") + End Sub + + + Public Sub NotInCatchTest() + VerifyItemExistsAsync(" +Class C + Async Sub Goo() + Try + Catch + Dim z = $$ + End Try + + End Sub +End Class +", "Await") + End Sub + + + Public Sub NotInCatchExceptionFilterTest() + VerifyNoItemsExistAsync(" +Class C + Async Sub Goo() + Try + Catch When Err = $$ + End Try + + End Sub +End Class +") + End Sub + + + Public Sub InCatchNestedDelegateTest() + VerifyItemExistsAsync(" +Class C + Async Sub Goo() + Try + Catch + Dim z = Function() $$ + End Try + + End Sub +End Class +", "Await") + End Sub + + + Public Sub NotInFinallyTest() + VerifyItemExistsAsync(" +Class C + Async Sub Goo() + Try + Finally + Dim z = $$ + End Try + + End Sub +End Class +", "Await") + End Sub + + + Public Sub NotInSyncLockTest() + VerifyItemExistsAsync(" +Class C + Async Sub Goo() + SyncLock True + Dim z = $$ + End SyncLock + End Sub +End Class +", "Await") + End Sub + End Class +End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Recommendations/Expressions/AwaitKeywordRecommenderTests.vb b/src/EditorFeatures/VisualBasicTest/Recommendations/Expressions/AwaitKeywordRecommenderTests.vb deleted file mode 100644 index f0582a7aeb03d..0000000000000 --- a/src/EditorFeatures/VisualBasicTest/Recommendations/Expressions/AwaitKeywordRecommenderTests.vb +++ /dev/null @@ -1,113 +0,0 @@ -' Licensed to the .NET Foundation under one or more agreements. -' The .NET Foundation licenses this file to you under the MIT license. -' See the LICENSE file in the project root for more information. - -Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Recommendations.Expressions - Public Class AwaitKeywordRecommenderTests - - Public Sub InSynchronousMethodTest() - VerifyRecommendationsContain( -Class C - Sub Goo() - Dim z = | - End Sub -End Class - , "Await") - End Sub - - - Public Sub InMethodStatementTest() - VerifyRecommendationsContain( -Class C - Async Sub Goo() - | - End Sub -End Class - , "Await") - End Sub - - - Public Sub InMethodExpressionTest() - VerifyRecommendationsContain( -Class C - Async Sub Goo() - Dim z = | - End Sub -End Class - , "Await") - End Sub - - - Public Sub NotInCatchTest() - VerifyRecommendationsMissing( -Class C - Async Sub Goo() - Try - Catch - Dim z = | - End Try - - End Sub -End Class - , "Await") - End Sub - - - Public Sub NotInCatchExceptionFilterTest() - VerifyRecommendationsMissing( -Class C - Async Sub Goo() - Try - Catch When Err = | - End Try - - End Sub -End Class - , "Await") - End Sub - - - Public Sub InCatchNestedDelegateTest() - VerifyRecommendationsContain( -Class C - Async Sub Goo() - Try - Catch - Dim z = Function() | - End Try - - End Sub -End Class - , "Await") - End Sub - - - Public Sub NotInFinallyTest() - VerifyRecommendationsMissing( -Class C - Async Sub Goo() - Try - Finally - Dim z = | - End Try - - End Sub -End Class - , "Await") - End Sub - - - Public Sub NotInSyncLockTest() - VerifyRecommendationsMissing( -Class C - Async Sub Goo() - SyncLock True - Dim z = | - End SyncLock - End Sub -End Class - , "Await") - End Sub - End Class -End Namespace - diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 38cffbfe26c88..2db265f432efc 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -200,15 +200,6 @@ Invalid selection. - - Selection does not contain a valid token. - - - No valid selection to perform extraction. - - - No common root node for extraction. - Contains invalid selection. @@ -495,6 +486,9 @@ Convert to 'switch' expression + + Use recursive patterns + <Name> @@ -618,10 +612,6 @@ Change to cast - - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - record diff --git a/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..19ef4ebe9d3eb --- /dev/null +++ b/src/Features/CSharp/Portable/CodeRefactorings/UseRecursivePatterns/UseRecursivePatternsCodeRefactoringProvider.cs @@ -0,0 +1,503 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseRecursivePatterns +{ + using static SyntaxFactory; + using static SyntaxKind; + + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.UseRecursivePatterns), Shared] + internal sealed class UseRecursivePatternsCodeRefactoringProvider : CodeRefactoringProvider + { + private static readonly PatternSyntax s_trueConstantPattern = ConstantPattern(LiteralExpression(TrueLiteralExpression)); + private static readonly PatternSyntax s_falseConstantPattern = ConstantPattern(LiteralExpression(FalseLiteralExpression)); + + private static readonly Func s_canConvertToSubpattern = + (name, model) => model.GetSymbolInfo(name).Symbol is + { + IsStatic: false, + Kind: SymbolKind.Property or SymbolKind.Field, + ContainingType: not { SpecialType: SpecialType.System_Nullable_T } + }; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public UseRecursivePatternsCodeRefactoringProvider() + { + } + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var (document, textSpan, cancellationToken) = context; + if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) + return; + + if (textSpan.Length > 0) + return; + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + if (((CSharpParseOptions)root.SyntaxTree.Options).LanguageVersion < LanguageVersion.CSharp9) + return; + + var model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var node = root.FindToken(textSpan.Start).Parent; + var replacementFunc = GetReplacementFunc(node, model); + if (replacementFunc is null) + return; + + context.RegisterRefactoring( + new MyCodeAction( + CSharpFeaturesResources.Use_recursive_patterns, + _ => Task.FromResult(document.WithSyntaxRoot(replacementFunc(root))), + nameof(CSharpFeaturesResources.Use_recursive_patterns))); + } + + private static Func? GetReplacementFunc(SyntaxNode? node, SemanticModel model) + => node switch + { + BinaryExpressionSyntax(LogicalAndExpression) logicalAnd => CombineLogicalAndOperands(logicalAnd, model), + CasePatternSwitchLabelSyntax { WhenClause: { } whenClause } switchLabel => CombineWhenClauseCondition(switchLabel.Pattern, whenClause.Condition, model), + SwitchExpressionArmSyntax { WhenClause: { } whenClause } switchArm => CombineWhenClauseCondition(switchArm.Pattern, whenClause.Condition, model), + WhenClauseSyntax { Parent: CasePatternSwitchLabelSyntax switchLabel } whenClause => CombineWhenClauseCondition(switchLabel.Pattern, whenClause.Condition, model), + WhenClauseSyntax { Parent: SwitchExpressionArmSyntax switchArm } whenClause => CombineWhenClauseCondition(switchArm.Pattern, whenClause.Condition, model), + _ => null + }; + + private static Func? CombineLogicalAndOperands(BinaryExpressionSyntax logicalAnd, SemanticModel model) + { + if (TryDetermineReceiver(logicalAnd.Left, model) is not var (leftReceiver, leftTarget, leftFlipped) || + TryDetermineReceiver(logicalAnd.Right, model) is not var (rightReceiver, rightTarget, rightFlipped)) + { + return null; + } + + // If we have an is-expression on the left, first we check if there is a variable designation that's been used on the right-hand-side, + // in which case, we'll convert and move the check inside the existing pattern, if possible. + // For instance, `e is C c && c.p == 0` is converted to `e is C { p: 0 } c` + if (leftTarget.Parent is IsPatternExpressionSyntax isPatternExpression && + TryFindVariableDesignation(isPatternExpression.Pattern, rightReceiver, model) is var (containingPattern, rightNamesOpt)) + { + Debug.Assert(leftTarget == isPatternExpression.Pattern); + Debug.Assert(leftReceiver == isPatternExpression.Expression); + return root => + { + var rightPattern = CreatePattern(rightReceiver, rightTarget, rightFlipped); + var rewrittenPattern = RewriteContainingPattern(containingPattern, rightPattern, rightNamesOpt); + var replacement = isPatternExpression.ReplaceNode(containingPattern, rewrittenPattern); + return root.ReplaceNode(logicalAnd, AdjustBinaryExpressionOperands(logicalAnd, replacement)); + }; + } + + if (TryGetCommonReceiver(leftReceiver, rightReceiver, model) is var (commonReceiver, leftNames, rightNames)) + { + return root => + { + var leftSubpattern = CreateSubpattern(leftNames, CreatePattern(leftReceiver, leftTarget, leftFlipped)); + var rightSubpattern = CreateSubpattern(rightNames, CreatePattern(rightReceiver, rightTarget, rightFlipped)); + // If the common receiver is null, it's an implicit `this` reference in source. + // For instance, `prop == 1 && field == 2` would be converted to `this is { prop: 1, field: 2 }` + var replacement = IsPatternExpression(commonReceiver, RecursivePattern(leftSubpattern, rightSubpattern)); + return root.ReplaceNode(logicalAnd, AdjustBinaryExpressionOperands(logicalAnd, replacement)); + }; + } + + return null; + + static SyntaxNode AdjustBinaryExpressionOperands(BinaryExpressionSyntax logicalAnd, ExpressionSyntax replacement) + { + // If there's a `&&` on the left, we have picked the right-hand-side for the combination. + // In which case, we should replace that instead of the whole `&&` operator in a chain. + // For instance, `expr && a.b == 1 && a.c == 2` is converted to `expr && a is { b: 1, c: 2 }` + if (logicalAnd.Left is BinaryExpressionSyntax(LogicalAndExpression) leftExpression) + replacement = leftExpression.WithRight(replacement); + return replacement.ConvertToSingleLine().WithAdditionalAnnotations(Formatter.Annotation); + } + } + + private static Func? CombineWhenClauseCondition(PatternSyntax switchPattern, ExpressionSyntax condition, SemanticModel model) + { + if (TryDetermineReceiver(condition, model, inWhenClause: true) is not var (receiver, target, flipped) || + TryFindVariableDesignation(switchPattern, receiver, model) is not var (containingPattern, namesOpt)) + { + return null; + } + + return root => + { + var editor = new SyntaxEditor(root, CSharpSyntaxGenerator.Instance); + switch (receiver.GetRequiredParent().Parent) + { + // This is the leftmost `&&` operand in a when-clause. Remove the left-hand-side which we've just morphed in the switch pattern. + // For instance, `case { p: var v } when v.q == 1 && expr:` would be converted to `case { p: { q: 1 } } v when expr:` + case BinaryExpressionSyntax(LogicalAndExpression) logicalAnd: + editor.ReplaceNode(logicalAnd, logicalAnd.Right); + break; + // If we reach here, there's no other expression left in the when-clause. Remove. + // For instance, `case { p: var v } when v.q == 1:` would be converted to `case { p: { q: 1 } v }:` + case WhenClauseSyntax whenClause: + editor.RemoveNode(whenClause, SyntaxRemoveOptions.AddElasticMarker); + break; + case var v: + throw ExceptionUtilities.UnexpectedValue(v); + } + + var generatedPattern = CreatePattern(receiver, target, flipped); + var rewrittenPattern = RewriteContainingPattern(containingPattern, generatedPattern, namesOpt); + editor.ReplaceNode(containingPattern, rewrittenPattern); + return editor.GetChangedRoot(); + }; + } + + private static PatternSyntax RewriteContainingPattern( + PatternSyntax containingPattern, + PatternSyntax generatedPattern, + ImmutableArray namesOpt) + { + // This is a variable designation match. We'll try to combine the generated + // pattern from the right-hand-side into the containing pattern of this designation. + var rewrittenPattern = namesOpt.IsDefault + // If there's no name, we will combine the pattern itself. + ? Combine(containingPattern, generatedPattern) + // Otherwise, we generate a subpattern per each name and rewrite as a recursive pattern. + : AddSubpattern(containingPattern, CreateSubpattern(namesOpt, generatedPattern)); + + return rewrittenPattern.ConvertToSingleLine().WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation); + + static PatternSyntax Combine(PatternSyntax containingPattern, PatternSyntax generatedPattern) + { + // We know we have a var-pattern, declaration-pattern or a recursive-pattern on the left as the containing node of the variable designation. + // Depending on the generated pattern off of the expression on the right, we can give a better result by morphing it into the existing match. + return (containingPattern, generatedPattern) switch + { + // e.g. `e is var x && x is { p: 1 }` => `e is { p: 1 } x` + (VarPatternSyntax var, RecursivePatternSyntax { Designation: null } recursive) + => recursive.WithDesignation(var.Designation), + + // e.g. `e is C x && x is { p: 1 }` => `is C { p: 1 } x` + (DeclarationPatternSyntax decl, RecursivePatternSyntax { Type: null, Designation: null } recursive) + => recursive.WithType(decl.Type).WithDesignation(decl.Designation), + + // e.g. `e is { p: 1 } x && x is C` => `is C { p: 1 } x` + (RecursivePatternSyntax { Type: null } recursive, TypePatternSyntax type) + => recursive.WithType(type.Type), + + // e.g. `e is { p: 1 } x && x is { q: 2 }` => `e is { p: 1, q: 2 } x` + (RecursivePatternSyntax recursive, RecursivePatternSyntax { Type: null, Designation: null } other) + when recursive.PositionalPatternClause is null || other.PositionalPatternClause is null + => recursive + .WithPositionalPatternClause(recursive.PositionalPatternClause ?? other.PositionalPatternClause) + .WithPropertyPatternClause(Concat(recursive.PropertyPatternClause, other.PropertyPatternClause)), + + // In any other case, we fallback to an `and` pattern. + // UNDONE: This may result in a few unused variables which should be removed in a later pass. + _ => BinaryPattern(AndPattern, containingPattern.Parenthesize(), generatedPattern.Parenthesize()), + }; + } + + static PatternSyntax AddSubpattern(PatternSyntax containingPattern, SubpatternSyntax subpattern) + { + return containingPattern switch + { + // e.g. `case var x when x.p is 1` => `case { p: 1 } x` + VarPatternSyntax p => RecursivePattern(type: null, subpattern, p.Designation), + + // e.g. `case Type x when x.p is 1` => `case Type { p: 1 } x` + DeclarationPatternSyntax p => RecursivePattern(p.Type, subpattern, p.Designation), + + // e.g. `case { p: 1 } x when x.q is 2` => `case { p: 1, q: 2 } x` + RecursivePatternSyntax p => p.AddPropertyPatternClauseSubpatterns(subpattern), + + // We've already checked that the designation is contained in any of the above pattern forms. + var p => throw ExceptionUtilities.UnexpectedValue(p) + }; + } + + static PropertyPatternClauseSyntax? Concat(PropertyPatternClauseSyntax? left, PropertyPatternClauseSyntax? right) + { + if (left is null || right is null) + return left ?? right; + var leftSubpatterns = left.Subpatterns.GetWithSeparators(); + if (leftSubpatterns.Any() && !leftSubpatterns.Last().IsToken) + leftSubpatterns = leftSubpatterns.Add(Token(CommaToken)); + var rightSubpatterns = right.Subpatterns.GetWithSeparators(); + var list = new SyntaxNodeOrTokenList(leftSubpatterns.Concat(rightSubpatterns)); + return left.WithSubpatterns(SeparatedList(list)); + } + } + + private static PatternSyntax CreatePattern(ExpressionSyntax originalReceiver, ExpressionOrPatternSyntax target, bool flipped) + { + return target switch + { + // A pattern come from an `is` expression on either side of `&&` + PatternSyntax pattern => pattern, + TypeSyntax type when originalReceiver.IsParentKind(IsExpression) => TypePattern(type), + // Otherwise, this is a constant. Depending on the original receiver, we create an appropriate pattern. + ExpressionSyntax constant => originalReceiver.Parent switch + { + BinaryExpressionSyntax(EqualsExpression) => ConstantPattern(constant), + BinaryExpressionSyntax(NotEqualsExpression) => UnaryPattern(ConstantPattern(constant)), + BinaryExpressionSyntax(GreaterThanExpression or + GreaterThanOrEqualExpression or + LessThanOrEqualExpression or + LessThanExpression) e + => RelationalPattern(flipped ? Flip(e.OperatorToken) : e.OperatorToken, constant), + var v => throw ExceptionUtilities.UnexpectedValue(v), + }, + var v => throw ExceptionUtilities.UnexpectedValue(v), + }; + + static SyntaxToken Flip(SyntaxToken token) + { + return Token(token.Kind() switch + { + LessThanToken => GreaterThanToken, + LessThanEqualsToken => GreaterThanEqualsToken, + GreaterThanEqualsToken => LessThanEqualsToken, + GreaterThanToken => LessThanToken, + var v => throw ExceptionUtilities.UnexpectedValue(v) + }); + } + } + + private static (PatternSyntax ContainingPattern, ImmutableArray NamesOpt)? TryFindVariableDesignation( + PatternSyntax leftPattern, + ExpressionSyntax rightReceiver, + SemanticModel model) + { + using var _ = ArrayBuilder.GetInstance(out var names); + if (GetInnermostReceiver(rightReceiver, names, model) is not IdentifierNameSyntax identifierName) + return null; + + var designation = leftPattern.DescendantNodes() + .OfType() + .Where(d => d.Identifier.ValueText == identifierName.Identifier.ValueText) + .FirstOrDefault(); + + if (designation is not { Parent: PatternSyntax containingPattern }) + return null; + + // Only the following patterns can directly contain a variable designation. + // Note: While a parenthesized designation can also contain other variables, + // it is not a pattern, so it would not get past the PatternSyntax test above. + Debug.Assert(containingPattern.IsKind(SyntaxKind.VarPattern, SyntaxKind.DeclarationPattern, SyntaxKind.RecursivePattern)); + return (containingPattern, names.ToImmutableOrNull()); + } + + private static (ExpressionSyntax Receiver, ExpressionOrPatternSyntax Target, bool Flipped)? TryDetermineReceiver( + ExpressionSyntax node, + SemanticModel model, + bool inWhenClause = false) + { + return node switch + { + // For comparison operators, after we have determined the + // constant operand, we rewrite it as a constant or relational pattern. + BinaryExpressionSyntax(EqualsExpression or + NotEqualsExpression or + GreaterThanExpression or + GreaterThanOrEqualExpression or + LessThanOrEqualExpression or + LessThanExpression) expr + => TryDetermineConstant(expr, model), + + // If we found a `&&` here, there's two possibilities: + // + // 1) If we're in a when-clause, we look for the leftmost expression + // which we will try to combine with the switch arm/label pattern. + // For instance, we return `a` if we have `case when a && b && c`. + // + // 2) Otherwise, we will return the operand that *appears* to be on the left in the source. + // For instance, we return `a` if we have `x && a && b` with the cursor on the second operator. + // Since `&&` is left-associative, it's guaranteed to be the expression that we want. + // For simplicity, we won't descend into any parenthesized expression here. + // + BinaryExpressionSyntax(LogicalAndExpression) expr => TryDetermineReceiver(inWhenClause ? expr.Left : expr.Right, model, inWhenClause), + + // If we have an `is` operator, we'll try to combine the existing pattern/type with the other operand. + BinaryExpressionSyntax(IsExpression) { Right: NullableTypeSyntax type } expr => (expr.Left, type.ElementType, Flipped: false), + BinaryExpressionSyntax(IsExpression) { Right: TypeSyntax type } expr => (expr.Left, type, Flipped: false), + IsPatternExpressionSyntax expr => (expr.Expression, expr.Pattern, Flipped: false), + + // We treat any other expression as if they were compared to true/false. + // For instance, `a.b && !a.c` will be converted to `a is { b: true, c: false }` + PrefixUnaryExpressionSyntax(LogicalNotExpression) expr => (expr.Operand, s_falseConstantPattern, Flipped: false), + var expr => (expr, s_trueConstantPattern, Flipped: false), + }; + + static (ExpressionSyntax Expression, ExpressionSyntax Constant, bool Flipped)? TryDetermineConstant(BinaryExpressionSyntax node, SemanticModel model) + { + return (node.Left, node.Right) switch + { + var (left, right) when model.GetConstantValue(left).HasValue => (right, left, Flipped: true), + var (left, right) when model.GetConstantValue(right).HasValue => (left, right, Flipped: false), + _ => null + }; + } + } + + private static SubpatternSyntax CreateSubpattern(ImmutableArray names, PatternSyntax pattern) + { + Debug.Assert(!names.IsDefaultOrEmpty); + var subpattern = Subpattern(names[0], pattern); + for (var i = 1; i < names.Length; i++) + subpattern = Subpattern(names[i], RecursivePattern(subpattern)); + return subpattern; + } + + private static SubpatternSyntax Subpattern(IdentifierNameSyntax name, PatternSyntax pattern) + => SyntaxFactory.Subpattern(NameColon(name), pattern); + + private static RecursivePatternSyntax RecursivePattern(params SubpatternSyntax[] subpatterns) + => SyntaxFactory.RecursivePattern(type: null, positionalPatternClause: null, PropertyPatternClause(SeparatedList(subpatterns)), designation: null); + + private static RecursivePatternSyntax RecursivePattern(TypeSyntax? type, SubpatternSyntax subpattern, VariableDesignationSyntax? designation) + => SyntaxFactory.RecursivePattern(type, positionalPatternClause: null, PropertyPatternClause(SingletonSeparatedList(subpattern)), designation); + + private static RecursivePatternSyntax RecursivePattern(SubpatternSyntax subpattern) + => RecursivePattern(type: null, subpattern, designation: null); + + /// + /// Obtain the outermost common receiver between two expressions. This can succeed with a null 'CommonReceiver' + /// in the case that the common receiver is the 'implicit this'. + /// + private static (ExpressionSyntax CommonReceiver, ImmutableArray LeftNames, ImmutableArray RightNames)? TryGetCommonReceiver( + ExpressionSyntax left, + ExpressionSyntax right, + SemanticModel model) + { + using var _1 = ArrayBuilder.GetInstance(out var leftNames); + using var _2 = ArrayBuilder.GetInstance(out var rightNames); + + if (!TryGetInnermostReceiver(left, leftNames, out var leftReceiver, model) || + !TryGetInnermostReceiver(right, rightNames, out var rightReceiver, model) || + !AreEquivalent(leftReceiver, rightReceiver)) // We must have a common starting point to proceed. + { + return null; + } + + var commonReceiver = leftReceiver; + + // To reduce noise on superfluous subpatterns and avoid duplicates, skip any common name in the path. + var lastName = SkipCommonNames(leftNames, rightNames); + if (lastName is not null) + { + // If there were some common names in the path, we rewrite the receiver to include those. + // For instance, in `a.b.c && a.b.d`, we have `b` as the last common name in the path, + // So we want `a.b` as the receiver so that we convert it to `a.b is { c: true, d: true }`. + commonReceiver = GetInnermostReceiver(left, lastName, static (identifierName, lastName) => identifierName != lastName); + } + + return (commonReceiver ?? ThisExpression(), leftNames.ToImmutable(), rightNames.ToImmutable()); + + static bool TryGetInnermostReceiver(ExpressionSyntax node, ArrayBuilder builder, [NotNullWhen(true)] out ExpressionSyntax? receiver, SemanticModel model) + { + receiver = GetInnermostReceiver(node, builder, model); + return builder.Any(); + } + + static IdentifierNameSyntax? SkipCommonNames(ArrayBuilder leftNames, ArrayBuilder rightNames) + { + IdentifierNameSyntax? lastName = null; + int leftIndex, rightIndex; + // Note: we don't want to skip the first name to still be able to convert to a subpattern, hence checking `> 0` below. + for (leftIndex = leftNames.Count - 1, rightIndex = rightNames.Count - 1; leftIndex > 0 && rightIndex > 0; leftIndex--, rightIndex--) + { + var leftName = leftNames[leftIndex]; + var rightName = rightNames[rightIndex]; + if (!AreEquivalent(leftName, rightName)) + break; + lastName = leftName; + } + + leftNames.Clip(leftIndex + 1); + rightNames.Clip(rightIndex + 1); + return lastName; + } + } + + private static ExpressionSyntax? GetInnermostReceiver(ExpressionSyntax node, ArrayBuilder builder, SemanticModel model) + => GetInnermostReceiver(node, model, s_canConvertToSubpattern, builder); + + private static ExpressionSyntax? GetInnermostReceiver( + ExpressionSyntax node, TArg arg, + Func canConvertToSubpattern, + ArrayBuilder? builder = null) + { + return GetInnermostReceiver(node); + + ExpressionSyntax? GetInnermostReceiver(ExpressionSyntax node) + { + switch (node) + { + + case IdentifierNameSyntax name + when canConvertToSubpattern(name, arg): + builder?.Add(name); + // This is a member reference with an implicit `this` receiver. + // We know this is true because we already checked canConvertToSubpattern. + // Any other name outside the receiver position is captured in the cases below. + return null; + + case MemberBindingExpressionSyntax { Name: IdentifierNameSyntax name } + when canConvertToSubpattern(name, arg): + builder?.Add(name); + // We only reach here from a parent conditional-access. + // Returning null here means that all the names on the right were convertible to a property pattern. + return null; + + case MemberAccessExpressionSyntax(SimpleMemberAccessExpression) { Name: IdentifierNameSyntax name } memberAccess + when canConvertToSubpattern(name, arg) && !memberAccess.Expression.IsKind(SyntaxKind.BaseExpression): + builder?.Add(name); + // For a simple member access we simply record the name and descend into the expression on the left-hand-side. + return GetInnermostReceiver(memberAccess.Expression); + + case ConditionalAccessExpressionSyntax conditionalAccess: + // For a conditional access, first we need to verify the right-hand-side is convertible to a property pattern. + var right = GetInnermostReceiver(conditionalAccess.WhenNotNull); + if (right is not null) + { + // If it has it's own receiver other than a member-binding expression, we return this node as the receiver. + // For instance, if we had `a?.M().b`, the name `b` is already captured, so we need to return `a?.M()` as the innermost receiver. + // If there was no name, this call returns itself, e.g. in `a?.M()` the receiver is the entire existing conditional access. + return conditionalAccess.WithWhenNotNull(right); + } + // Otherwise, descend into the the expression on the left-hand-side. + return GetInnermostReceiver(conditionalAccess.Expression); + + default: + return node; + } + } + } + + private sealed class MyCodeAction : CodeActions.CodeAction.DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument, string equivalenceKey) + : base(title, createChangedDocument, equivalenceKey) + { + } + } + } +} diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs index 8d09d671a80a0..21b4fb8e9c30d 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/AwaitCompletionProvider.cs @@ -5,33 +5,22 @@ using System; using System.Collections.Immutable; using System.Composition; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Completion.Providers; +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers { - /// - /// A completion provider for offering keyword. - /// This is implemented separately, not as a keyword recommender as it contains extra logic for making container method async. - /// - /// - /// The container is made async if and only if the containing method is returning a Task-like type. - /// [ExportCompletionProvider(nameof(AwaitCompletionProvider), LanguageNames.CSharp)] [ExtensionOrder(After = nameof(KeywordCompletionProvider))] [Shared] - internal sealed class AwaitCompletionProvider : LSPCompletionProvider + internal sealed class AwaitCompletionProvider : AbstractAwaitCompletionProvider { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -41,62 +30,10 @@ public AwaitCompletionProvider() public override ImmutableHashSet TriggerCharacters => CompletionUtilities.CommonTriggerCharactersWithArgumentList; - public override async Task ProvideCompletionsAsync(CompletionContext context) - { - var document = context.Document; - var position = context.Position; - var cancellationToken = context.CancellationToken; - var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); - var workspace = document.Project.Solution.Workspace; - var syntaxContext = CSharpSyntaxContext.CreateContext(workspace, semanticModel, position, cancellationToken); - if (!syntaxContext.IsAwaitKeywordContext(position)) - { - return; - } - - var method = syntaxContext.TargetToken.GetAncestor(node => node.IsAsyncSupportingFunctionSyntax()); - var shouldMakeContainerAsync = method is not null && !method.GetModifiers().Any(SyntaxKind.AsyncKeyword); - var completionItem = CommonCompletionItem.Create( - displayText: SyntaxFacts.GetText(SyntaxKind.AwaitKeyword), - displayTextSuffix: "", - rules: CompletionItemRules.Default, - Glyph.Keyword, - description: RecommendedKeyword.CreateDisplayParts(SyntaxFacts.GetText(SyntaxKind.AwaitKeyword), string.Empty), - inlineDescription: shouldMakeContainerAsync ? CSharpFeaturesResources.Make_container_async : null, - isComplexTextEdit: shouldMakeContainerAsync); - context.AddItem(completionItem); - } - - public override async Task GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default) - { - // IsComplexTextEdit is true when we want to add async to the container. - if (!item.IsComplexTextEdit) - { - return await base.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false); - } - - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var declaration = root.FindToken(item.Span.Start).GetAncestor(node => node.IsAsyncSupportingFunctionSyntax()); - if (declaration is null) - { - // We already check that in ProvideCompletionsAsync above. - Debug.Assert(false, "Expected non-null value for declaration."); - return await base.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false); - } - - using var _ = ArrayBuilder.GetInstance(out var builder); - builder.Add(new TextChange(new TextSpan(GetSpanStart(declaration), 0), "async ")); - builder.Add(new TextChange(item.Span, item.DisplayText)); - - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var newText = text.WithChanges(builder); - return CompletionChange.Create(CodeAnalysis.Completion.Utilities.Collapse(newText, builder.ToImmutableArray())); - } - /// /// Gets the span start where async keyword should go. /// - private static int GetSpanStart(SyntaxNode declaration) + private protected override int GetSpanStart(SyntaxNode declaration) { return declaration switch { @@ -111,5 +48,8 @@ private static int GetSpanStart(SyntaxNode declaration) _ => throw ExceptionUtilities.UnexpectedValue(declaration.Kind()) }; } + + private protected override SyntaxNode? GetAsyncSupportingDeclaration(SyntaxToken token) + => token.GetAncestor(node => node.IsAsyncSupportingFunctionSyntax()); } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs index 2f62a02807e29..0381a340937dc 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs @@ -49,8 +49,7 @@ static CompletionItemRules MakeRule((bool importDirective, bool preselect, bool { // '<' should not filter the completion list, even though it's in generic items like IList<> var generalBaseline = CompletionItemRules.Default. - WithFilterCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '<')). - WithCommitCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Add, '<')); + WithFilterCharacterRule(CharacterSetModificationRule.Create(CharacterSetModificationKind.Remove, '<')); var importDirectiveBaseline = CompletionItemRules.Create(commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '.', ';'))); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs index cac34a8dd9131..81b7436a6c8df 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs @@ -36,6 +36,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs index 1d7dff124b77e..3a37ee8a9cc34 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs @@ -38,6 +38,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs index e7268d7c204d9..315d31b52f2e8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs @@ -36,6 +36,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs index 849e39d2008cf..9eea27ae2bc4d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs @@ -36,6 +36,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs index 37edc6c3d06df..bf5a6113f5c1f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs @@ -36,6 +36,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs index 133bc3481f02f..c479f3cbde795 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs @@ -36,6 +36,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs index 96d976a59ac84..5aad1f58a9b81 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs index 3c8c813427543..30f7127acf88d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs index 7679d177dff82..ac949e83c7b90 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs @@ -35,6 +35,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsLocalVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsTypeOfExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs index 56494ef0bda61..ec0c59104ecac 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs index ac572e464faff..6126c0e2cca2c 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs index c1224ccde8710..c8796ceaea285 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs @@ -35,6 +35,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsLocalVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsTypeOfExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs index d116622874dbb..74d17fdd359ee 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs index 31aa183da124f..4ad9c223b7e06 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs @@ -37,6 +37,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs index 5e57268138c3c..b5c57b9c6741e 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs @@ -42,6 +42,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsFixedVariableDeclarationContext || context.IsParameterTypeContext || context.IsPossibleLambdaOrAnonymousMethodParameterTypeContext || + context.IsLocalFunctionDeclarationContext || context.IsImplicitOrExplicitOperatorTypeContext || context.IsPrimaryFunctionExpressionContext || context.IsCrefContext || diff --git a/src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs b/src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs similarity index 64% rename from src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs rename to src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs index a908916e98648..7df1867a4a5ef 100644 --- a/src/Features/CSharp/Portable/DocumentationComments/DocumentationCommentSnippetService.cs +++ b/src/Features/CSharp/Portable/DocumentationComments/CSharpDocumentationCommentSnippetService.cs @@ -11,21 +11,34 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.DocumentationComments { [ExportLanguageService(typeof(IDocumentationCommentSnippetService), LanguageNames.CSharp), Shared] - internal class DocumentationCommentSnippetService : AbstractDocumentationCommentSnippetService + internal class CSharpDocumentationCommentSnippetService : AbstractDocumentationCommentSnippetService { public override string DocumentationCommentCharacter => "/"; protected override bool AddIndent => true; protected override string ExteriorTriviaText => "///"; + private static readonly SymbolDisplayFormat s_format = + new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DocumentationCommentSnippetService() + public CSharpDocumentationCommentSnippetService() { } @@ -111,10 +124,13 @@ protected override List GetDocumentationCommentStubLines(MemberDeclarati } } - if (member.IsKind(SyntaxKind.MethodDeclaration) || - member.IsKind(SyntaxKind.IndexerDeclaration) || - member.IsKind(SyntaxKind.DelegateDeclaration) || - member.IsKind(SyntaxKind.OperatorDeclaration)) + if (member.IsKind( + SyntaxKind.MethodDeclaration, + SyntaxKind.IndexerDeclaration, + SyntaxKind.DelegateDeclaration, + SyntaxKind.OperatorDeclaration, + SyntaxKind.ConstructorDeclaration, + SyntaxKind.DestructorDeclaration)) { var returnType = member.GetMemberType(); if (returnType != null && @@ -122,11 +138,97 @@ protected override List GetDocumentationCommentStubLines(MemberDeclarati { list.Add("/// "); } + + foreach (var exceptionType in GetExceptions(member)) + { + list.Add(@$"/// "); + } } return list; } + private static IEnumerable GetExceptions(SyntaxNode member) + { + var throwExpressionsAndStatements = member.DescendantNodes().Where(n => n.IsKind(SyntaxKind.ThrowExpression, SyntaxKind.ThrowStatement)); + + var usings = member.GetEnclosingUsingDirectives(); + var hasUsingSystem = usings.Any(u => u.Name is IdentifierNameSyntax { Identifier: { ValueText: nameof(System) } }); + + using var _ = PooledHashSet.GetInstance(out var seenExceptionTypes); + foreach (var throwExpressionOrStatement in throwExpressionsAndStatements) + { + var expression = throwExpressionOrStatement switch + { + ThrowExpressionSyntax throwExpression => throwExpression.Expression, + ThrowStatementSyntax throwStatement => throwStatement.Expression, + _ => throw ExceptionUtilities.Unreachable + }; + + if (expression.IsKind(SyntaxKind.NullLiteralExpression)) + { + // `throw null;` throws NullReferenceException at runtime. + var exception = hasUsingSystem ? nameof(NullReferenceException) : $"{nameof(System)}.{nameof(NullReferenceException)}"; + if (seenExceptionTypes.Add(exception)) + yield return exception; + } + else if (expression is ObjectCreationExpressionSyntax { Type: TypeSyntax exceptionType }) + { + exceptionType = exceptionType.ConvertToSingleLine(); + if (!IsExceptionCaughtAndNotRethrown(hasUsingSystem, exceptionType)) + { + var exception = exceptionType.ToString(); + if (seenExceptionTypes.Add(exception)) + yield return exception.Replace('<', '{').Replace('>', '}'); + } + } + } + } + + private static bool IsExceptionCaughtAndNotRethrown(bool hasUsingSystem, TypeSyntax exceptionType) + { + for (SyntaxNode? current = exceptionType; current != null; current = current?.Parent) + { + if (current is not BlockSyntax { Parent: TryStatementSyntax tryStatement } block || + tryStatement.Block != block || + block.DescendantNodes().OfType().Any(t => t.Expression is null)) + { + continue; + } + + foreach (var catchClause in tryStatement.Catches) + { + if (catchClause.Filter != null) + continue; + + // AN empty `catch { }` will always catch everything. + if (catchClause.Declaration == null) + return true; + + // Poor mans equivalence check since we don't have semantics here. + if (SyntaxFactory.AreEquivalent(exceptionType, catchClause.Declaration.Type.ConvertToSingleLine())) + return true; + + if (hasUsingSystem && + catchClause.Declaration.Type is IdentifierNameSyntax { Identifier: { ValueText: nameof(Exception) } }) + { + return true; + } + + if (catchClause.Declaration.Type is QualifiedNameSyntax + { + Left: IdentifierNameSyntax { Identifier: { ValueText: nameof(System) } }, + Right: IdentifierNameSyntax { Identifier: { ValueText: nameof(Exception) } }, + }) + { + return true; + } + } + } + + return false; + } + protected override SyntaxToken GetTokenToRight( SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 79fedf833dda5..86d4e34c3ee1f 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -1373,7 +1373,7 @@ join newVariable in newVariables on oldVariable.Identifier.Text equals newVariab SemanticModel model, CancellationToken cancellationToken) { - if (node.IsKind(SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration)) + if (node.IsKind(SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration, SyntaxKind.FileScopedNamespaceDeclaration)) { return null; } @@ -1915,6 +1915,7 @@ internal override string GetDisplayName(IMethodSymbol symbol) return CSharpFeaturesResources.using_directive; case SyntaxKind.NamespaceDeclaration: + case SyntaxKind.FileScopedNamespaceDeclaration: return FeaturesResources.namespace_; case SyntaxKind.ClassDeclaration: diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionValidator.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionValidator.cs index fa30ca1f3580d..3e63a1ac9e0e5 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionValidator.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionValidator.cs @@ -175,7 +175,7 @@ private SelectionInfo GetInitialSelectionInfo(SyntaxNode root, SourceText text) { return new SelectionInfo { - Status = new OperationStatus(OperationStatusFlag.None, CSharpFeaturesResources.Selection_does_not_contain_a_valid_token), + Status = new OperationStatus(OperationStatusFlag.None, FeaturesResources.Selection_does_not_contain_a_valid_token), OriginalSpan = adjustedSpan, FirstTokenInOriginalSpan = firstTokenInSelection, LastTokenInOriginalSpan = lastTokenInSelection @@ -186,7 +186,7 @@ private SelectionInfo GetInitialSelectionInfo(SyntaxNode root, SourceText text) { return new SelectionInfo { - Status = new OperationStatus(OperationStatusFlag.None, CSharpFeaturesResources.No_valid_selection_to_perform_extraction), + Status = new OperationStatus(OperationStatusFlag.None, FeaturesResources.No_valid_selection_to_perform_extraction), OriginalSpan = adjustedSpan, FirstTokenInOriginalSpan = firstTokenInSelection, LastTokenInOriginalSpan = lastTokenInSelection @@ -198,7 +198,7 @@ private SelectionInfo GetInitialSelectionInfo(SyntaxNode root, SourceText text) { return new SelectionInfo { - Status = new OperationStatus(OperationStatusFlag.None, CSharpFeaturesResources.No_common_root_node_for_extraction), + Status = new OperationStatus(OperationStatusFlag.None, FeaturesResources.No_common_root_node_for_extraction), OriginalSpan = adjustedSpan, FirstTokenInOriginalSpan = firstTokenInSelection, LastTokenInOriginalSpan = lastTokenInSelection @@ -210,7 +210,7 @@ private SelectionInfo GetInitialSelectionInfo(SyntaxNode root, SourceText text) { return new SelectionInfo { - Status = new OperationStatus(OperationStatusFlag.None, CSharpFeaturesResources.No_valid_selection_to_perform_extraction), + Status = new OperationStatus(OperationStatusFlag.None, FeaturesResources.No_valid_selection_to_perform_extraction), OriginalSpan = adjustedSpan, FirstTokenInOriginalSpan = firstTokenInSelection, LastTokenInOriginalSpan = lastTokenInSelection diff --git a/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs index e2080313e09fd..eaecfd9a64afa 100644 --- a/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMemberStatic/CSharpMakeMemberStaticCodeFixProvider.cs @@ -2,14 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - +using System; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MakeMemberStatic; namespace Microsoft.CodeAnalysis.CSharp.MakeMemberStatic @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.MakeMemberStatic internal sealed class CSharpMakeMemberStaticCodeFixProvider : AbstractMakeMemberStaticCodeFixProvider { [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpMakeMemberStaticCodeFixProvider() { } @@ -28,8 +28,22 @@ public CSharpMakeMemberStaticCodeFixProvider() "CS0708" // 'MyMethod': cannot declare instance members in a static class ); - protected override bool IsValidMemberNode(SyntaxNode node) => - node is MemberDeclarationSyntax || - (node.IsKind(SyntaxKind.VariableDeclarator) && node.Ancestors().Any(a => a.IsKind(SyntaxKind.FieldDeclaration) || a.IsKind(SyntaxKind.EventFieldDeclaration))); + protected override bool TryGetMemberDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? memberDeclaration) + { + if (node is MemberDeclarationSyntax) + { + memberDeclaration = node; + return true; + } + + if (node.IsKind(SyntaxKind.VariableDeclarator) && node.Parent is VariableDeclarationSyntax { Parent: FieldDeclarationSyntax or EventFieldDeclarationSyntax }) + { + memberDeclaration = node.Parent.Parent; + return true; + } + + memberDeclaration = null; + return false; + } } } diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index 45a27fb7e9b9e..3702eb2713aaf 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -137,11 +137,6 @@ Zjistily se konflikty. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Nastavit privátní pole jako jenom pro čtení, kde je to možné @@ -187,6 +182,11 @@ Rozpečetit třídu {0} + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Upozornění: dočasné vkládání do volání podmíněné metody. @@ -302,21 +302,6 @@ Neplatný výběr - - Selection does not contain a valid token. - Výběr neobsahuje platný token. - - - - No valid selection to perform extraction. - Žádný platný výběr k provedení extrakce - - - - No common root node for extraction. - Žádný společný kořenový uzel pro extrakci - - Contains invalid selection. Obsahuje neplatný výběr. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 837f34974766b..91196c439507c 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -137,11 +137,6 @@ Konflikt(e) erkannt. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Private Felder nach Möglichkeit als schreibgeschützt festlegen @@ -187,6 +182,11 @@ Versiegelung der Klasse "{0}" aufheben + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Warnung: temporäres Inlining in bedingtem Methodenaufruf. @@ -302,21 +302,6 @@ Ungültige Auswahl. - - Selection does not contain a valid token. - Auswahl enthält kein gültiges Token. - - - - No valid selection to perform extraction. - Keine gültige Auswahl zum Durchführen der Extraktion. - - - - No common root node for extraction. - Kein gemeinsamer Stammknoten für Extraktion. - - Contains invalid selection. Enthält eine ungültige Auswahl. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 97bdbf70cdbca..bec3d1419f1d4 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -137,11 +137,6 @@ Conflicto(s) detectado(s). - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Cuando sea posible, hacer que los cambios privados sean solo de lectura @@ -187,6 +182,11 @@ Quitar el sello de la clase "{0}" + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Advertencia: Inserción de una llamada temporal en otra de método condicional. @@ -302,21 +302,6 @@ Selección no válida. - - Selection does not contain a valid token. - La selección no contiene un token válido - - - - No valid selection to perform extraction. - Selección no válida para efectuar extracción. - - - - No common root node for extraction. - No hay nodo raíz común para la extracción. - - Contains invalid selection. Contiene una selección no válida. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index bf6ca47d26a47..d87f11267b27f 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -137,11 +137,6 @@ Conflit(s) détecté(s). - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Configurer les champs privés en lecture seule quand cela est possible @@ -187,6 +182,11 @@ Classe unsealed '{0}' + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Avertissement : Inlining temporaire dans un appel de méthode conditionnel. @@ -302,21 +302,6 @@ Sélection incorrecte. - - Selection does not contain a valid token. - La sélection ne contient pas un jeton valide. - - - - No valid selection to perform extraction. - Pas de sélection valide pour procéder à l'extraction. - - - - No common root node for extraction. - Pas de nœud racine commun pour l'extraction. - - Contains invalid selection. Contient une sélection incorrecte. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 0447635cd07e5..bdd40c5dbe4ed 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -137,11 +137,6 @@ Sono stati rilevati conflitti. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Imposta i campi privati come di sola lettura quando possibile @@ -187,6 +182,11 @@ Rimuovi seal dalla classe '{0}' + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Avviso: incorporamento dell'elemento temporaneo nella chiamata a un metodo condizionale. @@ -302,21 +302,6 @@ Selezione non valida. - - Selection does not contain a valid token. - La selezione non contiene un token valido. - - - - No valid selection to perform extraction. - La selezione non è valida per eseguire l'estrazione. - - - - No common root node for extraction. - Non esiste un nodo radice comune per l'estrazione. - - Contains invalid selection. Contiene una selezione non valida. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 8717f718be560..0079e8fbf0053 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -137,11 +137,6 @@ 競合が検出されました。 - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible 可能な場合、private フィールドを読み取り専用にする @@ -187,6 +182,11 @@ クラス '{0}' のシールを解除 + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. 警告: 一時メソッド呼び出しを条件付きメソッド呼び出しにインライン展開しています。 @@ -302,21 +302,6 @@ 選択が無効です。 - - Selection does not contain a valid token. - 選択には有効なトークンは含まれません。 - - - - No valid selection to perform extraction. - 抽出を行うための有効な選択がありません。 - - - - No common root node for extraction. - 抽出するための一般的なルート ノードがありません。 - - Contains invalid selection. 無効な選択が含まれています。 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 8e0702f33c52a..112b1206b2cc6 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -137,11 +137,6 @@ 충돌이 감지되었습니다. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible 가능한 경우 프라이빗 필드를 읽기 전용으로 만들기 @@ -187,6 +182,11 @@ '{0}' 클래스 봉인 해제 + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. 경고: 임시 작업을 조건부 메서드 호출로 인라인 처리합니다. @@ -302,21 +302,6 @@ 잘못된 선택 항목입니다. - - Selection does not contain a valid token. - 선택 항목에 유효한 토큰이 포함되어 있지 않습니다. - - - - No valid selection to perform extraction. - 추출을 수행할 선택 항목이 잘못되었습니다. - - - - No common root node for extraction. - 추출에 필요한 일반 루트 노드가 없습니다. - - Contains invalid selection. 잘못된 선택 항목을 포함합니다. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 930c28f5d4e9d..bc4040ea0f22f 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -137,11 +137,6 @@ Wykryto konflikty. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Ustawiaj pola prywatne jako tylko do odczytu, gdy to możliwe @@ -187,6 +182,11 @@ Odpieczętuj klasę „{0}” + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Ostrzeżenie: tymczasowe wbudowywanie do wywołania metody warunkowej. @@ -302,21 +302,6 @@ Nieprawidłowe zaznaczenie. - - Selection does not contain a valid token. - Zaznaczenie nie zawiera prawidłowego tokenu. - - - - No valid selection to perform extraction. - Brak prawidłowego zaznaczenia do wyodrębnienia. - - - - No common root node for extraction. - Brak wspólnego węzła głównego do wyodrębnienia. - - Contains invalid selection. Zawiera nieprawidłowe zaznaczenie. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index 5a1b3b7449225..c7ed992348c80 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -137,11 +137,6 @@ Conflito(s) detectado(s). - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Tornar os campos privados somente leitura quando possível @@ -187,6 +182,11 @@ Desselar a classe '{0}' + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Aviso: embutindo a chamada de método temporária na condicional. @@ -302,21 +302,6 @@ Seleção inválida. - - Selection does not contain a valid token. - A seleção não contém um token válido. - - - - No valid selection to perform extraction. - Nenhuma seleção válida para realizar a extração. - - - - No common root node for extraction. - Nenhum nó de raiz comum para extração. - - Contains invalid selection. Contém uma seleção inválida. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index a003e9df7cb61..de20becf81d5c 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -137,11 +137,6 @@ Обнаружены конфликты. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible При возможности делать частные поля доступными только для чтения @@ -187,6 +182,11 @@ Распечатать класс "{0}" + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Предупреждение: встраивание временных элементов в условный вызов метода. @@ -302,21 +302,6 @@ Недопустимое выделение. - - Selection does not contain a valid token. - Выделение не содержит допустимый токен. - - - - No valid selection to perform extraction. - Нет допустимого выделения для извлечения. - - - - No common root node for extraction. - Нет стандартного корневого узла для извлечения. - - Contains invalid selection. Содержит недопустимое выделение. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 5ab4cd6e4f8b2..b32bf8f1c0af6 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -137,11 +137,6 @@ Çakışmalar algılandı. - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible Özel alanları mümkün olduğunda salt okunur yap @@ -187,6 +182,11 @@ '{0}' sınıfının mührünü aç + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. Uyarı: Koşullu yöntem çağrısında geçici öğe satır içinde kullanılıyor. @@ -302,21 +302,6 @@ Geçersiz seçim. - - Selection does not contain a valid token. - Seçim, geçerli bir belirteç içermiyor. - - - - No valid selection to perform extraction. - Ayıklamayı gerçekleştirme için geçerli seçim yok. - - - - No common root node for extraction. - Ayıklama için ortak kök düğümü yok. - - Contains invalid selection. Geçersiz seçimi içerir. diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 11ca99c89a76e..edd6f403a7169 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -137,11 +137,6 @@ 检测到冲突。 - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible 尽可能将私有字段设置为“只读” @@ -187,6 +182,11 @@ Unseal 类 "{0}" + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. 警告: 即将在条件方法调用中内联临时内容。 @@ -302,21 +302,6 @@ 无效的选择。 - - Selection does not contain a valid token. - 所选内容不包含有效令牌。 - - - - No valid selection to perform extraction. - 没有执行提取的有效选择。 - - - - No common root node for extraction. - 没有可用于提取的公共根节点。 - - Contains invalid selection. 包含无效的选择。 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 3333bd8b7eed5..cff1d93b94da8 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -137,11 +137,6 @@ 偵測到衝突。 - - Make container 'async' - Make container 'async' - {Locked="async"} "async" is a C# keyword and should not be localized. - Make private fields readonly when possible 若可能的話,請將私用欄位設定為唯讀 @@ -187,6 +182,11 @@ 為類別 '{0}' 解密 + + Use recursive patterns + Use recursive patterns + + Warning: Inlining temporary into conditional method call. 警告: 內嵌臨時加入條件式方法呼叫。 @@ -302,21 +302,6 @@ 無效的選取範圍。 - - Selection does not contain a valid token. - 選取範圍沒有包含有效的語彙基元。 - - - - No valid selection to perform extraction. - 沒有可執行擷取的有效選取範圍。 - - - - No common root node for extraction. - 沒有根節點可供擷取。 - - Contains invalid selection. 包含無效的選取範圍。 diff --git a/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs b/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs index ea00e9e4a6aaf..a6dcc9516df70 100644 --- a/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs +++ b/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs @@ -77,5 +77,6 @@ internal static class PredefinedCodeRefactoringProviderNames public const string ConvertPlaceholderToInterpolatedString = nameof(ConvertPlaceholderToInterpolatedString); public const string ConvertConcatenationToInterpolatedString = nameof(ConvertConcatenationToInterpolatedString); public const string InvertMultiLineIf = nameof(InvertMultiLineIf); + public const string UseRecursivePatterns = nameof(UseRecursivePatterns); } } diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractAwaitCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractAwaitCompletionProvider.cs new file mode 100644 index 0000000000000..a02284e842dc6 --- /dev/null +++ b/src/Features/Core/Portable/Completion/Providers/AbstractAwaitCompletionProvider.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Completion.Providers +{ + /// + /// A completion provider for offering keyword. + /// This is implemented separately, not as a keyword recommender as it contains extra logic for making container method async. + /// + internal abstract class AbstractAwaitCompletionProvider : LSPCompletionProvider + { + /// + /// Gets the span start where async keyword should go. + /// + private protected abstract int GetSpanStart(SyntaxNode declaration); + + private protected abstract SyntaxNode? GetAsyncSupportingDeclaration(SyntaxToken token); + + public sealed override async Task ProvideCompletionsAsync(CompletionContext context) + { + var document = context.Document; + var position = context.Position; + var cancellationToken = context.CancellationToken; + var syntaxFacts = document.GetRequiredLanguageService(); + var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken)) + { + return; + } + + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); + var workspace = document.Project.Solution.Workspace; + var syntaxContext = document.GetRequiredLanguageService().CreateContext(workspace, semanticModel, position, cancellationToken); + if (!syntaxContext.IsAwaitKeywordContext()) + { + return; + } + + var generator = SyntaxGenerator.GetGenerator(document); + var syntaxKinds = document.GetRequiredLanguageService(); + var completionItem = GetCompletionItem(syntaxContext.TargetToken, generator, syntaxKinds, syntaxFacts); + context.AddItem(completionItem); + } + + public sealed override async Task GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default) + { + // IsComplexTextEdit is true when we want to add async to the container. + if (!item.IsComplexTextEdit) + { + return await base.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false); + } + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var declaration = GetAsyncSupportingDeclaration(root.FindToken(item.Span.Start)); + if (declaration is null) + { + // IsComplexTextEdit should only be true when GetAsyncSupportingDeclaration returns non-null. + // This is ensured by the ShouldMakeContainerAsync overrides. + Debug.Assert(false, "Expected non-null value for declaration."); + return await base.GetChangeAsync(document, item, commitKey, cancellationToken).ConfigureAwait(false); + } + + var syntaxFacts = document.GetRequiredLanguageService(); + var syntaxKinds = document.GetRequiredLanguageService(); + + using var _ = ArrayBuilder.GetInstance(out var builder); + builder.Add(new TextChange(new TextSpan(GetSpanStart(declaration), 0), syntaxFacts.GetText(syntaxKinds.AsyncKeyword) + " ")); + builder.Add(new TextChange(item.Span, item.DisplayText)); + + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var newText = text.WithChanges(builder); + return CompletionChange.Create(Utilities.Collapse(newText, builder.ToImmutableArray())); + } + +#pragma warning disable VSTHRD200 // Use "Async" suffix for async methods + private protected bool ShouldMakeContainerAsync(SyntaxToken token, SyntaxGenerator generator) +#pragma warning restore VSTHRD200 // Use "Async" suffix for async methods + { + var declaration = GetAsyncSupportingDeclaration(token); + return declaration is not null && !generator.GetModifiers(declaration).IsAsync; + } + + private CompletionItem GetCompletionItem(SyntaxToken token, SyntaxGenerator generator, ISyntaxKindsService syntaxKinds, ISyntaxFactsService syntaxFacts) + { + var shouldMakeContainerAsync = ShouldMakeContainerAsync(token, generator); + var text = syntaxFacts.GetText(syntaxKinds.AwaitKeyword); + return CommonCompletionItem.Create( + displayText: text, + displayTextSuffix: "", + rules: CompletionItemRules.Default, + Glyph.Keyword, + description: RecommendedKeyword.CreateDisplayParts(text, FeaturesResources.Asynchronously_waits_for_the_task_to_finish), + inlineDescription: shouldMakeContainerAsync ? FeaturesResources.Make_containing_scope_async : null, + isComplexTextEdit: shouldMakeContainerAsync); + } + } +} diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index dfeac6717fa33..79a3082ca3f9e 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -4576,18 +4576,29 @@ select clausesByQuery.First()) } } + using var oldLambdaBodyEnumerator = GetLambdaBodies(oldMemberBody).GetEnumerator(); + using var newLambdaBodyEnumerator = GetLambdaBodies(newMemberBody).GetEnumerator(); + var oldHasLambdas = oldLambdaBodyEnumerator.MoveNext(); + var newHasLambdas = newLambdaBodyEnumerator.MoveNext(); + + // Exit early if there are no lambdas in the method to avoid expensive data flow analysis: + if (!oldHasLambdas && !newHasLambdas) + { + return; + } + var oldCaptures = GetCapturedVariables(oldModel, oldMemberBody); var newCaptures = GetCapturedVariables(newModel, newMemberBody); // { new capture index -> old capture index } - var reverseCapturesMap = ArrayBuilder.GetInstance(newCaptures.Length, 0); + using var _1 = ArrayBuilder.GetInstance(newCaptures.Length, fillWithValue: 0, out var reverseCapturesMap); // { new capture index -> new closure scope or null for "this" } - var newCapturesToClosureScopes = ArrayBuilder.GetInstance(newCaptures.Length, null); + using var _2 = ArrayBuilder.GetInstance(newCaptures.Length, fillWithValue: null, out var newCapturesToClosureScopes); // Can be calculated from other maps but it's simpler to just calculate it upfront. // { old capture index -> old closure scope or null for "this" } - var oldCapturesToClosureScopes = ArrayBuilder.GetInstance(oldCaptures.Length, null); + using var _3 = ArrayBuilder.GetInstance(oldCaptures.Length, fillWithValue: null, out var oldCapturesToClosureScopes); CalculateCapturedVariablesMaps( oldCaptures, @@ -4617,8 +4628,8 @@ select clausesByQuery.First()) // - Lambda methods are generated to the same frame as before, so they can be updated in-place. // - "Parent" links between closure scopes are preserved. - using var _1 = PooledDictionary.GetInstance(out var oldCapturesIndex); - using var _2 = PooledDictionary.GetInstance(out var newCapturesIndex); + using var _11 = PooledDictionary.GetInstance(out var oldCapturesIndex); + using var _12 = PooledDictionary.GetInstance(out var newCapturesIndex); BuildIndex(oldCapturesIndex, oldCaptures); BuildIndex(newCapturesIndex, newCaptures); @@ -4706,42 +4717,45 @@ select clausesByQuery.First()) var containingTypeDeclaration = TryGetContainingTypeDeclaration(newMemberBody); var isInInterfaceDeclaration = containingTypeDeclaration != null && IsInterfaceDeclaration(containingTypeDeclaration); - foreach (var newLambda in newMemberBody.DescendantNodesAndSelf()) + var newHasLambdaBodies = newHasLambdas; + while (newHasLambdaBodies) { - if (TryGetLambdaBodies(newLambda, out var newLambdaBody1, out var newLambdaBody2)) + var (newLambda, newLambdaBody1, newLambdaBody2) = newLambdaBodyEnumerator.Current; + + if (!map.Reverse.ContainsKey(newLambda)) { - if (!map.Reverse.ContainsKey(newLambda)) + if (!CanAddNewLambda(newLambda, capabilities, matchedLambdas)) { - if (!CanAddNewLambda(newLambda, capabilities, matchedLambdas)) - { - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertNotSupportedByRuntime, GetDiagnosticSpan(newLambda, EditKind.Insert), newLambda, new string[] { GetDisplayName(newLambda, EditKind.Insert) })); - } + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertNotSupportedByRuntime, GetDiagnosticSpan(newLambda, EditKind.Insert), newLambda, new string[] { GetDisplayName(newLambda, EditKind.Insert) })); + } - // TODO: https://github.com/dotnet/roslyn/issues/37128 - // Local functions are emitted directly to the type containing the containing method. - // Although local functions are non-virtual the Core CLR currently does not support adding any method to an interface. - if (isInInterfaceDeclaration && IsLocalFunction(newLambda)) - { - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertLocalFunctionIntoInterfaceMethod, GetDiagnosticSpan(newLambda, EditKind.Insert), newLambda)); - } + // TODO: https://github.com/dotnet/roslyn/issues/37128 + // Local functions are emitted directly to the type containing the containing method. + // Although local functions are non-virtual the Core CLR currently does not support adding any method to an interface. + if (isInInterfaceDeclaration && IsLocalFunction(newLambda)) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertLocalFunctionIntoInterfaceMethod, GetDiagnosticSpan(newLambda, EditKind.Insert), newLambda)); + } - ReportMultiScopeCaptures(newLambdaBody1, newModel, newCaptures, newCaptures, newCapturesToClosureScopes, newCapturesIndex, reverseCapturesMap, diagnostics, isInsert: true, cancellationToken: cancellationToken); + ReportMultiScopeCaptures(newLambdaBody1, newModel, newCaptures, newCaptures, newCapturesToClosureScopes, newCapturesIndex, reverseCapturesMap, diagnostics, isInsert: true, cancellationToken: cancellationToken); - if (newLambdaBody2 != null) - { - ReportMultiScopeCaptures(newLambdaBody2, newModel, newCaptures, newCaptures, newCapturesToClosureScopes, newCapturesIndex, reverseCapturesMap, diagnostics, isInsert: true, cancellationToken: cancellationToken); - } + if (newLambdaBody2 != null) + { + ReportMultiScopeCaptures(newLambdaBody2, newModel, newCaptures, newCaptures, newCapturesToClosureScopes, newCapturesIndex, reverseCapturesMap, diagnostics, isInsert: true, cancellationToken: cancellationToken); } - - syntaxMapRequired = true; } + + newHasLambdaBodies = newLambdaBodyEnumerator.MoveNext(); } // Similarly for addition. We don't allow removal of lambda that has captures from multiple scopes. - foreach (var oldLambda in oldMemberBody.DescendantNodesAndSelf()) + var oldHasMoreLambdas = oldHasLambdas; + while (oldHasMoreLambdas) { - if (TryGetLambdaBodies(oldLambda, out var oldLambdaBody1, out var oldLambdaBody2) && !map.Forward.ContainsKey(oldLambda)) + var (oldLambda, oldLambdaBody1, oldLambdaBody2) = oldLambdaBodyEnumerator.Current; + + if (!map.Forward.ContainsKey(oldLambda)) { ReportMultiScopeCaptures(oldLambdaBody1, oldModel, oldCaptures, newCaptures, oldCapturesToClosureScopes, oldCapturesIndex, reverseCapturesMap, diagnostics, isInsert: false, cancellationToken: cancellationToken); @@ -4750,11 +4764,22 @@ select clausesByQuery.First()) ReportMultiScopeCaptures(oldLambdaBody2, oldModel, oldCaptures, newCaptures, oldCapturesToClosureScopes, oldCapturesIndex, reverseCapturesMap, diagnostics, isInsert: false, cancellationToken: cancellationToken); } } + + oldHasMoreLambdas = oldLambdaBodyEnumerator.MoveNext(); } - reverseCapturesMap.Free(); - newCapturesToClosureScopes.Free(); - oldCapturesToClosureScopes.Free(); + syntaxMapRequired = newHasLambdas; + } + + private IEnumerable<(SyntaxNode lambda, SyntaxNode lambdaBody1, SyntaxNode? lambdaBody2)> GetLambdaBodies(SyntaxNode body) + { + foreach (var node in body.DescendantNodesAndSelf()) + { + if (TryGetLambdaBodies(node, out var body1, out var body2)) + { + yield return (node, body1, body2); + } + } } private bool CanAddNewLambda(SyntaxNode newLambda, EditAndContinueCapabilities capabilities, IReadOnlyDictionary? matchedLambdas) diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 81212894ae76d..d87c3270a535e 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -471,7 +471,7 @@ Updating the library name of Declare statement requires restarting the application. {Locked="Declare"} "Declare" is VB keyword and should not be localized. - + Updating the alias of Declare statement requires restarting the application. {Locked="Declare"} "Declare" is VB keyword and should not be localized. @@ -962,6 +962,9 @@ This version used in: {2} Add missing param nodes + + Asynchronously waits for the task to finish. + Make containing scope async @@ -2899,4 +2902,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Changing constraints of {0} requires restarting the application. + + No common root node for extraction. + + + No valid selection to perform extraction. + + + Selection does not contain a valid token. + + + Selection not contained inside a type. + \ No newline at end of file diff --git a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs index 6bf9986323c93..57b067df08fb8 100644 --- a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs +++ b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyCodeFixProvider.cs @@ -9,11 +9,14 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.Shared.Utilities.EditorBrowsableHelpers; namespace Microsoft.CodeAnalysis.CodeFixes.FullyQualify { @@ -62,7 +65,7 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var matchingTypes = await GetMatchingTypesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); + var matchingTypes = await GetMatchingTypesAsync(document, semanticModel, node, cancellationToken).ConfigureAwait(false); var matchingNamespaces = await GetMatchingNamespacesAsync(project, semanticModel, node, cancellationToken).ConfigureAwait(false); if (matchingTypes.IsEmpty && matchingNamespaces.IsEmpty) @@ -144,10 +147,11 @@ private async Task ProcessNodeAsync(Document document, SyntaxNode node } private async Task> GetMatchingTypesAsync( - Project project, SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + Document document, SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + var project = document.Project; var syntaxFacts = project.LanguageServices.GetRequiredService(); syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out var arity); @@ -167,9 +171,14 @@ private async Task> GetMatchingTypesAsync( symbols = symbols.Concat(attributeSymbols); } + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var hideAdvancedMembers = options.GetOption(CompletionOptions.HideAdvancedMembers); + var editorBrowserInfo = new EditorBrowsableInfo(semanticModel.Compilation); + var validSymbols = symbols .OfType() - .Where(s => IsValidNamedTypeSearchResult(semanticModel, arity, inAttributeContext, looksGeneric, s)) + .Where(s => IsValidNamedTypeSearchResult(semanticModel, arity, inAttributeContext, looksGeneric, s) && + s.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation, editorBrowserInfo)) .ToImmutableArray(); // Check what the current node binds to. If it binds to any symbols, but with diff --git a/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs b/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs index e2dacdac3da69..9e1ebb32365e3 100644 --- a/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs @@ -18,12 +18,12 @@ internal abstract class AbstractMakeMemberStaticCodeFixProvider : SyntaxEditorBa { internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.Compile; - protected abstract bool IsValidMemberNode([NotNullWhen(true)] SyntaxNode? node); + protected abstract bool TryGetMemberDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? memberDeclaration); public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) { if (context.Diagnostics.Length == 1 && - IsValidMemberNode(context.Diagnostics[0].Location?.FindNode(context.CancellationToken))) + TryGetMemberDeclaration(context.Diagnostics[0].Location.FindNode(context.CancellationToken), out _)) { context.RegisterCodeFix( new MyCodeAction(c => FixAsync(context.Document, context.Diagnostics[0], c)), @@ -38,12 +38,13 @@ protected sealed override Task FixAllAsync(Document document, ImmutableArray generator.WithModifiers(currentDeclaration, DeclarationModifiers.Static)); + var generator = SyntaxGenerator.GetGenerator(document); + var newNode = generator.WithModifiers(memberDeclaration, generator.GetModifiers(declaration).WithIsStatic(true)); + editor.ReplaceNode(declaration, newNode); } } diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index b9a2fbe8317c5..3826bb5c95f3a 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -54,6 +54,7 @@ +