Skip to content

Commit 43e33b4

Browse files
committed
Address review: set null at creation, reconstruct from VB syntax in Fix method
VisitParameter now sets the default to null directly when it detects a char-typed default for a string parameter, rather than converting to a char expression that FixCharDefaultsForStringParams would later replace. FixCharDefaultsForStringParams now reconstructs the char expression from the VB syntax node via BuildCharExpressionFromVbSyntax, removing the previously-set-then-overwritten value pattern. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX
1 parent 6b46add commit 43e33b4

File tree

2 files changed

+32
-18
lines changed

2 files changed

+32
-18
lines changed

CodeConverter/CSharp/DeclarationNodeVisitor.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -684,16 +684,17 @@ public override async Task<CSharpSyntaxNode> VisitMethodBlock(VBSyntax.MethodBlo
684684

685685
/// <summary>
686686
/// In VB, a Char constant can be the default value of a String parameter. In C#, this is invalid.
687-
/// Fix: replace the default with null and prepend a null-coalescing assignment in the method body.
687+
/// VisitParameter in ExpressionNodeVisitor sets the default to null for these cases; this method
688+
/// prepends a null-coalescing assignment to restore the char default at runtime.
688689
/// </summary>
689690
private static (BaseMethodDeclarationSyntax MethodBlock, BlockSyntax ConvertedStatements) FixCharDefaultsForStringParams(
690691
IMethodSymbol declaredSymbol, BaseMethodDeclarationSyntax methodBlock, BlockSyntax convertedStatements, SemanticModel semanticModel)
691692
{
692693
var prependedStatements = new List<StatementSyntax>();
693-
var updatedParams = methodBlock.ParameterList.Parameters.ToList();
694694
var vbParams = declaredSymbol?.Parameters ?? ImmutableArray<IParameterSymbol>.Empty;
695+
var csParams = methodBlock.ParameterList.Parameters;
695696

696-
for (int i = 0; i < updatedParams.Count && i < vbParams.Length; i++) {
697+
for (int i = 0; i < csParams.Count && i < vbParams.Length; i++) {
697698
var vbParam = vbParams[i];
698699
if (vbParam.Type.SpecialType != SpecialType.System_String
699700
|| !vbParam.HasExplicitDefaultValue) continue;
@@ -704,20 +705,17 @@ private static (BaseMethodDeclarationSyntax MethodBlock, BlockSyntax ConvertedSt
704705
if (defaultValueNode == null) continue;
705706
if (semanticModel.GetTypeInfo(defaultValueNode).Type?.SpecialType != SpecialType.System_Char) continue;
706707

707-
var csParam = updatedParams[i];
708-
var defaultExpr = csParam.Default?.Value;
709-
if (defaultExpr is null) continue;
708+
var csParam = csParams[i];
709+
// The default was set to null at point of creation in VisitParameter (ExpressionNodeVisitor).
710+
// Reconstruct the char expression from VB syntax to avoid depending on the already-converted value.
711+
var charExpr = BuildCharExpressionFromVbSyntax(defaultValueNode, semanticModel);
710712

711-
// Replace the default value with null
712-
updatedParams[i] = csParam.WithDefault(
713-
CS.SyntaxFactory.EqualsValueClause(ValidSyntaxFactory.NullExpression));
714-
715-
// Build: paramName = paramName ?? existingDefaultExpr.ToString();
713+
// Build: paramName = paramName ?? charExpr.ToString();
716714
var paramId = ValidSyntaxFactory.IdentifierName(csParam.Identifier.ValueText);
717715
var toStringCall = CS.SyntaxFactory.InvocationExpression(
718716
CS.SyntaxFactory.MemberAccessExpression(
719717
CS.SyntaxKind.SimpleMemberAccessExpression,
720-
defaultExpr.WithoutTrivia(),
718+
charExpr,
721719
CS.SyntaxFactory.IdentifierName("ToString")));
722720
var coalesce = CS.SyntaxFactory.BinaryExpression(CS.SyntaxKind.CoalesceExpression, paramId, toStringCall);
723721
var assignment = CS.SyntaxFactory.AssignmentExpression(CS.SyntaxKind.SimpleAssignmentExpression, paramId, coalesce);
@@ -726,11 +724,18 @@ private static (BaseMethodDeclarationSyntax MethodBlock, BlockSyntax ConvertedSt
726724

727725
if (prependedStatements.Count == 0) return (methodBlock, convertedStatements);
728726

729-
var newParamList = methodBlock.ParameterList.WithParameters(CS.SyntaxFactory.SeparatedList(updatedParams, methodBlock.ParameterList.Parameters.GetSeparators()));
730-
methodBlock = methodBlock.WithParameterList(newParamList);
731-
convertedStatements = convertedStatements.WithStatements(CS.SyntaxFactory.List(prependedStatements.Concat(convertedStatements.Statements)));
727+
return (methodBlock, convertedStatements.WithStatements(CS.SyntaxFactory.List(prependedStatements.Concat(convertedStatements.Statements))));
728+
}
732729

733-
return (methodBlock, convertedStatements);
730+
private static ExpressionSyntax BuildCharExpressionFromVbSyntax(VBSyntax.ExpressionSyntax defaultValueNode, SemanticModel semanticModel)
731+
{
732+
// For constant char literals (e.g. "^"c), reconstruct directly from the constant value
733+
var constant = semanticModel.GetConstantValue(defaultValueNode);
734+
if (constant.HasValue && constant.Value is char c) {
735+
return CS.SyntaxFactory.LiteralExpression(CS.SyntaxKind.CharacterLiteralExpression, CS.SyntaxFactory.Literal(c));
736+
}
737+
// For named constant references (e.g. DlM), use the VB expression text as-is
738+
return ValidSyntaxFactory.IdentifierName(defaultValueNode.ToString());
734739
}
735740

736741
private static bool IsThisResumeLayoutInvocation(StatementSyntax s)

CodeConverter/CSharp/ExpressionNodeVisitor.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,17 @@ public override async Task<CSharpSyntaxNode> VisitParameter(VBSyntax.ParameterSy
476476
attributes.Insert(0,
477477
CS.SyntaxFactory.AttributeList(CS.SyntaxFactory.SeparatedList(optionalAttributes)));
478478
} else {
479-
@default = CS.SyntaxFactory.EqualsValueClause(
480-
await node.Default.Value.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor));
479+
var defaultValue = node.Default.Value.SkipIntoParens();
480+
var paramSymbol = _semanticModel.GetDeclaredSymbol(node) as IParameterSymbol;
481+
if (paramSymbol?.Type?.SpecialType == SpecialType.System_String &&
482+
_semanticModel.GetTypeInfo(defaultValue).Type?.SpecialType == SpecialType.System_Char) {
483+
// VB allows a Char constant as default for a String parameter; C# does not.
484+
// Set null here; FixCharDefaultsForStringParams (DeclarationNodeVisitor) adds the null-coalesce assignment.
485+
@default = CS.SyntaxFactory.EqualsValueClause(ValidSyntaxFactory.NullExpression);
486+
} else {
487+
@default = CS.SyntaxFactory.EqualsValueClause(
488+
await node.Default.Value.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor));
489+
}
481490
}
482491
}
483492

0 commit comments

Comments
 (0)