From 2e4d146eddf35a190f1ad664db010d17c33406f4 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 3 Jul 2019 10:42:29 -0700 Subject: [PATCH] Adjust IAnonymousPropertyOperation to be resilient to nullability changes. --- .../CSharpOperationFactory_Methods.cs | 29 +++++++++++-- ...Tests_IAnonymousObjectCreationOperation.cs | 41 +++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs index fc72dd6cb57..afaad117aad 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory_Methods.cs @@ -395,9 +395,9 @@ internal static ImmutableArray CreateInvalidChildrenFromArgumentsExpr isImplicit: true); // Find matching declaration for the current argument. - IPropertySymbol property = AnonymousTypeManager.GetAnonymousTypeProperty((NamedTypeSymbol)type, i); - if (currentDeclarationIndex >= declarations.Length || - (object)property != declarations[currentDeclarationIndex].Property) + PropertySymbol property = AnonymousTypeManager.GetAnonymousTypeProperty((NamedTypeSymbol)type, i); + BoundAnonymousPropertyDeclaration anonymousProperty = getDeclaration(declarations, property, ref currentDeclarationIndex); + if (anonymousProperty is null) { // No matching declaration, synthesize a property reference to be assigned. target = new PropertyReferenceOperation( @@ -413,7 +413,6 @@ internal static ImmutableArray CreateInvalidChildrenFromArgumentsExpr } else { - BoundAnonymousPropertyDeclaration anonymousProperty = declarations[currentDeclarationIndex++]; target = new PropertyReferenceOperation(anonymousProperty.Property, instance, ImmutableArray.Empty, @@ -435,6 +434,28 @@ internal static ImmutableArray CreateInvalidChildrenFromArgumentsExpr Debug.Assert(currentDeclarationIndex == declarations.Length); return builder.ToImmutableAndFree(); + + static BoundAnonymousPropertyDeclaration getDeclaration(ImmutableArray declarations, PropertySymbol currentProperty, ref int currentDeclarationIndex) + { + if (currentDeclarationIndex >= declarations.Length) + { + return null; + } + + var currentDeclaration = declarations[currentDeclarationIndex]; + + // https://github.com/dotnet/roslyn/issues/35044: This works for simple success cases, but does not work for failures. Likely will have to do something more complicated here involving rebinding the + // declarators based on the newly constructed anonymous type symbol above and matching them to the existing symbol + if ((object)currentProperty == currentDeclaration.Property || + (currentDeclaration.Property.Name == currentProperty.Name && + currentDeclaration.Property.Type.Equals(currentProperty.Type, TypeCompareKind.ConsiderEverything | TypeCompareKind.AllNullableIgnoreOptions))) + { + currentDeclarationIndex++; + return currentDeclaration; + } + + return null; + } } internal class Helper diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAnonymousObjectCreationOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAnonymousObjectCreationOperation.cs index 9502ef748cd..00f93ad585a 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAnonymousObjectCreationOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IAnonymousObjectCreationOperation.cs @@ -1639,5 +1639,46 @@ void M(object p, int? i1, int i2, int? j1, int j2) VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } + + [Fact] + public void AnonymousObjectCreation_NullableEnabled_PropertyMatches() + { + var source = @" +#nullable enable +class C +{ + void M(int i1, object o1) + { + var obj = /**/new { a = i1, o1, b = ""Hello world!"" }/**/; + } +}"; + + var expectedOperationTree = @" +IAnonymousObjectCreationOperation (OperationKind.AnonymousObjectCreation, Type: ) (Syntax: 'new { a = i ... o world!"" }') + Initializers(3): + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32) (Syntax: 'a = i1') + Left: + IPropertyReferenceOperation: System.Int32 .a { get; } (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'a') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: , IsImplicit) (Syntax: 'new { a = i ... o world!"" }') + Right: + IParameterReferenceOperation: i1 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'i1') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Object, IsImplicit) (Syntax: 'o1') + Left: + IPropertyReferenceOperation: System.Object .o1 { get; } (OperationKind.PropertyReference, Type: System.Object, IsImplicit) (Syntax: 'o1') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: , IsImplicit) (Syntax: 'new { a = i ... o world!"" }') + Right: + IParameterReferenceOperation: o1 (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o1') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String, Constant: ""Hello world!"") (Syntax: 'b = ""Hello world!""') + Left: + IPropertyReferenceOperation: System.String .b { get; } (OperationKind.PropertyReference, Type: System.String) (Syntax: 'b') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: , IsImplicit) (Syntax: 'new { a = i ... o world!"" }') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: ""Hello world!"") (Syntax: '""Hello world!""')"; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics: DiagnosticDescription.None); + } } } -- GitLab