Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 6 additions & 6 deletions CodeConverter/CSharp/ArgumentConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task<CSharpSyntaxNode> ConvertSimpleArgumentAsync(VBSyntax.SimpleAr
var refType = CommonConversions.GetRefConversionType(node, argList, possibleParameters.Value, out var argName, out var refKind);
token = CommonConversions.GetRefToken(refKind);
if (refType != SemanticModelExtensions.RefConversion.Inline) {
convertedArgExpression = HoistByRefDeclaration(node, convertedArgExpression, refType, argName, refKind);
convertedArgExpression = HoistByRefDeclaration(node.Expression, convertedArgExpression, refType, argName, refKind);
} else {
convertedArgExpression = typeConversionAnalyzer.AddExplicitConversion(node.Expression, convertedArgExpression, defaultToCast: refKind != RefKind.None);
}
Expand Down Expand Up @@ -119,11 +119,11 @@ CSSyntax.ArgumentSyntax ConvertOmittedArgument(IParameterSymbol parameter)
}


private CSSyntax.ExpressionSyntax HoistByRefDeclaration(VBSyntax.SimpleArgumentSyntax node, CSSyntax.ExpressionSyntax refLValue, SemanticModelExtensions.RefConversion refType, string argName, RefKind refKind)
internal CSSyntax.ExpressionSyntax HoistByRefDeclaration(VBSyntax.ExpressionSyntax node, CSSyntax.ExpressionSyntax refLValue, SemanticModelExtensions.RefConversion refType, string argName, RefKind refKind)
{
string prefix = $"arg{argName}";
var expressionTypeInfo = _semanticModel.GetTypeInfo(node.Expression);
bool useVar = expressionTypeInfo.Type?.Equals(expressionTypeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability) == true && !CommonConversions.ShouldPreferExplicitType(node.Expression, expressionTypeInfo.ConvertedType, out var _);
var expressionTypeInfo = _semanticModel.GetTypeInfo(node);
bool useVar = expressionTypeInfo.Type?.Equals(expressionTypeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability) == true && !CommonConversions.ShouldPreferExplicitType(node, expressionTypeInfo.ConvertedType, out var _);
var typeSyntax = CommonConversions.GetTypeSyntax(expressionTypeInfo.ConvertedType, useVar);

if (refLValue is CSSyntax.ElementAccessExpressionSyntax eae) {
Expand All @@ -132,12 +132,12 @@ private CSSyntax.ExpressionSyntax HoistByRefDeclaration(VBSyntax.SimpleArgumentS
refLValue = eae.WithExpression(tmpContainer.IdentifierName);
}

var withCast = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, refLValue, defaultToCast: refKind != RefKind.None);
var withCast = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node, refLValue, defaultToCast: refKind != RefKind.None);

var local = _typeContext.PerScopeState.Hoist(new AdditionalDeclaration(prefix, withCast, typeSyntax));

if (refType == SemanticModelExtensions.RefConversion.PreAndPostAssignment) {
var convertedLocalIdentifier = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node.Expression, local.IdentifierName, forceSourceType: expressionTypeInfo.ConvertedType, forceTargetType: expressionTypeInfo.Type);
var convertedLocalIdentifier = CommonConversions.TypeConversionAnalyzer.AddExplicitConversion(node, local.IdentifierName, forceSourceType: expressionTypeInfo.ConvertedType, forceTargetType: expressionTypeInfo.Type);
_typeContext.PerScopeState.Hoist(new AdditionalAssignment(refLValue, convertedLocalIdentifier));
}

Expand Down
38 changes: 30 additions & 8 deletions CodeConverter/CSharp/NameExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public async Task<CSharpSyntaxNode> ConvertMemberAccessExpressionAsync(VBasic.Sy
if (IsSubPartOfConditionalAccess(node)) {
return isDefaultProperty ? SyntaxFactory.ElementBindingExpression()
: await AdjustForImplicitInvocationAsync(node, SyntaxFactory.MemberBindingExpression(simpleNameSyntax));
} else if (node.IsParentKind(Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.NamedFieldInitializer)) {
} else if (node.IsParentKind(VBasic.SyntaxKind.NamedFieldInitializer)) {
return ValidSyntaxFactory.IdentifierName(_tempNameForAnonymousScope[node.Name.Identifier.Text].Peek().TempName);
}
left = _withBlockLhs.Peek();
Expand Down Expand Up @@ -285,14 +285,36 @@ private async Task<ExpressionSyntax> ConvertInvocationAsync(VBSyntax.InvocationE
}

if (invocationSymbol.IsReducedExtension() && invocationSymbol is IMethodSymbol { ReducedFrom: { Parameters: var parameters } } &&
!parameters.FirstOrDefault().ValidCSharpExtensionMethodParameter() &&
node.Expression is VBSyntax.MemberAccessExpressionSyntax maes) {
var thisArgExpression = await maes.Expression.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor);
var thisArg = SyntaxFactory.Argument(thisArgExpression).WithRefKindKeyword(CommonConversions.GetRefToken(RefKind.Ref));
convertedArgumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(convertedArgumentList.Arguments.Prepend(thisArg)));
var containingType = (ExpressionSyntax)CommonConversions.CsSyntaxGenerator.TypeExpression(invocationSymbol.ContainingType);
convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, containingType,
ValidSyntaxFactory.IdentifierName((invocationSymbol.Name)));

var thisParam = parameters.FirstOrDefault();
bool requiresStaticInvocation = !thisParam.ValidCSharpExtensionMethodParameter();
var refKind = CommonConversions.GetCsRefKind(thisParam);

bool requiresHoist = false;
RefConversion refConversion = RefConversion.Inline;
if (refKind != RefKind.None) {
refConversion = _semanticModel.GetRefConversionForExpression(maes.Expression);
requiresHoist = refConversion != RefConversion.Inline;
}

if (requiresStaticInvocation || requiresHoist) {
var thisArgExpression = await maes.Expression.AcceptAsync<ExpressionSyntax>(TriviaConvertingExpressionVisitor);

if (requiresHoist) {
thisArgExpression = _argumentConverter.HoistByRefDeclaration(maes.Expression, thisArgExpression, refConversion, thisParam.Name, refKind);
}

if (requiresStaticInvocation) {
var thisArg = SyntaxFactory.Argument(thisArgExpression).WithRefKindKeyword(CommonConversions.GetRefToken(refKind));
convertedArgumentList = SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(convertedArgumentList.Arguments.Prepend(thisArg)));
var containingType = (ExpressionSyntax)CommonConversions.CsSyntaxGenerator.TypeExpression(invocationSymbol.ContainingType);
convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, containingType,
ValidSyntaxFactory.IdentifierName((invocationSymbol.Name)));
} else {
convertedExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, thisArgExpression, ValidSyntaxFactory.IdentifierName((invocationSymbol.Name)));
}
}
}

if (invocationSymbol is IMethodSymbol m && convertedExpression is LambdaExpressionSyntax) {
Expand Down
22 changes: 14 additions & 8 deletions CodeConverter/CSharp/SemanticModelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,15 @@ public static RefConversion NeedsVariableForArgument(this SemanticModel semantic
if (!(node is VBSyntax.SimpleArgumentSyntax sas) || sas is { Expression: VBSyntax.ParenthesizedExpressionSyntax }) return RefConversion.PreAssigment;
var expression = sas.Expression;

return GetRefConversion(expression);
return semanticModel.GetRefConversionForExpression(expression);

RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)
}

public static RefConversion GetRefConversionForExpression(this SemanticModel semanticModel, VBasic.Syntax.ExpressionSyntax expression)
{
RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expr)
{
var symbolInfo = semanticModel.GetSymbolInfoInDocument<ISymbol>(expression);
var symbolInfo = semanticModel.GetSymbolInfoInDocument<ISymbol>(expr);
if (symbolInfo is IPropertySymbol { ReturnsByRef: false, ReturnsByRefReadonly: false } propertySymbol) {
// a property in VB.NET code can be ReturnsByRef if it's defined in a C# assembly the VB.NET code references
return propertySymbol.IsReadOnly ? RefConversion.PreAssigment : RefConversion.PreAndPostAssignment;
Expand All @@ -87,10 +91,10 @@ RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)

if (DeclaredInUsing(symbolInfo)) return RefConversion.PreAssigment;

if (expression is VBasic.Syntax.IdentifierNameSyntax || expression is VBSyntax.MemberAccessExpressionSyntax ||
IsRefArrayAcces(expression)) {
if (expr is VBasic.Syntax.IdentifierNameSyntax || expr is VBSyntax.MemberAccessExpressionSyntax ||
IsRefArrayAcces(expr)) {

var typeInfo = semanticModel.GetTypeInfo(expression);
var typeInfo = semanticModel.GetTypeInfo(expr);
bool isTypeMismatch = typeInfo.Type == null || !typeInfo.Type.Equals(typeInfo.ConvertedType, SymbolEqualityComparer.IncludeNullability);

if (isTypeMismatch) {
Expand All @@ -103,9 +107,9 @@ RefConversion GetRefConversion(VBSyntax.ExpressionSyntax expression)
return RefConversion.PreAssigment;
}

bool IsRefArrayAcces(VBSyntax.ExpressionSyntax expression)
bool IsRefArrayAcces(VBSyntax.ExpressionSyntax expr)
{
if (!(expression is VBSyntax.InvocationExpressionSyntax ies)) return false;
if (!(expr is VBSyntax.InvocationExpressionSyntax ies)) return false;
var op = semanticModel.GetOperation(ies);
return (op.IsArrayElementAccess() || IsReturnsByRefPropertyElementAccess(op))
&& GetRefConversion(ies.Expression) == RefConversion.Inline;
Expand All @@ -117,6 +121,8 @@ static bool IsReturnsByRefPropertyElementAccess(IOperation op)
&& (prop.ReturnsByRef || prop.ReturnsByRefReadonly);
}
}

return GetRefConversion((VBSyntax.ExpressionSyntax)expression);
}

private static bool DeclaredInUsing(ISymbol symbolInfo)
Expand Down
61 changes: 61 additions & 0 deletions Tests/VB/ExtensionMethodRefPropertyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Threading.Tasks;
using Xunit;

namespace ICSharpCode.CodeConverter.Tests.VB;

public class ExtensionMethodRefPropertyTests : TestRunners.ConverterTestBase
{
[Fact]
public async Task TestExtensionMethodRefPropertyAsync()
{
await TestConversionVisualBasicToCSharpAsync(
@"Imports System.Runtime.CompilerServices
Public Class ExtensionMethodsRefPropertyParameter
Public Property Number As Integer = 3
Public Sub WithExtensionMethod()
Number.NegEx()
End Sub
Public Sub WithMethod()
Neg(Number)
End Sub
End Class

Public Module MathEx
<Extension()>
Public Sub NegEx(ByRef num As Integer)
num = -num
End Sub
Public Sub Neg(ByRef num As Integer)
num = -num
End Sub
End Module", @"
public partial class ExtensionMethodsRefPropertyParameter
{
public int Number { get; set; } = 3;
public void WithExtensionMethod()
{
int argnum = Number;
argnum.NegEx();
Number = argnum;
}
public void WithMethod()
{
int argnum = Number;
MathEx.Neg(ref argnum);
Number = argnum;
}
}

public static partial class MathEx
{
public static void NegEx(this ref int num)
{
num = -num;
}
public static void Neg(ref int num)
{
num = -num;
}
}", incompatibleWithAutomatedCommentTesting: true);
}
}
Loading