Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle unspecified anonymous type property in VisualBasicOperationFactory #74091

Merged
merged 4 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Handle unspecified anonymous type property in VisualBasicOperationFac…
…tory
  • Loading branch information
cston committed Jun 20, 2024
commit 6723d453491d707e48efcc35341c0a4d21e9d432
Original file line number Diff line number Diff line change
Expand Up @@ -1602,8 +1602,12 @@ Namespace Microsoft.CodeAnalysis.Operations
Return New AnonymousObjectCreationOperation(initializers, _semanticModel, syntax, type, isImplicit)
End Function

Private Function CreateBoundAnonymousTypePropertyAccessOperation(boundAnonymousTypePropertyAccess As BoundAnonymousTypePropertyAccess) As IPropertyReferenceOperation
Private Function CreateBoundAnonymousTypePropertyAccessOperation(boundAnonymousTypePropertyAccess As BoundAnonymousTypePropertyAccess) As IOperation
Dim [property] As IPropertySymbol = DirectCast(boundAnonymousTypePropertyAccess.ExpressionSymbol, IPropertySymbol)
Return CreateBoundAnonymousTypePropertyAccessOperation(boundAnonymousTypePropertyAccess, [property])
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return CreateBoundAnonymousTypePropertyAccessOperation(boundAnonymousTypePropertyAccess, [property])

Alternatively, consider creating InvalidOperation when [property] Is Nothing and reverting changes in GetAnonymousTypeCreationInitializers. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that initially. However, TestOperationVisitor.VisitAnonymousObjectCreation() expects the initializer target to be an IPropertyReferenceOperation, and I assumed there might be consumers other than test code that also expect the same.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should simply change the assertion in TestOperationVisitor.VisitAnonymousObjectCreation, what it is doing is mostly driven by implementation. When we are fixing bugs, we are changing shape of IOperation tree even for success scenarios, not to mention error scenarios. The binder says, it doesn't know what the property is (that is what the bound node returns) and I think we should stick with that rather than trying to re-guess what that property might be. Otherwise, we probably should fix what the bound node returns, so that SemanticModel and IOperation tree produce consistent result.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Based on our offline discussion, I've left this as is, and added a check of the SemanticModel to the tests.

End Function

Private Function CreateBoundAnonymousTypePropertyAccessOperation(boundAnonymousTypePropertyAccess As BoundAnonymousTypePropertyAccess, [property] As IPropertySymbol) As IOperation
Dim instance As IOperation = CreateAnonymousTypePropertyAccessImplicitReceiverOperation([property], boundAnonymousTypePropertyAccess.Syntax.FirstAncestorOrSelf(Of AnonymousObjectCreationExpressionSyntax))
Dim arguments = ImmutableArray(Of IArgumentOperation).Empty
Dim syntax As SyntaxNode = boundAnonymousTypePropertyAccess.Syntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ Namespace Microsoft.CodeAnalysis.Operations
Dim builder = ArrayBuilder(Of IOperation).GetInstance(expression.Arguments.Length)
Dim currentDeclarationIndex = 0
For i As Integer = 0 To expression.Arguments.Length - 1
Dim [property] As IPropertySymbol = properties(i)
Dim value As IOperation = Create(expression.Arguments(i))

Dim target As IOperation
Expand All @@ -326,7 +327,6 @@ Namespace Microsoft.CodeAnalysis.Operations
If currentDeclarationIndex >= expression.Declarations.Length OrElse
i <> expression.Declarations(currentDeclarationIndex).PropertyIndex Then
' No matching declaration, synthesize a property reference with an implicit receiver to be assigned.
Dim [property] As IPropertySymbol = properties(i)
Dim instance As IInstanceReferenceOperation = CreateAnonymousTypePropertyAccessImplicitReceiverOperation([property], expression.Syntax)
target = New PropertyReferenceOperation(
[property], constrainedToType:=Nothing,
Expand All @@ -339,7 +339,7 @@ Namespace Microsoft.CodeAnalysis.Operations
isImplicitAssignment = True
Else
Debug.Assert(i = expression.Declarations(currentDeclarationIndex).PropertyIndex)
target = CreateBoundAnonymousTypePropertyAccessOperation(expression.Declarations(currentDeclarationIndex))
target = CreateBoundAnonymousTypePropertyAccessOperation(expression.Declarations(currentDeclarationIndex), [property])
currentDeclarationIndex = currentDeclarationIndex + 1
isImplicitAssignment = expression.WasCompilerGenerated
End If
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1821,5 +1821,189 @@ Block[B3] - Exit

VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics)
End Sub

<Fact()>
Public Sub ObjectCreation_WithMissingMember()
Dim source = <![CDATA[
Module Program
Sub Main() 'BIND:"Sub Main()"
Dim item = New C() With {.}
End Sub
End Module
Class C
End Class
]]>.Value
Dim expectedDiagnostics = <![CDATA[
BC30201: Expression expected.
Dim item = New C() With {.}
~
BC30203: Identifier expected.
Dim item = New C() With {.}
~
BC30984: '=' expected (object initializer).
Dim item = New C() With {.}
~
]]>.Value
Dim expectedFlowGraph = <![CDATA[
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expectedFlowGraph

Please assert original IOperation tree for all added tests #Closed

Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
Locals: [item As C]
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (3)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'New C() With {.}')
Value:
IObjectCreationOperation (Constructor: Sub C..ctor()) (OperationKind.ObjectCreation, Type: C, IsInvalid) (Syntax: 'New C() With {.}')
Arguments(0)
Initializer:
null
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: '.')
Left:
IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid, IsImplicit) (Syntax: '.')
Children(0)
Right:
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '')
Children(0)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C, IsInvalid, IsImplicit) (Syntax: 'item = New C() With {.}')
Left:
ILocalReferenceOperation: item (IsDeclaration: True) (OperationKind.LocalReference, Type: C, IsImplicit) (Syntax: 'item')
Right:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsInvalid, IsImplicit) (Syntax: 'New C() With {.}')
Next (Regular) Block[B2]
Leaving: {R1}
}
Block[B2] - Exit
Predecessors: [B1]
Statements (0)
]]>.Value
VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics)
End Sub

<Fact()>
Public Sub AnonymousObjectCreation_WithMissingMember_01()
Dim source = <![CDATA[
Module Program
Sub Main() 'BIND:"Sub Main()"
Dim item = New With {.}
End Sub
End Module
]]>.Value
Dim expectedDiagnostics = <![CDATA[
BC30201: Expression expected.
Dim item = New With {.}
~
BC30203: Identifier expected.
Dim item = New With {.}
~
BC30984: '=' expected (object initializer).
Dim item = New With {.}
~
]]>.Value
Dim expectedFlowGraph = <![CDATA[
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
Locals: [item As <anonymous type: $0 As ?>]
CaptureIds: [0]
Block[B1] - Block
Predecessors: [B0]
Statements (2)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '')
Value:
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '')
Children(0)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: <anonymous type: $0 As ?>, IsInvalid, IsImplicit) (Syntax: 'item = New With {.}')
Left:
ILocalReferenceOperation: item (IsDeclaration: True) (OperationKind.LocalReference, Type: <anonymous type: $0 As ?>, IsImplicit) (Syntax: 'item')
Right:
IAnonymousObjectCreationOperation (OperationKind.AnonymousObjectCreation, Type: <anonymous type: $0 As ?>, IsInvalid) (Syntax: 'New With {.}')
Initializers(1):
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: '.')
Left:
IPropertyReferenceOperation: Property <anonymous type: $0 As ?>.$0 As ? (OperationKind.PropertyReference, Type: ?, IsInvalid) (Syntax: '')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: <anonymous type: $0 As ?>, IsInvalid, IsImplicit) (Syntax: 'New With {.}')
Right:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: null, IsInvalid, IsImplicit) (Syntax: '')
Next (Regular) Block[B2]
Leaving: {R1}
}
Block[B2] - Exit
Predecessors: [B1]
Statements (0)
]]>.Value
VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics)
End Sub

<Fact()>
Public Sub AnonymousObjectCreation_WithMissingMember_02()
Dim source = <![CDATA[
Module Program
Sub Main() 'BIND:"Sub Main()"
Dim item = New With {.a = 1, .b = .}
End Sub
End Module
]]>.Value
Dim expectedDiagnostics = <![CDATA[
BC30203: Identifier expected.
Dim item = New With {.a = 1, .b = .}
~
]]>.Value
Dim expectedFlowGraph = <![CDATA[
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
Locals: [item As <anonymous type: a As System.Int32, b As ?>]
CaptureIds: [0] [1]
Block[B1] - Block
Predecessors: [B0]
Statements (3)
IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '1')
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.')
Value:
IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: '.')
Children(0)
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: <anonymous type: a As System.Int32, b As ?>, IsInvalid, IsImplicit) (Syntax: 'item = New ... 1, .b = .}')
Left:
ILocalReferenceOperation: item (IsDeclaration: True) (OperationKind.LocalReference, Type: <anonymous type: a As System.Int32, b As ?>, IsImplicit) (Syntax: 'item')
Right:
IAnonymousObjectCreationOperation (OperationKind.AnonymousObjectCreation, Type: <anonymous type: a As System.Int32, b As ?>, IsInvalid) (Syntax: 'New With {. ... 1, .b = .}')
Initializers(2):
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, Constant: 1) (Syntax: '.a = 1')
Left:
IPropertyReferenceOperation: Property <anonymous type: a As System.Int32, b As ?>.a As System.Int32 (OperationKind.PropertyReference, Type: System.Int32) (Syntax: 'a')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: <anonymous type: a As System.Int32, b As ?>, IsInvalid, IsImplicit) (Syntax: 'New With {. ... 1, .b = .}')
Right:
IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: '1')
ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: '.b = .')
Left:
IPropertyReferenceOperation: Property <anonymous type: a As System.Int32, b As ?>.b As ? (OperationKind.PropertyReference, Type: ?) (Syntax: 'b')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: <anonymous type: a As System.Int32, b As ?>, IsInvalid, IsImplicit) (Syntax: 'New With {. ... 1, .b = .}')
Right:
IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: ?, IsInvalid, IsImplicit) (Syntax: '.')
Next (Regular) Block[B2]
Leaving: {R1}
}
Block[B2] - Exit
Predecessors: [B1]
Statements (0)
]]>.Value
VerifyFlowGraphAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedFlowGraph, expectedDiagnostics)
End Sub
End Class
End Namespace