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}