-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Fix IOperation APIs for object and collection initializers #20689
Fix IOperation APIs for object and collection initializers #20689
Conversation
See dotnet#18781 (comment) for the final design. Fixes dotnet#18781
@@ -1329,6 +1329,10 @@ | |||
<Field Name="Expanded" Type="bool"/> | |||
<Field Name="ArgsToParamsOpt" Type="ImmutableArray<int>" Null="allow"/> | |||
<Field Name="ResultKind" PropertyOverrides="true" Type="LookupResultKind"/> | |||
|
|||
<!-- BinderOpt and UseSetterForDefaultArgumentGeneration are added as a temporary solution for IOperation implementation and should probably be removed in the future --> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is similar to the current approach used for BoundIndexerAccess
See https://github.com/mavasani/roslyn/blob/07421be2f2618cae7226438c06401fe0a40d7553/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml#L1505. @genlu for any context. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the context.
Do we have an issue tracking removing this in the future?
In reply to: 126044205 [](ancestors = 126044205)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #20787
Also tagging @dotnet/analyzer-ioperation for review #Resolved |
👍 #Resolved |
cc/ @dotnet/roslyn-compiler #Resolved |
Ping.. need compiler signoff on the PR. #Resolved |
{ | ||
case SymbolKind.Field: | ||
var field = (FieldSymbol)boundObjectInitializerMember.MemberSymbol; | ||
return new LazyFieldReferenceExpression(field, instance, field, isInvalid, syntax, type, constantValue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the second field
parameter necessary for LazyFieldReferenceExpression
? (Are the two parameter values ever different?) Same question for LazyEventReferenceExpression
and LazyPropertyReferenceExpression
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like it is redundant and there are more than one Operation types with such redundant parameters. I have added this to the list of comments to be addressed in the operation node generator: #20204 (comment)
Tagging @heejaechang as an FYI
return DeriveArguments(boundObjectInitializerMember.HasErrors, | ||
boundObjectInitializerMember.BinderOpt, | ||
property, | ||
property.SetMethod, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateBoundIndexerAccessOperation()
uses GetOwnOrInheritedSetMethod()
. Are there cases where that will give a different accessor here? If so, consider extracting a helper method that can be used by the factory methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, will change to use that method here as well.
boundMember.Type, | ||
hasErrors); | ||
binderOpt: this, | ||
useSetterForDefaultArgumentGeneration: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useSetterForDefaultArgumentGeneration [](start = 16, length = 37)
Is there any case where useSetterForDefaultArgumentGeneration
is false
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code path is while binding left of object member initializer, so it should always invoke the setter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm suggesting useSetterForDefaultArgumentGeneration
should not exist if it is always true
. I could not find code that could set it to false
, but we should double-check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, let me make that change.
@@ -237,7 +247,7 @@ private IInvocationExpression CreateBoundCallOperation(BoundCall boundCall) | |||
boundCall.ReceiverOpt != null && | |||
(boundCall.Method.IsVirtual || boundCall.Method.IsAbstract || boundCall.Method.IsOverride) && | |||
!boundCall.ReceiverOpt.SuppressVirtualCalls; | |||
Lazy<ImmutableArray<IArgument>> argumentsInEvaluationOrder = new Lazy<ImmutableArray<IArgument>>(() => DeriveArguments(boundCall, boundCall.BinderOpt, boundCall.Method, boundCall.Method, boundCall.Arguments, boundCall.ArgumentNamesOpt, boundCall.ArgsToParamsOpt, boundCall.ArgumentRefKindsOpt, boundCall.Method.Parameters, boundCall.Expanded, boundCall.Syntax, boundCall.InvokedAsExtensionMethod)); | |||
Lazy<ImmutableArray<IArgument>> argumentsInEvaluationOrder = new Lazy<ImmutableArray<IArgument>>(() => DeriveArguments(boundCall.HasErrors, boundCall.BinderOpt, boundCall.Method, boundCall.Method, boundCall.Arguments, boundCall.ArgumentNamesOpt, boundCall.ArgsToParamsOpt, boundCall.ArgumentRefKindsOpt, boundCall.Method.Parameters, boundCall.Expanded, boundCall.Syntax, boundCall.InvokedAsExtensionMethod)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Break line #Resolved
|
||
private IMemberReferenceExpression CreateBoundObjectInitializerMemberOperation(BoundObjectInitializerMember boundObjectInitializerMember) | ||
{ | ||
Lazy<IOperation> instance = new Lazy<IOperation>(() => new InstanceReferenceExpression(InstanceReferenceKind.Implicit, boundObjectInitializerMember.HasErrors, syntax: boundObjectInitializerMember.Syntax, type: boundObjectInitializerMember.MemberSymbol.ContainingType, constantValue: default(Optional<object>))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
break line
In general, we don't have a strict limit, but I'm confused why some code above (for example, call to DeriveArguments
) is on multiple lines (good, readable), but some other lines are crazy long...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add break lines for the couple of cases here. The formatting is not consistent across this file, so I have added a comment to #20204 (comment). Tagging @heejaechang as an FYI, we should address this in the operation node generator.
@@ -73,7 +73,7 @@ internal static IArgument CreateArgumentOperation(ArgumentKind kind, IParameterS | |||
// Note this check doesn't cover all scenarios. For example, when a parameter is a generic type but the type of the type argument | |||
// is undefined. | |||
if ((object)optionalParametersMethod == null | |||
|| boundNode.HasAnyErrors | |||
|| hasErrors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like callers are now passing boundNode.HasErrors
rather than boundNode.HasAnyErrors
. I'm not sure what the implications are but was that change intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change it to HasAnyErrors at all call sites, it was not an intentional change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requiring callers to pass HasAnyErrors
seems error prone. Is it possible to revert to passing in the BoundNode
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that seems better.
var expectedDiagnostics = new DiagnosticDescription[] { | ||
// CS1061: 'MyList<string>' does not contain a definition for 'Add' and no extension method 'Add' accepting a first argument of type 'MyList<string>' could be found (are you missing a using directive or an assembly reference?) | ||
// var coll = /*<bind>*/new MyList<string> { "str" }/*</bind>*/; | ||
Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"""str""").WithArguments("MyList<string>", "Add").WithLocation(7, 51) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The location was dropped from the comment, here and in other cases below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Operation unit test generator doesn't seem to include the diagnostic location in the generated comment for expected diagnostics. Filed #20788 to track updating the generator.
/// <summary> | ||
/// Add method invoked on collection. Might be null for dynamic invocation. | ||
/// </summary> | ||
IMethodSymbol AddMethod { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like AddMethod
is always null
for dynamic invocation. Consider changing comment from Might be null ...
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
End Function | ||
|
||
Private Function CreateBoundCollectionElementInitializerOperation(boundExpression As BoundExpression) As IOperation | ||
If (boundExpression.Kind <> BoundKind.Call) Then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parentheses are not needed.
|
||
public override void VisitCollectionElementInitializerExpression(ICollectionElementInitializerExpression operation) | ||
{ | ||
var addMethod = operation.AddMethod; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two unused locals #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The behavior of this override and a few above doesn't add anything to the base behavior. They can be removed.
In reply to: 126534989 [](ancestors = 126534989)
Left: IOperation: (OperationKind.None) (Syntax: 'Property2') | ||
Right: IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: B, IsInvalid) (Syntax: '1') | ||
ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') | ||
Initializer: IObjectOrCollectionInitializerExpression (OperationKind.ObjectOrCollectionInitializerExpression, Type: F, IsInvalid) (Syntax: '{ Property2 = 1 }') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I raised this question earlier, but I don't remember if it was answered. Do we know why some indents are 2 spaces, but others are 4? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some formatting will be fixed by Jinu's PR, but probably not this issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #20789 to track the indenting issues.
I believe @genlu wrote this walker as a checker that runs on any code and verifies no crashes on accessing properties off operation nodes. #Resolved |
LGTM |
…ctAndCollectionInitializer2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
windows_debug_unit32_prtest failed due to flaky test failure tracked by #14592 |
@dotnet-bot retest windows_debug_unit32_prtest please |
windows_debug_unit64_prtest failed due to flaky test #20370 |
@dotnet-bot retest windows_debug_unit64_prtest please |
@dotnet-bot retest windows_debug_vs-integration_prtest please |
@dotnet-bot retest windows_release_vs-integration_prtest please |
See #18781 (comment) for the final design.
Fixes #18781