Skip to content

Commit 257aae9

Browse files
Merge pull request #1116 from TymurGubayev/fix/RefReturn/1
Handle C# `ref return` when converting VB->C#
2 parents b8c386d + 4a85f8b commit 257aae9

30 files changed

Lines changed: 2065 additions & 7 deletions

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,7 +1884,9 @@ private RefConversion NeedsVariableForArgument(VBasic.Syntax.ArgumentSyntax node
18841884
RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)
18851885
{
18861886
var symbolInfo = GetSymbolInfoInDocument<ISymbol>(expression);
1887-
if (symbolInfo is IPropertySymbol propertySymbol) {
1887+
if (symbolInfo is IPropertySymbol propertySymbol
1888+
// a property in VB.NET code can be ReturnsByRef if it's defined in a C# assembly the VB.NET code references
1889+
&& !propertySymbol.ReturnsByRef && !propertySymbol.ReturnsByRefReadonly) {
18881890
return propertySymbol.IsReadOnly ? RefConversion.PreAssigment : RefConversion.PreAndPostAssignment;
18891891
}
18901892
else if (symbolInfo is IFieldSymbol { IsConst: true } or ILocalSymbol { IsConst: true }) {
@@ -1912,7 +1914,16 @@ RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)
19121914
bool IsRefArrayAcces(VBSyntax.ExpressionSyntax expression)
19131915
{
19141916
if (!(expression is VBSyntax.InvocationExpressionSyntax ies)) return false;
1915-
return _semanticModel.GetOperation(ies).IsArrayElementAccess() && GetRefConversion(ies.Expression) == RefConversion.Inline;
1917+
var op = _semanticModel.GetOperation(ies);
1918+
return (op.IsArrayElementAccess() || IsReturnsByRefPropertyElementAccess(op))
1919+
&& GetRefConversion(ies.Expression) == RefConversion.Inline;
1920+
1921+
static bool IsReturnsByRefPropertyElementAccess(IOperation op)
1922+
{
1923+
return op.IsPropertyElementAccess()
1924+
&& op is IPropertyReferenceOperation { Property: { } prop }
1925+
&& (prop.ReturnsByRef || prop.ReturnsByRefReadonly);
1926+
}
19161927
}
19171928
}
19181929

CodeConverter/CSharp/OperationExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ public static bool IsAssignableExpression(this IOperation operation)
5656
case OperationKind.DynamicMemberReference:
5757
return true;
5858

59-
//Just documenting since it's the only one mentioning reference that can't necessarily be assigned to AFAIK
6059
case OperationKind.PropertyReference:
61-
return false;
60+
//a property might be RefReturn, if it's defined in a referenced C# assembly
61+
var prop = ((IPropertyReferenceOperation)operation).Property;
62+
return prop.ReturnsByRef || prop.ReturnsByRefReadonly;
6263
}
6364

6465
return false;

Tests/CSharp/MultiFileSolutionAndProjectTests.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,22 @@ public MultiFileSolutionAndProjectTests(MultiFileTestFixture multiFileTestFixtur
1818
_multiFileTestFixture = multiFileTestFixture;
1919
}
2020

21-
[Fact] /* enable for executing locally */
21+
[Fact]
2222
public async Task ConvertWholeSolutionAsync()
2323
{
2424

2525
await _multiFileTestFixture.ConvertProjectsWhereAsync(p => true, Language.CS);
2626
}
2727

28-
[Fact] /* enable for executing locally */
28+
[Fact]
2929
public async Task ConvertVbLibraryOnlyAsync()
3030
{
3131
await _multiFileTestFixture.ConvertProjectsWhereAsync(p => p.Name == "VbLibrary", Language.CS);
3232
}
33+
34+
[Fact(Skip= "Roslyn bugs mean we can't run this test: https://github.com/icsharpcode/CodeConverter/pull/1116#issuecomment-2242645546")]
35+
public async Task ConvertVbUsingCSharpRefReturnOnlyAsync()
36+
{
37+
await _multiFileTestFixture.ConvertProjectsWhereAsync(p => p.Name == "VisualBasicUsingCSharpRefReturn", Language.CS);
38+
}
3339
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
4+
<PropertyGroup>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
6+
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
7+
<ProjectGuid>{8B843547-F49D-40A2-8C4E-1B81D8C5D589}</ProjectGuid>
8+
<OutputType>Library</OutputType>
9+
<AppDesignerFolder>Properties</AppDesignerFolder>
10+
<RootNamespace>CSharpRefReturn</RootNamespace>
11+
<AssemblyName>CSharpRefReturn</AssemblyName>
12+
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
13+
<FileAlignment>512</FileAlignment>
14+
<Deterministic>true</Deterministic>
15+
</PropertyGroup>
16+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
17+
<DebugSymbols>true</DebugSymbols>
18+
<DebugType>full</DebugType>
19+
<Optimize>false</Optimize>
20+
<OutputPath>bin\Debug\</OutputPath>
21+
<DefineConstants>DEBUG;TRACE</DefineConstants>
22+
<ErrorReport>prompt</ErrorReport>
23+
<WarningLevel>4</WarningLevel>
24+
</PropertyGroup>
25+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
26+
<DebugType>pdbonly</DebugType>
27+
<Optimize>true</Optimize>
28+
<OutputPath>bin\Release\</OutputPath>
29+
<DefineConstants>TRACE</DefineConstants>
30+
<ErrorReport>prompt</ErrorReport>
31+
<WarningLevel>4</WarningLevel>
32+
</PropertyGroup>
33+
<ItemGroup>
34+
<Reference Include="System" />
35+
<Reference Include="System.Core" />
36+
<Reference Include="System.Xml.Linq" />
37+
<Reference Include="System.Data.DataSetExtensions" />
38+
<Reference Include="Microsoft.CSharp" />
39+
<Reference Include="System.Data" />
40+
<Reference Include="System.Net.Http" />
41+
<Reference Include="System.Xml" />
42+
</ItemGroup>
43+
<ItemGroup>
44+
<Compile Include="RefReturnList.cs" />
45+
<Compile Include="Properties\AssemblyInfo.cs" />
46+
</ItemGroup>
47+
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
48+
</Project>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("CSharpRefReturn")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("CSharpRefReturn")]
13+
[assembly: AssemblyCopyright("Copyright © 2024")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("8b843547-f49d-40a2-8c4e-1b81d8c5d589")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace CSharpRefReturn
8+
{
9+
public class RefReturnList<T>
10+
{
11+
private T dummy;
12+
public ref T this[int i] {
13+
get {
14+
return ref dummy;
15+
}
16+
}
17+
18+
public ref T RefProperty
19+
{
20+
get
21+
{
22+
return ref dummy;
23+
}
24+
}
25+
}
26+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Public Class ByRefArgument
2+
Sub UseArr()
3+
Dim arrObj() As Object
4+
Modify(arrObj(0))
5+
6+
Dim arrInt() As Integer
7+
Modify(arrInt(0))
8+
End Sub
9+
10+
Sub UseRefReturn()
11+
Dim lstObj As CSharpRefReturn.RefReturnList(Of Object)
12+
Modify(lstObj(0))
13+
Modify(lstObj.RefProperty)
14+
15+
Dim lstInt As CSharpRefReturn.RefReturnList(Of Integer)
16+
Modify(lstInt(0))
17+
Modify(lstInt.RefProperty)
18+
End Sub
19+
20+
Sub Modify(ByRef o As Object)
21+
End Sub
22+
End Class

Tests/TestData/MultiFileCharacterization/SourceFiles/VisualBasicUsesCSharpRefReturn/My Project/Application.Designer.vb

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3+
<MySubMain>false</MySubMain>
4+
<SingleInstance>false</SingleInstance>
5+
<ShutdownMode>0</ShutdownMode>
6+
<EnableVisualStyles>true</EnableVisualStyles>
7+
<AuthenticationMode>0</AuthenticationMode>
8+
<ApplicationType>1</ApplicationType>
9+
<SaveMySettingsOnExit>true</SaveMySettingsOnExit>
10+
</MyApplicationData>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Imports System
2+
Imports System.Reflection
3+
Imports System.Runtime.InteropServices
4+
5+
' General Information about an assembly is controlled through the following
6+
' set of attributes. Change these attribute values to modify the information
7+
' associated with an assembly.
8+
9+
' Review the values of the assembly attributes
10+
11+
<Assembly: AssemblyTitle("VisualBasicUsesCSharpRefReturn")>
12+
<Assembly: AssemblyDescription("")>
13+
<Assembly: AssemblyCompany("")>
14+
<Assembly: AssemblyProduct("VisualBasicUsesCSharpRefReturn")>
15+
<Assembly: AssemblyCopyright("Copyright © 2024")>
16+
<Assembly: AssemblyTrademark("")>
17+
18+
<Assembly: ComVisible(False)>
19+
20+
'The following GUID is for the ID of the typelib if this project is exposed to COM
21+
<Assembly: Guid("47520bc8-6837-4f63-9aef-0a252d368f05")>
22+
23+
' Version information for an assembly consists of the following four values:
24+
'
25+
' Major Version
26+
' Minor Version
27+
' Build Number
28+
' Revision
29+
'
30+
' You can specify all the values or you can default the Build and Revision Numbers
31+
' by using the '*' as shown below:
32+
' <Assembly: AssemblyVersion("1.0.*")>
33+
34+
<Assembly: AssemblyVersion("1.0.0.0")>
35+
<Assembly: AssemblyFileVersion("1.0.0.0")>

0 commit comments

Comments
 (0)