Skip to content

Commit

Permalink
IObjectCreationExpression API Change
Browse files Browse the repository at this point in the history
There are couple of changes here:
1. API change: `ImmutableArray<ISymbolInitializer> MemberInitializers` is changed to `ImmutableArray<IOperation> Initializers`.
2. Implementation changes:
   1. Instead of returning the member initializers as synthesized ISymbolInitializer nodes, we now return member intializers as IAssignmentExpression nodes. This ensures completeness of IOperation tree.
   2. Now we also return the collection intializer expressions within an object creation expression.

Fixes dotnet#18115

There are 2 bugs still affecting this area:
1. dotnet#18781: IOperation API shape for collection initializer expressions
2. dotnet#19276: Missing Field/Property reference expression nodes in object creation initializer node
  • Loading branch information
mavasani committed May 4, 2017
1 parent ca1b475 commit dcd8fcb
Show file tree
Hide file tree
Showing 14 changed files with 590 additions and 312 deletions.
38 changes: 1 addition & 37 deletions src/Compilers/CSharp/Portable/BoundTree/Expression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -515,43 +515,7 @@ internal partial class BoundObjectCreationExpression : IObjectCreationExpression

ImmutableArray<IArgument> IHasArgumentsExpression.ArgumentsInEvaluationOrder => BoundCall.DeriveArguments(this.Arguments, this.ArgumentNamesOpt, this.ArgsToParamsOpt, this.ArgumentRefKindsOpt, this.Constructor.Parameters, this.Syntax);

ImmutableArray<ISymbolInitializer> IObjectCreationExpression.MemberInitializers
{
get
{
return (ImmutableArray<ISymbolInitializer>)s_memberInitializersMappings.GetValue(this,
objectCreationExpression =>
{
var objectInitializerExpression = this.InitializerExpressionOpt as BoundObjectInitializerExpression;
if (objectInitializerExpression != null)
{
var builder = ArrayBuilder<ISymbolInitializer>.GetInstance(objectInitializerExpression.Initializers.Length);
foreach (var memberAssignment in objectInitializerExpression.Initializers)
{
var assignment = memberAssignment as BoundAssignmentOperator;
var leftSymbol = (assignment?.Left as BoundObjectInitializerMember)?.MemberSymbol;

if ((object)leftSymbol == null)
{
continue;
}

switch (leftSymbol.Kind)
{
case SymbolKind.Field:
builder.Add(new FieldInitializer(assignment.Syntax, (IFieldSymbol)leftSymbol, assignment.Right));
break;
case SymbolKind.Property:
builder.Add(new PropertyInitializer(assignment.Syntax, (IPropertySymbol)leftSymbol, assignment.Right));
break;
}
}
return builder.ToImmutableAndFree();
}
return ImmutableArray<ISymbolInitializer>.Empty;
});
}
}
ImmutableArray<IOperation> IObjectCreationExpression.Initializers => GetChildInitializers(this.InitializerExpressionOpt).As<IOperation>();

internal static ImmutableArray<BoundExpression> GetChildInitializers(BoundExpression objectOrCollectionInitializer)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<Compile Include="IOperation\IOperationTests_IArgument.cs" />
<Compile Include="IOperation\IOperationTests_IIfStatement.cs" />
<Compile Include="IOperation\IOperationTests_IFieldReferenceExpression.cs" />
<Compile Include="IOperation\IOperationTests_IObjectCreationExpression.cs" />
<Compile Include="IOperation\IOperationTests_IParameterReferenceExpression.cs" />
<Compile Include="IOperation\IOperationTests_ISymbolInitializer.cs" />
<Compile Include="IOperation\IOperationTests_InvalidExpression.cs" />
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -165,26 +165,30 @@ public void M(int x, int y, int z)
";
string expectedOperationTree = @"
IObjectCreationExpression (Constructor: Class..ctor()) (OperationKind.ObjectCreationExpression, Type: Class) (Syntax: 'new Class() ... { X = z } }')
Member Initializers(4): IPropertyInitializer (Property: System.Int32 Class.X { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'X = x')
IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
IPropertyInitializer (Property: System.Collections.Generic.List<System.Int32> Class.Y { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Y = { x, y, 3 }')
IOperation: (OperationKind.None) (Syntax: '{ x, y, 3 }')
Children(3): IOperation: (OperationKind.None) (Syntax: 'x')
Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
IOperation: (OperationKind.None) (Syntax: 'y')
Children(1): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y')
IOperation: (OperationKind.None) (Syntax: '3')
Children(1): ILiteralExpression (Text: 3) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 3) (Syntax: '3')
IPropertyInitializer (Property: System.Collections.Generic.Dictionary<System.Int32, System.Int32> Class.Z { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Z = { { x, y } }')
IOperation: (OperationKind.None) (Syntax: '{ { x, y } }')
Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }')
Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y')
IPropertyInitializer (Property: Class Class.C { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'C = { X = z }')
IOperation: (OperationKind.None) (Syntax: '{ X = z }')
Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = z')
Left: IOperation: (OperationKind.None) (Syntax: 'X')
Right: IParameterReferenceExpression: z (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'z')
Initializers(4): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = x')
Left: IOperation: (OperationKind.None) (Syntax: 'X')
Right: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Collections.Generic.List<System.Int32>) (Syntax: 'Y = { x, y, 3 }')
Left: IOperation: (OperationKind.None) (Syntax: 'Y')
Right: IOperation: (OperationKind.None) (Syntax: '{ x, y, 3 }')
Children(3): IOperation: (OperationKind.None) (Syntax: 'x')
Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
IOperation: (OperationKind.None) (Syntax: 'y')
Children(1): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y')
IOperation: (OperationKind.None) (Syntax: '3')
Children(1): ILiteralExpression (Text: 3) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 3) (Syntax: '3')
IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Collections.Generic.Dictionary<System.Int32, System.Int32>) (Syntax: 'Z = { { x, y } }')
Left: IOperation: (OperationKind.None) (Syntax: 'Z')
Right: IOperation: (OperationKind.None) (Syntax: '{ { x, y } }')
Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }')
Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x')
IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y')
IAssignmentExpression (OperationKind.AssignmentExpression, Type: Class) (Syntax: 'C = { X = z }')
Left: IOperation: (OperationKind.None) (Syntax: 'C')
Right: IOperation: (OperationKind.None) (Syntax: '{ X = z }')
Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = z')
Left: IOperation: (OperationKind.None) (Syntax: 'X')
Right: IParameterReferenceExpression: z (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'z')
";
var expectedDiagnostics = DiagnosticDescription.None;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,113 +332,5 @@ class C

VerifyOperationTreeAndDiagnosticsForTest<EqualsValueClauseSyntax>(source, expectedOperationTree, expectedDiagnostics);
}

[Fact, WorkItem(17595, "https://github.com/dotnet/roslyn/issues/17595")]
public void MemberInitializerCSharp()
{
string source = @"
struct B
{
public bool Field;
}
class F
{
public int Field;
public string Property1 { set; get; }
public B Property2 { set; get; }
}
class C
{
public void M1()
/*<bind>*/{
var x1 = new F();
var x2 = new F() { Field = 2 };
var x3 = new F() { Property1 = """""""" };
var x4 = new F() { Property1 = """""""", Field = 2 };
var x5 = new F() { Property2 = new B { Field = true } };
var e1 = new F() { Property2 = 1 };
var e2 = new F() { """""""" };
}/*</bind>*/
}
";
string expectedOperationTree = @"
IBlockStatement (7 statements, 7 locals) (OperationKind.BlockStatement, IsInvalid) (Syntax: '{ ... }')
Locals: Local_1: F x1
Local_2: F x2
Local_3: F x3
Local_4: F x4
Local_5: F x5
Local_6: F e1
Local_7: F e2
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x1 = new F();')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x1 = new F();')
Variables: Local_1: F x1
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F()')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x2 = ne ... ield = 2 };')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x2 = ne ... ield = 2 };')
Variables: Local_1: F x2
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { Field = 2 }')
Member Initializers(1): IFieldInitializer (Field: System.Int32 F.Field) (OperationKind.FieldInitializerInCreation) (Syntax: 'Field = 2')
ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var x3 = ne ... 1 = """""""" };')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var x3 = ne ... 1 = """""""" };')
Variables: Local_1: F x3
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... y1 = """""""" }')
Member Initializers(1): IPropertyInitializer (Property: System.String F.Property1 { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Property1 = """"')
ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var x4 = ne ... ield = 2 };')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var x4 = ne ... ield = 2 };')
Variables: Local_1: F x4
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... Field = 2 }')
Member Initializers(2): IPropertyInitializer (Property: System.String F.Property1 { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Property1 = """"')
ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: """") (Syntax: '""""')
IFieldInitializer (Field: System.Int32 F.Field) (OperationKind.FieldInitializerInCreation) (Syntax: 'Field = 2')
ILiteralExpression (Text: 2) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 2) (Syntax: '2')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var x5 = ne ... = true } };')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var x5 = ne ... = true } };')
Variables: Local_1: F x5
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F) (Syntax: 'new F() { P ... = true } }')
Member Initializers(1): IPropertyInitializer (Property: B F.Property2 { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Property2 = ... ld = true }')
IObjectCreationExpression (Constructor: B..ctor()) (OperationKind.ObjectCreationExpression, Type: B) (Syntax: 'new B { Field = true }')
Member Initializers(1): IFieldInitializer (Field: System.Boolean B.Field) (OperationKind.FieldInitializerInCreation) (Syntax: 'Field = true')
ILiteralExpression (OperationKind.LiteralExpression, Type: System.Boolean, Constant: True) (Syntax: 'true')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var e1 = ne ... rty2 = 1 };')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var e1 = ne ... rty2 = 1 };')
Variables: Local_1: F e1
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { P ... erty2 = 1 }')
Member Initializers(1): IPropertyInitializer (Property: B F.Property2 { get; set; }) (OperationKind.PropertyInitializerInCreation, IsInvalid) (Syntax: 'Property2 = 1')
IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: B, IsInvalid) (Syntax: '1')
ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1')
IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement, IsInvalid) (Syntax: 'var e2 = ne ... ) { """""""" };')
IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration, IsInvalid) (Syntax: 'var e2 = ne ... ) { """""""" };')
Variables: Local_1: F e2
Initializer: IObjectCreationExpression (Constructor: F..ctor()) (OperationKind.ObjectCreationExpression, Type: F, IsInvalid) (Syntax: 'new F() { """""""" }')
";
var expectedDiagnostics = new DiagnosticDescription[] {
// CS1003: Syntax error, ',' expected
// var x3 = new F() { Property1 = """" };
Diagnostic(ErrorCode.ERR_SyntaxError, @"""""").WithArguments(",", "").WithLocation(20, 42),
// CS1003: Syntax error, ',' expected
// var x4 = new F() { Property1 = """", Field = 2 };
Diagnostic(ErrorCode.ERR_SyntaxError, @"""""").WithArguments(",", "").WithLocation(21, 42),
// CS1003: Syntax error, ',' expected
// var e2 = new F() { """" };
Diagnostic(ErrorCode.ERR_SyntaxError, @"""""").WithArguments(",", "").WithLocation(25, 30),
// CS0747: Invalid initializer member declarator
// var x3 = new F() { Property1 = """" };
Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, @"""""").WithLocation(20, 42),
// CS0747: Invalid initializer member declarator
// var x4 = new F() { Property1 = """", Field = 2 };
Diagnostic(ErrorCode.ERR_InvalidInitializerElementInitializer, @"""""").WithLocation(21, 42),
// CS0029: Cannot implicitly convert type 'int' to 'B'
// var e1 = new F() { Property2 = 1 };
Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "B").WithLocation(24, 40)
};

VerifyOperationTreeAndDiagnosticsForTest<BlockSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1522,11 +1522,11 @@ public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, T
/// </remarks>
internal sealed partial class ObjectCreationExpression : Operation, IHasArgumentsExpression, IObjectCreationExpression
{
public ObjectCreationExpression(IMethodSymbol constructor, ImmutableArray<ISymbolInitializer> memberInitializers, bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue) :
public ObjectCreationExpression(IMethodSymbol constructor, ImmutableArray<IOperation> initializers, bool isInvalid, SyntaxNode syntax, ITypeSymbol type, Optional<object> constantValue) :
base(OperationKind.ObjectCreationExpression, isInvalid, syntax, type, constantValue)
{
Constructor = constructor;
MemberInitializers = memberInitializers;
Initializers = initializers;
}
/// <summary>
/// Constructor to be invoked on the created instance.
Expand All @@ -1535,7 +1535,7 @@ public ObjectCreationExpression(IMethodSymbol constructor, ImmutableArray<ISymbo
/// <summary>
/// Explicitly-specified member initializers.
/// </summary>
public ImmutableArray<ISymbolInitializer> MemberInitializers { get; }
public ImmutableArray<IOperation> Initializers { get; }
public override void Accept(OperationVisitor visitor)
{
visitor.VisitObjectCreationExpression(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ public interface IObjectCreationExpression : IHasArgumentsExpression
/// </summary>
IMethodSymbol Constructor { get; }
/// <summary>
/// Explicitly-specified member initializers.
/// List of member or collection initializer expressions in the object initializer, if any.
/// </summary>
ImmutableArray<ISymbolInitializer> MemberInitializers { get; }
ImmutableArray<IOperation> Initializers { get; }
}
}

2 changes: 1 addition & 1 deletion src/Compilers/Core/Portable/Operations/OperationWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ public override void VisitAddressOfExpression(IAddressOfExpression operation)
public override void VisitObjectCreationExpression(IObjectCreationExpression operation)
{
VisitArray(operation.ArgumentsInEvaluationOrder);
VisitArray(operation.MemberInitializers);
VisitArray(operation.Initializers);
}

public override void VisitFieldInitializer(IFieldInitializer operation)
Expand Down
Loading

0 comments on commit dcd8fcb

Please sign in to comment.