Skip to content

Commit

Permalink
Bind lambda in object initializer despite errors (#75695)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Nov 7, 2024
1 parent 103fe0c commit 96b3071
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 159 deletions.
8 changes: 5 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ private BoundStatement BindYieldReturnStatement(YieldStatementSyntax node, Bindi
? BadExpression(node).MakeCompilerGenerated()
: BindValue(node.Expression, diagnostics, BindValueKind.RValue);

if (!argument.HasErrors && ((object)argument.Type == null || !argument.Type.IsErrorType()))
if (elementType is { } && node.Expression != null)
{
argument = GenerateConversionForAssignment(elementType, argument, diagnostics);
}
Expand Down Expand Up @@ -1482,11 +1482,13 @@ private BoundAssignmentOperator BindAssignment(

bool hasErrors = op1.HasAnyErrors || op2.HasAnyErrors;

if (!op1.HasAnyErrors)
if (op1.Type is { } lhsType && !lhsType.IsErrorType())
{
// Build bound conversion. The node might not be used if this is a dynamic conversion
// but diagnostics should be reported anyways.
var conversion = GenerateConversionForAssignment(op1.Type, op2, diagnostics, isRef ? ConversionForAssignmentFlags.RefAssignment : ConversionForAssignmentFlags.None);
var conversion = GenerateConversionForAssignment(lhsType, op2,
hasErrors ? BindingDiagnosticBag.Discarded : diagnostics,
isRef ? ConversionForAssignmentFlags.RefAssignment : ConversionForAssignmentFlags.None);

// If the result is a dynamic assignment operation (SetMember or SetIndex),
// don't generate the boxing conversion to the dynamic type.
Expand Down
73 changes: 73 additions & 0 deletions src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenExprLambdaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
using Roslyn.Test.Utilities;
using Xunit;
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
Expand Down Expand Up @@ -1815,6 +1817,77 @@ static string P(S? s)
S");
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72571")]
public void LambdaWithBindingErrorInInitializerOfTargetTypedNew()
{
var src = """
AddConfig(new()
{
A = a =>
{
a // 1
},
});
static void AddConfig(Config config) { }
class Config
{
public System.Action<A> A { get; set; }
}
class A { }
""";
var comp = CreateCompilation(src);
comp.VerifyEmitDiagnostics(
// (5,10): error CS1002: ; expected
// a // 1
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 10));

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var s = GetSyntax<IdentifierNameSyntax>(tree, "a");
Assert.Equal("A a", model.GetSymbolInfo(s).Symbol.ToTestDisplayString());
Assert.Equal(new string[] { }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings());
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72571")]
public void LambdaWithBindingErrorInInitializerWithReadonlyTarget()
{
var src = """
AddConfig(new Config()
{
A = a =>
{
a // 1
},
});
static void AddConfig(Config config) { }
class Config
{
public System.Action<A> A { get; }
}
class A { }
""";
var comp = CreateCompilation(src);
comp.VerifyEmitDiagnostics(
// (3,5): error CS0200: Property or indexer 'Config.A' cannot be assigned to -- it is read only
// A = a =>
Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "A").WithArguments("Config.A").WithLocation(3, 5),
// (5,10): error CS1002: ; expected
// a // 1
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(5, 10));

var tree = comp.SyntaxTrees.Single();
var model = comp.GetSemanticModel(tree);
var s = GetSyntax<IdentifierNameSyntax>(tree, "a");
Assert.Equal("A a", model.GetSymbolInfo(s).Symbol.ToTestDisplayString());
Assert.Equal(new string[] { }, model.GetSymbolInfo(s).CandidateSymbols.ToTestDisplayStrings());
}

#region Regression Tests

[WorkItem(544159, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544159")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,6 @@ public struct S1
Statements (0)
Next (Regular) Block[B1]
Entering: {R1} {R2}
.locals {R1}
{
CaptureIds: [0]
Expand All @@ -1004,61 +1003,58 @@ public struct S1
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x')
Value:
Value:
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?, IsInvalid) (Syntax: 'x')
Jump if True (Regular) to Block[B3]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x')
Operand:
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x')
Leaving: {R2}
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.P1')
Value:
Value:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: '.P1')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(ImplicitNullable)
Operand:
Operand:
IPropertyReferenceOperation: System.Int32 S1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Int32, IsInvalid) (Syntax: '.P1')
Instance Receiver:
Instance Receiver:
IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsInvalid, IsImplicit) (Syntax: 'x')
Instance Receiver:
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x')
Arguments(0)
Next (Regular) Block[B4]
Leaving: {R2}
}
Block[B3] - Block
Predecessors: [B1]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x')
Value:
Value:
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x')
Next (Regular) Block[B4]
Block[B4] - Block
Predecessors: [B2] [B3]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x?.P1 = 0;')
Expression:
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32?, IsInvalid) (Syntax: 'x?.P1 = 0')
Left:
Left:
IInvalidOperation (OperationKind.Invalid, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1')
Children(1):
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsImplicit) (Syntax: '0')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(ImplicitNullable)
Operand:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Next (Regular) Block[B5]
Leaving: {R1}
}
Block[B5] - Exit
Predecessors: [B4]
Statements (0)
Expand Down Expand Up @@ -1094,7 +1090,6 @@ public struct S1
Statements (0)
Next (Regular) Block[B1]
Entering: {R1} {R2}
.locals {R1}
{
CaptureIds: [0]
Expand All @@ -1105,61 +1100,58 @@ public struct S1
Predecessors: [B0]
Statements (1)
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x')
Value:
Value:
IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?, IsInvalid) (Syntax: 'x')
Jump if True (Regular) to Block[B3]
IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x')
Operand:
Operand:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x')
Leaving: {R2}
Next (Regular) Block[B2]
Block[B2] - Block
Predecessors: [B1]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.P1')
Value:
Value:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: '.P1')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(ImplicitNullable)
Operand:
Operand:
IFieldReferenceOperation: System.Int32 S1.P1 (OperationKind.FieldReference, Type: System.Int32, IsInvalid) (Syntax: '.P1')
Instance Receiver:
Instance Receiver:
IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsInvalid, IsImplicit) (Syntax: 'x')
Instance Receiver:
Instance Receiver:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x')
Arguments(0)
Next (Regular) Block[B4]
Leaving: {R2}
}
Block[B3] - Block
Predecessors: [B1]
Statements (1)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x')
Value:
Value:
IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x')
Next (Regular) Block[B4]
Block[B4] - Block
Predecessors: [B2] [B3]
Statements (1)
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x?.P1 = 0;')
Expression:
Expression:
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32?, IsInvalid) (Syntax: 'x?.P1 = 0')
Left:
Left:
IInvalidOperation (OperationKind.Invalid, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1')
Children(1):
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1')
Right:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Right:
IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsImplicit) (Syntax: '0')
Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
(ImplicitNullable)
Operand:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
Next (Regular) Block[B5]
Leaving: {R1}
}
Block[B5] - Exit
Predecessors: [B4]
Statements (0)
Expand Down
Loading

0 comments on commit 96b3071

Please sign in to comment.