Skip to content

Commit 63c7a2a

Browse files
Merge pull request #1223 from GrahamTheCoder/master
Fixes for #1156, #1174, #1195
2 parents 763a24b + df7b89e commit 63c7a2a

15 files changed

+229
-27
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
param()
2+
3+
$ErrorActionPreference = 'Stop'
4+
5+
Write-Host "Creating global.json to enforce .NET 8 for MSBuild"
6+
$globalJson = '{"sdk":{"version":"8.0.0","rollForward":"latestFeature"}}'
7+
[System.IO.File]::WriteAllText('global.json', $globalJson, [System.Text.Encoding]::UTF8)
8+
9+
Write-Host "Searching for project files under Tests/TestData..."
10+
$projFiles = Get-ChildItem -Path Tests/TestData -Recurse -Include *.csproj,*.vbproj,*.fsproj -File -ErrorAction SilentlyContinue
11+
12+
if (-not $projFiles) {
13+
Write-Host "No project files found under Tests/TestData"
14+
exit 0
15+
}
16+
17+
$changed = $false
18+
foreach ($f in $projFiles) {
19+
$path = $f.FullName
20+
Write-Host "Processing: $path"
21+
22+
# Use StreamReader to detect encoding and preserve it when writing back
23+
$sr = [System.IO.StreamReader]::new($path, $true)
24+
try {
25+
$content = $sr.ReadToEnd()
26+
$encoding = $sr.CurrentEncoding
27+
} finally {
28+
$sr.Close()
29+
}
30+
31+
# Replace net10.0 and net10.0-windows with net8.0 / net8.0-windows
32+
$updated = [System.Text.RegularExpressions.Regex]::Replace($content, '<TargetFramework>net10\.0(-windows)?</TargetFramework>', '<TargetFramework>net8.0$1</TargetFramework>')
33+
34+
if ($updated -ne $content) {
35+
Write-Host "Updating TargetFramework in: $path"
36+
# Write back preserving detected encoding and internal newlines
37+
[System.IO.File]::WriteAllText($path, $updated, $encoding)
38+
$changed = $true
39+
}
40+
}
41+
42+
if ($changed) {
43+
Write-Host "Changes detected — committing to local repo so working tree is clean for tests"
44+
git config user.name "github-actions[bot]"
45+
if ($env:GITHUB_ACTOR) {
46+
git config user.email "$($env:GITHUB_ACTOR)@users.noreply.github.com"
47+
} else {
48+
git config user.email "[email protected]"
49+
}
50+
git add -A
51+
git commit -m "CI: Update Tests/TestData TargetFramework -> net8.0 for .NET 8 run" || Write-Host "No commit created (maybe no staged changes)"
52+
Write-Host "Committed changes locally."
53+
} else {
54+
Write-Host "No TargetFramework updates required."
55+
}
56+
57+
Write-Host "Done."

.github/workflows/dotnet-tests.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ jobs:
1919

2020
steps:
2121
- uses: actions/checkout@v4
22-
2322
- name: Setup .NET
2423
uses: actions/setup-dotnet@v4
2524
with:
@@ -30,11 +29,19 @@ jobs:
3029
with:
3130
vs-version: ${{ inputs.vs-version }}
3231

32+
- name: Build
33+
run: dotnet build DotNetBuildable.slnf /p:Configuration=Release
34+
35+
- name: Create global.json to enforce .NET 8 for MSBuild and update Tests/TestData projects
36+
if: inputs.dotnet-version == '8.0.x'
37+
shell: pwsh
38+
run: ./.github/scripts/update-testdata-targetframework.ps1
39+
3340
- name: Log MSBuild version
3441
run: msbuild -version
3542

36-
- name: Build
37-
run: dotnet build DotNetBuildable.slnf /p:Configuration=Release
43+
- name: Log .NET version
44+
run: dotnet --info
3845

3946
- name: Execute unit tests
40-
run: dotnet test Tests/bin/Release/ICSharpCode.CodeConverter.Tests.dll
47+
run: dotnet test Tests/Tests.csproj -c Release --framework net${{ inputs.dotnet-version == '8.0.x' && '8.0' || '10.0' }} --no-build

.github/workflows/dotnet.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
branches: [ master, main ]
88

99
env:
10-
BuildVersion: '10.0.0'
10+
BuildVersion: '10.0.1'
1111

1212
jobs:
1313
build:

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1414
### C# -> VB
1515

1616

17+
## [10.0.1] - 2026-02-28
18+
19+
* Reintroduce tentative legacy support for dotnet 8 and VS2022
20+
* Support slnx format [1195](https://github.com/icsharpcode/CodeConverter/issues/1195)
21+
22+
### VB -> C#
23+
* Fix for ReDim Preserve of array property - [#1156](https://github.com/icsharpcode/CodeConverter/issues/1156)
24+
* Fix for with block conversion with null conditional [#1174](https://github.com/icsharpcode/CodeConverter/issues/1174)
25+
Fixes #1195
26+
27+
1728
## [10.0.0] - 2026-02-06
1829

1930
* Support for net framework dropped. Please use an older version if you are converting projects that still use it.

CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,18 @@ private async Task<SyntaxList<StatementSyntax>> ConvertRedimClauseAsync(VBSyntax
320320
var csTargetArrayExpression = await node.Expression.AcceptAsync<ExpressionSyntax>(_expressionVisitor);
321321
var convertedBounds = (await CommonConversions.ConvertArrayBoundsAsync(node.ArrayBounds)).Sizes.ToList();
322322
if (preserve && convertedBounds.Count == 1) {
323-
var argumentList = new[] { csTargetArrayExpression, convertedBounds.Single() }.CreateCsArgList(SyntaxKind.RefKeyword);
323+
bool isProperty = _semanticModel.GetSymbolInfo(node.Expression).Symbol?.IsKind(SymbolKind.Property) == true;
324+
var arrayToResize = isProperty ? CreateLocalVariableWithUniqueName(node.Expression, "arg" + csTargetArrayExpression.ToString().Split('.').Last(), csTargetArrayExpression) : default;
325+
var resizeArg = isProperty ? (ExpressionSyntax)arrayToResize.Reference : csTargetArrayExpression;
326+
327+
var argumentList = new[] { resizeArg, convertedBounds.Single() }.CreateCsArgList(SyntaxKind.RefKeyword);
324328
var arrayResize = SyntaxFactory.InvocationExpression(ValidSyntaxFactory.MemberAccess(nameof(Array), nameof(Array.Resize)), argumentList);
329+
330+
if (isProperty) {
331+
var assignment = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, csTargetArrayExpression, arrayToResize.Reference);
332+
return SyntaxFactory.List(new StatementSyntax[] { arrayToResize.Declaration, SyntaxFactory.ExpressionStatement(arrayResize), SyntaxFactory.ExpressionStatement(assignment) });
333+
}
334+
325335
return SingleStatement(arrayResize);
326336
}
327337
var newArrayAssignment = CreateNewArrayAssignment(node.Expression, csTargetArrayExpression, convertedBounds);

CodeConverter/CSharp/NameExpressionNodeVisitor.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Data;
1+
using System.Data;
22
using System.Globalization;
33
using ICSharpCode.CodeConverter.CSharp.Replacements;
44
using ICSharpCode.CodeConverter.Util.FromRoslyn;
@@ -688,14 +688,19 @@ private static QualifiedNameSyntax Qualify(string qualification, ExpressionSynta
688688

689689
private static bool IsSubPartOfConditionalAccess(VBasic.Syntax.MemberAccessExpressionSyntax node)
690690
{
691-
var firstPossiblyConditionalAncestor = node.Parent;
692-
while (firstPossiblyConditionalAncestor != null &&
693-
firstPossiblyConditionalAncestor.IsKind(VBasic.SyntaxKind.InvocationExpression,
694-
VBasic.SyntaxKind.SimpleMemberAccessExpression)) {
695-
firstPossiblyConditionalAncestor = firstPossiblyConditionalAncestor.Parent;
691+
static bool IsMemberAccessChain(SyntaxNode exp) =>
692+
exp?.IsKind(VBasic.SyntaxKind.InvocationExpression,
693+
VBasic.SyntaxKind.SimpleMemberAccessExpression,
694+
VBasic.SyntaxKind.ParenthesizedExpression,
695+
VBasic.SyntaxKind.ConditionalAccessExpression) == true;
696+
697+
for (SyntaxNode child = node, parent = node.Parent; IsMemberAccessChain(parent); child = parent, parent = parent.Parent) {
698+
if (parent is VBSyntax.ConditionalAccessExpressionSyntax cae && cae.WhenNotNull == child) {
699+
return true; // On right hand side of a ?. conditional access
700+
}
696701
}
697702

698-
return firstPossiblyConditionalAncestor?.IsKind(VBasic.SyntaxKind.ConditionalAccessExpression) == true;
703+
return false;
699704
}
700705

701706
private static CSharpSyntaxNode ReplaceRightmostIdentifierText(CSharpSyntaxNode expr, SyntaxToken idToken, string overrideIdentifier)

CodeConverter/Common/SolutionFileTextEditor.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,21 @@ public class SolutionFileTextEditor : ISolutionFileTextEditor
1515
var projectReferenceReplacements = new List<(string Find, string Replace, bool FirstOnly)>();
1616
foreach (var relativeProjPath in relativeProjPaths)
1717
{
18-
var escapedProjPath = Regex.Escape(relativeProjPath);
19-
var newProjPath = PathConverter.TogglePathExtension(relativeProjPath);
20-
projectReferenceReplacements.Add((escapedProjPath, newProjPath, false));
18+
// Add replacements for both backslash and forward-slash variants so .slnx files using either separator are handled
19+
var nativeVariant = relativeProjPath;
20+
var altVariant = relativeProjPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
21+
22+
// native (likely backslashes on Windows)
23+
var escapedNative = Regex.Escape(nativeVariant);
24+
var newNative = PathConverter.TogglePathExtension(nativeVariant);
25+
projectReferenceReplacements.Add((escapedNative, newNative, false));
26+
27+
// alternate (forward slashes)
28+
if (altVariant != nativeVariant) {
29+
var escapedAlt = Regex.Escape(altVariant);
30+
var newAlt = PathConverter.TogglePathExtension(altVariant);
31+
projectReferenceReplacements.Add((escapedAlt, newAlt, false));
32+
}
2133
}
2234

2335
return projectReferenceReplacements;

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
<WarningLevel>4</WarningLevel>
99
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
1010
<LangVersion Condition="'$(LangVersion)' == ''">14.0</LangVersion>
11-
<AssemblyVersion>10.0.0.0</AssemblyVersion>
12-
<FileVersion>10.0.0.0</FileVersion>
13-
<Version>10.0.0</Version>
11+
<AssemblyVersion>10.0.1.0</AssemblyVersion>
12+
<FileVersion>10.0.1.0</FileVersion>
13+
<Version>10.0.1</Version>
1414
<Authors>ICSharpCode</Authors>
1515
<Copyright>Copyright (c) 2017-2023 AlphaSierraPapa for the CodeConverter team</Copyright>
1616
<Company>ICSharpCode</Company>

Tests/CSharp/StatementTests/MethodStatementTests.cs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
22
using ICSharpCode.CodeConverter.Tests.TestRunners;
33
using Xunit;
44

@@ -1673,4 +1673,44 @@ public object Func()
16731673
}
16741674
}");
16751675
}
1676-
}
1676+
1677+
[Fact]
1678+
public async Task WithBlockWithNullConditionalAccessAsync()
1679+
{
1680+
await TestConversionVisualBasicToCSharpAsync(@"
1681+
Public Class Class1
1682+
Public Property x As Class1
1683+
Public Property Name As String
1684+
End Class
1685+
1686+
Public Class TestClass
1687+
Private _Data As Class1
1688+
Private x As String
1689+
1690+
Public Sub TestMethod()
1691+
With _Data
1692+
x = .x?.Name
1693+
End With
1694+
End Sub
1695+
End Class", @"
1696+
public partial class Class1
1697+
{
1698+
public Class1 x { get; set; }
1699+
public string Name { get; set; }
1700+
}
1701+
1702+
public partial class TestClass
1703+
{
1704+
private Class1 _Data;
1705+
private string x;
1706+
1707+
public void TestMethod()
1708+
{
1709+
{
1710+
ref var withBlock = ref _Data;
1711+
x = withBlock.x?.Name;
1712+
}
1713+
}
1714+
}");
1715+
}
1716+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Threading.Tasks;
2+
using ICSharpCode.CodeConverter.Tests.TestRunners;
3+
using Xunit;
4+
5+
namespace ICSharpCode.CodeConverter.Tests.CSharp.StatementTests;
6+
7+
public class RedimPreserveTests : ConverterTestBase
8+
{
9+
[Fact]
10+
public async Task RedimPreserveOnPropertyAsync()
11+
{
12+
await TestConversionVisualBasicToCSharpAsync(
13+
@"Public Class TestClass
14+
Public Property NumArray1 As Integer()
15+
16+
Public Sub New()
17+
ReDim Preserve NumArray1(4)
18+
End Sub
19+
End Class", @"using System;
20+
21+
public partial class TestClass
22+
{
23+
public int[] NumArray1 { get; set; }
24+
25+
public TestClass()
26+
{
27+
var argNumArray1 = NumArray1;
28+
Array.Resize(ref argNumArray1, 5);
29+
NumArray1 = argNumArray1;
30+
}
31+
}");
32+
}
33+
}

0 commit comments

Comments
 (0)