Open bkoelman opened 6 years ago
Tagging @AlekseyTs for triage
I do not think IArgumentOperation node is used for dynamic invocations. This is purely early bound node with information that cannot be provided for dynamic/late bound operations. Looks by design to me.
I found a case where the argument to a dynamic invocation is actually visited. To repro, replace the source text with:
namespace N
{
class C
{
delegate int F(int x, params object[] args);
private F f;
void M(dynamic d)
{
d.X(1, 2, x: f(0));
N(4, 5, 6);
}
void N(int i, int j, int k) => throw null;
}
}
and rerun, which outputs:
0
x: f(0)
4
5
6
I found a case where the argument to a dynamic invocation is actually visited.
Please make sure to examine the actual IOperation tree you are dealing with. As far as I can see, none of the immediate children of IDynamicInvocationOperation
node are IArgumentOperation
s, there are two IArgumentOperation
involved into delegate invocation f(0)
, but this is not a dynamic invocation.
[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void Issue27676()
{
string source = @"
namespace N
{
class C
{
delegate int F(int x, params object[] args);
private F f;
void M(dynamic d)
{
/*<bind>*/d.X(1, 2, x: f(0))/*</bind>*/;
N(4, 5, 6);
}
void N(int i, int j, int k) => throw null;
}
}
";
string expectedOperationTree = @"
IDynamicInvocationOperation (OperationKind.DynamicInvocation, Type: dynamic) (Syntax: 'd.X(1, 2, x: f(0))')
Expression:
IDynamicMemberReferenceOperation (Member Name: ""X"", Containing Type: null) (OperationKind.DynamicMemberReference, Type: dynamic) (Syntax: 'd.X')
Type Arguments(0)
Instance Receiver:
IParameterReferenceOperation: d (OperationKind.ParameterReference, Type: dynamic) (Syntax: 'd')
Arguments(3):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
IInvocationOperation (virtual System.Int32 N.C.F.Invoke(System.Int32 x, params System.Object[] args)) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'f(0)')
Instance Receiver:
IFieldReferenceOperation: N.C.F N.C.f (OperationKind.FieldReference, Type: N.C.F) (Syntax: 'f')
Instance Receiver:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: N.C, IsImplicit) (Syntax: 'f')
Arguments(2):
IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: x) (OperationKind.Argument, Type: null) (Syntax: '0')
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
IArgumentOperation (ArgumentKind.ParamArray, Matching Parameter: args) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'x: f(0)')
IArrayCreationOperation (OperationKind.ArrayCreation, Type: System.Object[], IsImplicit) (Syntax: 'f(0)')
Dimension Sizes(1):
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsImplicit) (Syntax: 'f(0)')
Initializer:
IArrayInitializerOperation (0 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: 'f(0)')
Element Values(0)
InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null)
ArgumentNames(3):
""null""
""null""
""x""
ArgumentRefKinds(0)
";
var expectedDiagnostics = new[] {
// file.cs(8,19): warning CS0649: Field 'C.f' is never assigned to, and will always have its default value null
// private F f;
Diagnostic(ErrorCode.WRN_UnassignedInternalField, "f").WithArguments("N.C.f", "null").WithLocation(8, 19)
};
VerifyOperationTreeAndDiagnosticsForTest<InvocationExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics);
}
Aha, I see now. Another compiler-generated operation with an unexpected syntax location. Well, the chosen location makes sense, given that all operations must have backing syntax. Just not having a location for compiler-generated code feels more appropriate to be as an outsider. But there are probably reasons I am unaware of to have the current design, so lets forget about that.
Just out of curiosity: Is there a way to produce that operation dump as a Microsoft.CodeAnalysis NuGet consumer (not being at work inside the roslyn codebase)?
Getting back to the original issue: I understand that IArgumentOperation.Parameter
and the In/Out conversions make no sense for dynamic invocations. So lets change this issue into a request to add operation support for visiting dynamic arguments then.
Version Used: NuGet Microsoft.CodeAnalysis v2.8.2
Steps to Reproduce: Consider the next program, which prints visited arguments:
Expected Behavior: Output 1, 2, 3, 4, 5, 6
Actual Behavior: Output 4, 5, 6