ExpressionTreeToString icon indicating copy to clipboard operation
ExpressionTreeToString copied to clipboard

`ref` and `out` modifiers for C# and VB code formatters

Open zspitz opened this issue 6 years ago • 2 comments

For C#, ref and out are required in both the parameter declaration and the method call. I think we're already handling the parameter declaration, but if the method call expects a ref or out parameter, that modifier should be written.

For VB, out parameters ought to be decorated with the appropriate attribute in the parameter declaration. Calls don't require any special syntax.

zspitz avatar Sep 06 '19 06:09 zspitz

There are two areas of concern:

  • How do we render a call to a method which takes a ref or out parameter?
  • Action... and Func... don't have ref and out parameters. How do we render a LambdaExpression on a custom delegate type, which does have ref or out parameters?

There is a significant issue here. At least for the C# writer, when rendering a ParameterExpression that's an argument to a method call, that is where we have to decide whether to output ref or out. But at that point, we don't know what the method signature looks like, or even if we're within a method call.

The solution is to create a custom expression type for ref-rendered parameters, and wrap the original ParameterExpression. This expression type could be handled at the BuiltinWriterVisitor level.

See also #35, for a similar discussion WRT parameter vs parameter declaration, and metadata for block expression.

This would be a breaking change for any derived visitors (admittedly, I'm not sure if there are any...), so we're holding off on this for 4.0.

zspitz avatar Nov 02 '20 08:11 zspitz

Commenting out relevant tests in source, and storing the results here:

C# results:

---- VBCompiler.PassRef
(int i) => Dummy.DummyMethodWithRef(ref i)
---- VBCompiler.PassOut
(int i) => Dummy.DummyMethodWithOut(out i)
---- VBCompiler.PassRefField
(Dummy2 d) => Dummy.DummyMethodWithRef(ref d.Data)
---- VBCompiler.PassOutField
(Dummy2 d) => Dummy.DummyMethodWithOut(out d.Data)
---- VBCompiler.PassRefClosureVariable
() => Dummy.DummyMethodWithRef(ref i)
---- VBCompiler.PassOutClosureVariable
() => Dummy.DummyMethodWithOut(out i)
---- CSCompiler.PassRef
(int i) => Dummy.DummyMethodWithRef(ref i)
---- CSCompiler.PassOut
(int i) => Dummy.DummyMethodWithOut(out i)
---- CSCompiler.PassRefField
(Dummy2 d) => Dummy.DummyMethodWithRef(ref d.Data)
---- CSCompiler.PassOutField
(Dummy2 d) => Dummy.DummyMethodWithOut(out d.Data)
---- CSCompiler.PassRefClosureVariable
() => Dummy.DummyMethodWithRef(ref i)
---- CSCompiler.PassOutClosureVariable
() => Dummy.DummyMethodWithOut(out i)
---- FactoryMethods.OutParameter
(ref int $var0) => $var0
---- FactoryMethods.RefParameter
(ref int $var0) => $var0
---- FactoryMethods.PassRef
Dummy.DummyMethodWithRef(ref $var0)
---- FactoryMethods.PassOut
Dummy.DummyMethodWithOut(out $var0)
---- FactoryMethods.PassRefField
Dummy.DummyMethodWithRef(ref #Dummy2.Data)
---- FactoryMethods.PassOutField
Dummy.DummyMethodWithOut(out #Dummy2.Data)

and VB results. As noted, VB doesn't require ByRef at the call site, so only those test results are included here:

---- FactoryMethods.OutParameter
Function(ByRef $var0 As Integer) $var0
---- FactoryMethods.RefParameter
Function(ByRef $var0 As Integer) $var0

zspitz avatar Nov 02 '20 10:11 zspitz