|
| 1 | +using System.Collections.Immutable; |
1 | 2 | using System.Runtime.InteropServices; |
2 | 3 | using Microsoft.CodeAnalysis.Classification; |
3 | 4 | using Microsoft.CodeAnalysis.CSharp; |
@@ -674,11 +675,59 @@ public override async Task<CSharpSyntaxNode> VisitMethodBlock(VBSyntax.MethodBlo |
674 | 675 | convertedStatements = convertedStatements.InsertNodesBefore(firstResumeLayout, _typeContext.HandledEventsAnalysis.GetInitializeComponentClassEventHandlers()); |
675 | 676 | } |
676 | 677 |
|
| 678 | + (methodBlock, convertedStatements) = FixCharDefaultsForStringParams(declaredSymbol, methodBlock, convertedStatements); |
| 679 | + |
677 | 680 | var body = _accessorDeclarationNodeConverter.WithImplicitReturnStatements(node, convertedStatements, csReturnVariableOrNull); |
678 | 681 |
|
679 | 682 | return methodBlock.WithBody(body); |
680 | 683 | } |
681 | 684 |
|
| 685 | + /// <summary> |
| 686 | + /// 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. |
| 688 | + /// </summary> |
| 689 | + private static (BaseMethodDeclarationSyntax MethodBlock, BlockSyntax ConvertedStatements) FixCharDefaultsForStringParams( |
| 690 | + IMethodSymbol declaredSymbol, BaseMethodDeclarationSyntax methodBlock, BlockSyntax convertedStatements) |
| 691 | + { |
| 692 | + var prependedStatements = new List<StatementSyntax>(); |
| 693 | + var updatedParams = methodBlock.ParameterList.Parameters.ToList(); |
| 694 | + var vbParams = declaredSymbol?.Parameters ?? ImmutableArray<IParameterSymbol>.Empty; |
| 695 | + |
| 696 | + for (int i = 0; i < updatedParams.Count && i < vbParams.Length; i++) { |
| 697 | + var vbParam = vbParams[i]; |
| 698 | + if (vbParam.Type.SpecialType != SpecialType.System_String |
| 699 | + || !vbParam.HasExplicitDefaultValue |
| 700 | + || vbParam.ExplicitDefaultValue is not char) continue; |
| 701 | + |
| 702 | + var csParam = updatedParams[i]; |
| 703 | + var defaultExpr = csParam.Default?.Value; |
| 704 | + if (defaultExpr is null) continue; |
| 705 | + |
| 706 | + // Replace the default value with null |
| 707 | + updatedParams[i] = csParam.WithDefault( |
| 708 | + CS.SyntaxFactory.EqualsValueClause(ValidSyntaxFactory.NullExpression)); |
| 709 | + |
| 710 | + // Build: paramName = paramName ?? existingDefaultExpr.ToString(); |
| 711 | + var paramId = ValidSyntaxFactory.IdentifierName(csParam.Identifier.ValueText); |
| 712 | + var toStringCall = CS.SyntaxFactory.InvocationExpression( |
| 713 | + CS.SyntaxFactory.MemberAccessExpression( |
| 714 | + CS.SyntaxKind.SimpleMemberAccessExpression, |
| 715 | + defaultExpr.WithoutTrivia(), |
| 716 | + CS.SyntaxFactory.IdentifierName("ToString"))); |
| 717 | + var coalesce = CS.SyntaxFactory.BinaryExpression(CS.SyntaxKind.CoalesceExpression, paramId, toStringCall); |
| 718 | + var assignment = CS.SyntaxFactory.AssignmentExpression(CS.SyntaxKind.SimpleAssignmentExpression, paramId, coalesce); |
| 719 | + prependedStatements.Add(CS.SyntaxFactory.ExpressionStatement(assignment)); |
| 720 | + } |
| 721 | + |
| 722 | + if (prependedStatements.Count == 0) return (methodBlock, convertedStatements); |
| 723 | + |
| 724 | + var newParamList = methodBlock.ParameterList.WithParameters(CS.SyntaxFactory.SeparatedList(updatedParams, methodBlock.ParameterList.Parameters.GetSeparators())); |
| 725 | + methodBlock = methodBlock.WithParameterList(newParamList); |
| 726 | + convertedStatements = convertedStatements.WithStatements(CS.SyntaxFactory.List(prependedStatements.Concat(convertedStatements.Statements))); |
| 727 | + |
| 728 | + return (methodBlock, convertedStatements); |
| 729 | + } |
| 730 | + |
682 | 731 | private static bool IsThisResumeLayoutInvocation(StatementSyntax s) |
683 | 732 | { |
684 | 733 | return s is ExpressionStatementSyntax ess && ess.Expression is InvocationExpressionSyntax ies && ies.Expression.ToString().Equals("this.ResumeLayout", StringComparison.Ordinal); |
|
0 commit comments