Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
18f8528
Fix CS0206 when converting ReDim Preserve on a property
google-labs-jules[bot] Feb 27, 2026
1b5ac8e
Fix CS0206 when converting ReDim Preserve on a property
google-labs-jules[bot] Feb 28, 2026
dfe5298
Fix CS0206 when converting ReDim Preserve on a property
google-labs-jules[bot] Feb 28, 2026
0532ed5
Fix VB With block conversion with null-conditional operator
google-labs-jules[bot] Feb 27, 2026
83db1fd
Simplify
GrahamTheCoder Feb 28, 2026
63806cf
Less cyclomatic complexity
GrahamTheCoder Feb 28, 2026
cace870
Merge pull request #22 from GrahamTheCoder/fix-redim-preserve-propert…
GrahamTheCoder Feb 28, 2026
01a4ddb
Merge pull request #21 from GrahamTheCoder/fix-1174-vb-with-block-nul…
GrahamTheCoder Feb 28, 2026
579b74d
Merge remote-tracking branch 'origin/master'
GrahamTheCoder Feb 28, 2026
ef65cf8
New slnx format uses forward slash
GrahamTheCoder Feb 28, 2026
d675467
Try to force dotnet 8 for the vs2022 test run
GrahamTheCoder Feb 28, 2026
0fb3e58
v10.0.1 + changelog
GrahamTheCoder Feb 28, 2026
abcf0fd
Append framework
GrahamTheCoder Feb 28, 2026
824022d
Build with net10 for slnx support, then force net8 after that
GrahamTheCoder Feb 28, 2026
cf9510f
Get rid of build error in visual studio
GrahamTheCoder Feb 28, 2026
9197f23
Up one more directory
GrahamTheCoder Feb 28, 2026
fa3136f
The test projects won't restore under dotnet 10
GrahamTheCoder Mar 1, 2026
dc3f903
Consistent order
GrahamTheCoder Mar 1, 2026
df7b89e
Split script out and try to take care over messing with ending newlines
GrahamTheCoder Mar 1, 2026
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
57 changes: 57 additions & 0 deletions .github/scripts/update-testdata-targetframework.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
param()

$ErrorActionPreference = 'Stop'

Write-Host "Creating global.json to enforce .NET 8 for MSBuild"
$globalJson = '{"sdk":{"version":"8.0.0","rollForward":"latestFeature"}}'
[System.IO.File]::WriteAllText('global.json', $globalJson, [System.Text.Encoding]::UTF8)

Write-Host "Searching for project files under Tests/TestData..."
$projFiles = Get-ChildItem -Path Tests/TestData -Recurse -Include *.csproj,*.vbproj,*.fsproj -File -ErrorAction SilentlyContinue

if (-not $projFiles) {
Write-Host "No project files found under Tests/TestData"
exit 0
}

$changed = $false
foreach ($f in $projFiles) {
$path = $f.FullName
Write-Host "Processing: $path"

# Use StreamReader to detect encoding and preserve it when writing back
$sr = [System.IO.StreamReader]::new($path, $true)
try {
$content = $sr.ReadToEnd()
$encoding = $sr.CurrentEncoding
} finally {
$sr.Close()
}

# Replace net10.0 and net10.0-windows with net8.0 / net8.0-windows
$updated = [System.Text.RegularExpressions.Regex]::Replace($content, '<TargetFramework>net10\.0(-windows)?</TargetFramework>', '<TargetFramework>net8.0$1</TargetFramework>')

if ($updated -ne $content) {
Write-Host "Updating TargetFramework in: $path"
# Write back preserving detected encoding and internal newlines
[System.IO.File]::WriteAllText($path, $updated, $encoding)
$changed = $true
}
}

if ($changed) {
Write-Host "Changes detected — committing to local repo so working tree is clean for tests"
git config user.name "github-actions[bot]"
if ($env:GITHUB_ACTOR) {
git config user.email "$($env:GITHUB_ACTOR)@users.noreply.github.com"
} else {
git config user.email "actions@github.com"
}
git add -A
git commit -m "CI: Update Tests/TestData TargetFramework -> net8.0 for .NET 8 run" || Write-Host "No commit created (maybe no staged changes)"
Write-Host "Committed changes locally."
} else {
Write-Host "No TargetFramework updates required."
}

Write-Host "Done."
15 changes: 11 additions & 4 deletions .github/workflows/dotnet-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ jobs:

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
Expand All @@ -30,11 +29,19 @@ jobs:
with:
vs-version: ${{ inputs.vs-version }}

- name: Build
run: dotnet build DotNetBuildable.slnf /p:Configuration=Release

- name: Create global.json to enforce .NET 8 for MSBuild and update Tests/TestData projects
if: inputs.dotnet-version == '8.0.x'
shell: pwsh
run: ./.github/scripts/update-testdata-targetframework.ps1

- name: Log MSBuild version
run: msbuild -version

- name: Build
run: dotnet build DotNetBuildable.slnf /p:Configuration=Release
- name: Log .NET version
run: dotnet --info

- name: Execute unit tests
run: dotnet test Tests/bin/Release/ICSharpCode.CodeConverter.Tests.dll
run: dotnet test Tests/Tests.csproj -c Release --framework net${{ inputs.dotnet-version == '8.0.x' && '8.0' || '10.0' }} --no-build
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
branches: [ master, main ]

env:
BuildVersion: '10.0.0'
BuildVersion: '10.0.1'

jobs:
build:
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### C# -> VB


## [10.0.1] - 2026-02-28

* Reintroduce tentative legacy support for dotnet 8 and VS2022
* Support slnx format [1195](https://github.com/icsharpcode/CodeConverter/issues/1195)

### VB -> C#
* Fix for ReDim Preserve of array property - [#1156](https://github.com/icsharpcode/CodeConverter/issues/1156)
* Fix for with block conversion with null conditional [#1174](https://github.com/icsharpcode/CodeConverter/issues/1174)
Fixes #1195


## [10.0.0] - 2026-02-06

* Support for net framework dropped. Please use an older version if you are converting projects that still use it.
Expand Down
12 changes: 11 additions & 1 deletion CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,18 @@ private async Task<SyntaxList<StatementSyntax>> ConvertRedimClauseAsync(VBSyntax
var csTargetArrayExpression = await node.Expression.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
var convertedBounds = (await CommonConversions.ConvertArrayBoundsAsync(node.ArrayBounds)).Sizes.ToList();
if (preserve && convertedBounds.Count == 1) {
var argumentList = new[] { csTargetArrayExpression, convertedBounds.Single() }.CreateCsArgList(SyntaxKind.RefKeyword);
bool isProperty = _semanticModel.GetSymbolInfo(node.Expression).Symbol?.IsKind(SymbolKind.Property) == true;
var arrayToResize = isProperty ? CreateLocalVariableWithUniqueName(node.Expression, "arg" + csTargetArrayExpression.ToString().Split('.').Last(), csTargetArrayExpression) : default;
var resizeArg = isProperty ? (ExpressionSyntax)arrayToResize.Reference : csTargetArrayExpression;

var argumentList = new[] { resizeArg, convertedBounds.Single() }.CreateCsArgList(SyntaxKind.RefKeyword);
var arrayResize = SyntaxFactory.InvocationExpression(ValidSyntaxFactory.MemberAccess(nameof(Array), nameof(Array.Resize)), argumentList);

if (isProperty) {
var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, csTargetArrayExpression, arrayToResize.Reference);
return SyntaxFactory.List(new StatementSyntax[] { arrayToResize.Declaration, SyntaxFactory.ExpressionStatement(arrayResize), SyntaxFactory.ExpressionStatement(assignment) });
}

return SingleStatement(arrayResize);
}
var newArrayAssignment = CreateNewArrayAssignment(node.Expression, csTargetArrayExpression, convertedBounds);
Expand Down
19 changes: 12 additions & 7 deletions CodeConverter/CSharp/NameExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Data;
using System.Data;
using System.Globalization;
using ICSharpCode.CodeConverter.CSharp.Replacements;
using ICSharpCode.CodeConverter.Util.FromRoslyn;
Expand Down Expand Up @@ -688,14 +688,19 @@ private static QualifiedNameSyntax Qualify(string qualification, ExpressionSynta

private static bool IsSubPartOfConditionalAccess(VBasic.Syntax.MemberAccessExpressionSyntax node)
{
var firstPossiblyConditionalAncestor = node.Parent;
while (firstPossiblyConditionalAncestor != null &&
firstPossiblyConditionalAncestor.IsKind(VBasic.SyntaxKind.InvocationExpression,
VBasic.SyntaxKind.SimpleMemberAccessExpression)) {
firstPossiblyConditionalAncestor = firstPossiblyConditionalAncestor.Parent;
static bool IsMemberAccessChain(SyntaxNode exp) =>
exp?.IsKind(VBasic.SyntaxKind.InvocationExpression,
VBasic.SyntaxKind.SimpleMemberAccessExpression,
VBasic.SyntaxKind.ParenthesizedExpression,
VBasic.SyntaxKind.ConditionalAccessExpression) == true;

for (SyntaxNode child = node, parent = node.Parent; IsMemberAccessChain(parent); child = parent, parent = parent.Parent) {
if (parent is VBSyntax.ConditionalAccessExpressionSyntax cae && cae.WhenNotNull == child) {
return true; // On right hand side of a ?. conditional access
}
}

return firstPossiblyConditionalAncestor?.IsKind(VBasic.SyntaxKind.ConditionalAccessExpression) == true;
return false;
}

private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode expr, SyntaxToken idToken, string overrideIdentifier)
Expand Down
18 changes: 15 additions & 3 deletions CodeConverter/Common/SolutionFileTextEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,21 @@ public class SolutionFileTextEditor : ISolutionFileTextEditor
var projectReferenceReplacements = new List<(string Find, string Replace, bool FirstOnly)>();
foreach (var relativeProjPath in relativeProjPaths)
{
var escapedProjPath = Regex.Escape(relativeProjPath);
var newProjPath = PathConverter.TogglePathExtension(relativeProjPath);
projectReferenceReplacements.Add((escapedProjPath, newProjPath, false));
// Add replacements for both backslash and forward-slash variants so .slnx files using either separator are handled
var nativeVariant = relativeProjPath;
var altVariant = relativeProjPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);

// native (likely backslashes on Windows)
var escapedNative = Regex.Escape(nativeVariant);
var newNative = PathConverter.TogglePathExtension(nativeVariant);
projectReferenceReplacements.Add((escapedNative, newNative, false));

// alternate (forward slashes)
if (altVariant != nativeVariant) {
var escapedAlt = Regex.Escape(altVariant);
var newAlt = PathConverter.TogglePathExtension(altVariant);
projectReferenceReplacements.Add((escapedAlt, newAlt, false));
}
}

return projectReferenceReplacements;
Expand Down
6 changes: 3 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
<WarningLevel>4</WarningLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<LangVersion Condition="'$(LangVersion)' == ''">14.0</LangVersion>
<AssemblyVersion>10.0.0.0</AssemblyVersion>
<FileVersion>10.0.0.0</FileVersion>
<Version>10.0.0</Version>
<AssemblyVersion>10.0.1.0</AssemblyVersion>
<FileVersion>10.0.1.0</FileVersion>
<Version>10.0.1</Version>
<Authors>ICSharpCode</Authors>
<Copyright>Copyright (c) 2017-2023 AlphaSierraPapa for the CodeConverter team</Copyright>
<Company>ICSharpCode</Company>
Expand Down
44 changes: 42 additions & 2 deletions Tests/CSharp/StatementTests/MethodStatementTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using ICSharpCode.CodeConverter.Tests.TestRunners;
using Xunit;

Expand Down Expand Up @@ -1673,4 +1673,44 @@ public object Func()
}
}");
}
}

[Fact]
public async Task WithBlockWithNullConditionalAccessAsync()
{
await TestConversionVisualBasicToCSharpAsync(@"
Public Class Class1
Public Property x As Class1
Public Property Name As String
End Class

Public Class TestClass
Private _Data As Class1
Private x As String

Public Sub TestMethod()
With _Data
x = .x?.Name
End With
End Sub
End Class", @"
public partial class Class1
{
public Class1 x { get; set; }
public string Name { get; set; }
}

public partial class TestClass
{
private Class1 _Data;
private string x;

public void TestMethod()
{
{
ref var withBlock = ref _Data;
x = withBlock.x?.Name;
}
}
}");
}
}
33 changes: 33 additions & 0 deletions Tests/CSharp/StatementTests/RedimPreserveTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Threading.Tasks;
using ICSharpCode.CodeConverter.Tests.TestRunners;
using Xunit;

namespace ICSharpCode.CodeConverter.Tests.CSharp.StatementTests;

public class RedimPreserveTests : ConverterTestBase
{
[Fact]
public async Task RedimPreserveOnPropertyAsync()
{
await TestConversionVisualBasicToCSharpAsync(
@"Public Class TestClass
Public Property NumArray1 As Integer()

Public Sub New()
ReDim Preserve NumArray1(4)
End Sub
End Class", @"using System;

public partial class TestClass
{
public int[] NumArray1 { get; set; }

public TestClass()
{
var argNumArray1 = NumArray1;
Array.Resize(ref argNumArray1, 5);
NumArray1 = argNumArray1;
}
}");
}
}
25 changes: 25 additions & 0 deletions Tests/LanguageAgnostic/SolutionFileTextEditorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,31 @@ public void ConvertSolutionFile_WhenInSolutionBaseDirThenUpdated()
Assert.Equal(expectedSlnFile, Utils.HomogenizeEol(convertedSlnFile));
}

[Fact]
public void ConvertSlnxSolutionFile_ProjectElementsWithForwardSlashesAreUpdated()
{
//Arrange
var slnxContents = "<Solution>\r\n <Project Path=\"ConsoleApp1/ConsoleApp1.vbproj\" />\r\n</Solution>";
var slnxSln = CreateTestSolution(@"C:\MySolution\MySolution.slnx");
var projectId = ProjectId.CreateNewId();
var projInfo = ProjectInfo.Create(projectId, VersionStamp.Create(), "ConsoleApp1", "ConsoleApp1",
LanguageNames.VisualBasic, @"C:\MySolution\ConsoleApp1\ConsoleApp1.vbproj");
slnxSln = slnxSln.AddProject(projInfo);
var testProject = slnxSln.GetProject(projectId);

_fsMock.Setup(mock => mock.File.ReadAllText(It.IsAny<string>())).Returns("");

var slnConverter = SolutionConverter.CreateFor<VBToCSConversion>(new List<Project> { testProject },
fileSystem: _fsMock.Object, solutionContents: slnxContents);

//Act
var convertedSlnFile = slnConverter.ConvertSolutionFile().ConvertedCode;

//Assert
var expectedSlnFile = "<Solution>\r\n <Project Path=\"ConsoleApp1/ConsoleApp1.csproj\" />\r\n</Solution>";
Assert.Equal(expectedSlnFile, Utils.HomogenizeEol(convertedSlnFile));
}

[Fact]
public void ConvertSolutionFile_WhenInProjectFolderThenUpdated()
{
Expand Down
2 changes: 1 addition & 1 deletion Tests/TestConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static class TestConstants
public static string GetTestDataDirectory()
{
var assembly = Assembly.GetExecutingAssembly();
var solutionDir = new FileInfo(new Uri(assembly.Location).LocalPath).Directory?.Parent?.Parent ??
var solutionDir = new FileInfo(new Uri(assembly.Location).LocalPath).Directory?.Parent?.Parent?.Parent ??
throw new InvalidOperationException(assembly.Location);
return Path.Combine(solutionDir.FullName, "TestData");
}
Expand Down
5 changes: 3 additions & 2 deletions Tests/Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks>net10.0;net8.0</TargetFrameworks>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<OutputType>Library</OutputType>
<AssemblyName>ICSharpCode.CodeConverter.Tests</AssemblyName>
<RootNamespace>ICSharpCode.CodeConverter.Tests</RootNamespace>
Expand Down
2 changes: 1 addition & 1 deletion Vsix/source.extension.vsixmanifest
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="7e2a69d6-193b-4cdf-878d-3370d5931942" Version="10.0.0.0" Language="en-US" Publisher="IC#Code"/>
<Identity Id="7e2a69d6-193b-4cdf-878d-3370d5931942" Version="10.0.1.0" Language="en-US" Publisher="IC#Code"/>
<DisplayName>Code Converter (VB - C#)</DisplayName>
<Description xml:space="preserve">Convert VB.NET to C# and vice versa with this roslyn based converter</Description>
<MoreInfo>https://github.com/icsharpcode/CodeConverter</MoreInfo>
Expand Down
5 changes: 3 additions & 2 deletions web/web.esproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.4671869">
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.4671869">
<PropertyGroup>
<ResolveNuGetPackages>false</ResolveNuGetPackages>
<ShouldRunNpmAudit>false</ShouldRunNpmAudit>
<StartupCommand>npm run dev</StartupCommand>
<JavaScriptTestRoot>src\</JavaScriptTestRoot>
<JavaScriptTestFramework>Vitest</JavaScriptTestFramework>
Expand All @@ -8,5 +10,4 @@
<!-- Folder where production build objects will be placed -->
<BuildOutputFolder>$(MSBuildProjectDirectory)\dist</BuildOutputFolder>
</PropertyGroup>

</Project>
Loading