Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Differentiate between statics in setters and getters #1124

Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions CodeConverter/CSharp/ExpressionNodeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,10 @@ public override async Task<CSharpSyntaxNode> VisitIdentifierName(VBasic.Syntax.I

var sym = GetSymbolInfoInDocument<ISymbol>(node);
if (sym is ILocalSymbol) {
if (sym.IsStatic && sym.ContainingSymbol is IMethodSymbol m && m.AssociatedSymbol is IPropertySymbol) {
qualifiedIdentifier = qualifiedIdentifier.WithParentPropertyAccessorKind(m.MethodKind.ToString());
}

var vbMethodBlock = node.Ancestors().OfType<VBasic.Syntax.MethodBlockBaseSyntax>().FirstOrDefault();
if (vbMethodBlock != null &&
vbMethodBlock.MustReturn() &&
Expand Down
5 changes: 4 additions & 1 deletion CodeConverter/CSharp/HoistedFieldFromVbStaticVariable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ internal class HoistedFieldFromVbStaticVariable : IHoistedNode
{
public string OriginalMethodName { get; }
public string OriginalVariableName { get; }
public string OriginalParentAccessorKind { get; }
public ExpressionSyntax Initializer { get; }
public TypeSyntax Type { get; }
public bool IsStatic { get; }

public HoistedFieldFromVbStaticVariable(string originalMethodName, string originalVariableName, ExpressionSyntax initializer, TypeSyntax type, bool isStatic)
public HoistedFieldFromVbStaticVariable(string originalMethodName, string originalVariableName, string originalParentAccessorKind, ExpressionSyntax initializer, TypeSyntax type, bool isStatic)
{
OriginalMethodName = originalMethodName;
OriginalVariableName = originalVariableName;
OriginalParentAccessorKind = originalParentAccessorKind;
TymurGubayev marked this conversation as resolved.
Show resolved Hide resolved
Initializer = initializer;
Type = type;
IsStatic = isStatic;
}

public string FieldName => OriginalMethodName != null ? $"_{OriginalMethodName}_{OriginalVariableName}" : $"_{OriginalVariableName}";
public string PrefixedOriginalVariableName => PerScopeState.GetPrefixedName(OriginalParentAccessorKind, OriginalVariableName);
}
4 changes: 3 additions & 1 deletion CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public override async Task<SyntaxList<StatementSyntax>> VisitLocalDeclarationSta
string methodName;
SyntaxTokenList methodModifiers;

string parentAccessorKind = null;
if (_methodNode is VBSyntax.MethodBlockSyntax methodBlock) {
var methodStatement = methodBlock.BlockStatement as VBSyntax.MethodStatementSyntax;
methodModifiers = methodStatement.Modifiers;
Expand All @@ -120,13 +121,14 @@ public override async Task<SyntaxList<StatementSyntax>> VisitLocalDeclarationSta
} else if (_methodNode is VBSyntax.AccessorBlockSyntax accessorBlock) {
var propertyBlock = accessorBlock.Parent as VBSyntax.PropertyBlockSyntax;
methodName = propertyBlock.PropertyStatement.Identifier.Text;
parentAccessorKind = accessorBlock.IsKind(VBasic.SyntaxKind.GetAccessorBlock) ? "PropertyGet" : "PropertySet";
methodModifiers = propertyBlock.PropertyStatement.Modifiers;
} else {
throw new NotImplementedException(_methodNode.GetType() + " not implemented!");
}

var isVbShared = methodModifiers.Any(a => a.IsKind(VBasic.SyntaxKind.SharedKeyword));
_perScopeState.HoistToTopLevel(new HoistedFieldFromVbStaticVariable(methodName, variable.Identifier.Text, initializeValue, decl.Declaration.Type, isVbShared));
_perScopeState.HoistToTopLevel(new HoistedFieldFromVbStaticVariable(methodName, variable.Identifier.Text, parentAccessorKind, initializeValue, decl.Declaration.Type, isVbShared));
}
} else {
var shouldPullVariablesBeforeLoop = _perScopeState.IsInsideLoop() && declarator.Initializer is null && declarator.AsClause is not VBSyntax.AsNewClauseSyntax;
Expand Down
26 changes: 21 additions & 5 deletions CodeConverter/CSharp/PerScopeState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,22 @@ public async Task<SyntaxList<MemberDeclarationSyntax>> CreateVbStaticFieldsAsync
var declarations = new List<FieldDeclarationSyntax>();

var fieldInfo = GetFields();
var newNames = fieldInfo.ToDictionary(f => f.OriginalVariableName, f =>
var newNames = fieldInfo.ToDictionary(f => f.PrefixedOriginalVariableName, f =>
NameGenerator.CS.GetUniqueVariableNameInScope(semanticModel, generatedNames, typeNode, f.FieldName)
);
foreach (var field in fieldInfo) {
var decl = (field.Initializer != null)
? CommonConversions.CreateVariableDeclarationAndAssignment(newNames[field.OriginalVariableName], field.Initializer, field.Type)
: SyntaxFactory.VariableDeclaration(field.Type, SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(newNames[field.OriginalVariableName])));
? CommonConversions.CreateVariableDeclarationAndAssignment(newNames[field.PrefixedOriginalVariableName], field.Initializer, field.Type)
: SyntaxFactory.VariableDeclaration(field.Type, SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(newNames[field.PrefixedOriginalVariableName])));
var modifiers = new List<SyntaxToken> { CS.SyntaxFactory.Token(SyntaxKind.PrivateKeyword) };
if (field.IsStatic || namedTypeSymbol.IsModuleType()) {
modifiers.Add(CS.SyntaxFactory.Token(SyntaxKind.StaticKeyword));
}
declarations.Add(CS.SyntaxFactory.FieldDeclaration(CS.SyntaxFactory.List<AttributeListSyntax>(), CS.SyntaxFactory.TokenList(modifiers), decl));
var fieldDecl = CS.SyntaxFactory.FieldDeclaration(CS.SyntaxFactory.List<AttributeListSyntax>(), CS.SyntaxFactory.TokenList(modifiers), decl);
if (!string.IsNullOrEmpty(field.OriginalParentAccessorKind)) {
fieldDecl = fieldDecl.WithParentPropertyAccessorKind(field.OriginalParentAccessorKind);
}
declarations.Add(fieldDecl);
}

var statementsWithUpdatedIds = ReplaceNames(declarations.Concat(csNodes), newNames);
Expand All @@ -185,11 +189,23 @@ public static IEnumerable<T> ReplaceNames<T>(IEnumerable<T> csNodes, Dictionary<
public static T ReplaceNames<T>(T csNode, Dictionary<string, string> newNames) where T : SyntaxNode
{
return csNode.ReplaceNodes(csNode.DescendantNodesAndSelf().OfType<IdentifierNameSyntax>(), (_, idns) => {
if (newNames.TryGetValue(idns.Identifier.ValueText, out var newName)) {
if (TryGetValue(newNames, idns, out var newName)) {
return idns.WithoutAnnotations(AdditionalLocalAnnotation).WithIdentifier(CS.SyntaxFactory.Identifier(newName));
}
return idns;
});

static bool TryGetValue(Dictionary<string, string> newNames, IdentifierNameSyntax idns, out string newName)
{
var ann = idns.GetAnnotations(AnnotationConstants.ParentPropertyAccessorKindAnnotation).FirstOrDefault();
var key = GetPrefixedName(ann?.Data, idns.Identifier.ValueText);
return newNames.TryGetValue(key, out newName);
}
}

internal static string GetPrefixedName(string prefix, string name)
{
return string.IsNullOrEmpty(prefix) ? name : $"<{prefix}>.{name}";
}

public IEnumerable<StatementSyntax> ConvertExit(VBasic.SyntaxKind vbBlockKeywordKind)
Expand Down
6 changes: 6 additions & 0 deletions CodeConverter/Common/AnnotationConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal static class AnnotationConstants
public const string SourceEndLineAnnotationKind = "CodeConverter.SourceEndLine";
public const string LeadingTriviaAlreadyMappedAnnotation = nameof(CodeConverter) + "." + nameof(LeadingTriviaAlreadyMappedAnnotation);
public const string TrailingTriviaAlreadyMappedAnnotation = nameof(CodeConverter) + "." + nameof(TrailingTriviaAlreadyMappedAnnotation);
public const string ParentPropertyAccessorKindAnnotation = nameof(CodeConverter) + "." + nameof(ParentPropertyAccessorKindAnnotation);

private static string AsString(LinePosition position)
{
Expand Down Expand Up @@ -40,4 +41,9 @@ public static SyntaxAnnotation SourceEndLine(FileLinePositionSpan origLinespan)
{
return new SyntaxAnnotation(SourceEndLineAnnotationKind, origLinespan.EndLinePosition.Line.ToString(CultureInfo.InvariantCulture));
}

public static SyntaxAnnotation ParentPropertyAccessorKind(string accessorKind)
{
return new SyntaxAnnotation(ParentPropertyAccessorKindAnnotation, accessorKind);
}
}
5 changes: 5 additions & 0 deletions CodeConverter/Util/SyntaxNodeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ private static T WithoutSourceMappingNonRecursive<T>(this T node) where T : Synt
return node.WithoutAnnotations(AnnotationConstants.SourceStartLineAnnotationKind).WithoutAnnotations(AnnotationConstants.SourceEndLineAnnotationKind);
}

public static T WithParentPropertyAccessorKind<T>(this T node, string accessorKind) where T : SyntaxNode
{
return node.WithAdditionalAnnotations(AnnotationConstants.ParentPropertyAccessorKind(accessorKind));
}

private static bool IsBlockParent(SyntaxNode converted, SyntaxToken lastCsConvertedToken)
{
return lastCsConvertedToken.Parent == converted || lastCsConvertedToken.Parent is BlockSyntax b && b.Parent == converted;
Expand Down
39 changes: 39 additions & 0 deletions Tests/CSharp/MemberTests/MemberTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,45 @@ public void set_Prop(int i, string value)
}", incompatibleWithAutomatedCommentTesting: true);// Known bug: Additional declarations don't get comments correctly converted
}

[Fact]
public async Task StaticLocalsInPropertyGetterAndSetterAsync()
{
await TestConversionVisualBasicToCSharpAsync(
@"
Public Property Prop As String
Get
Static b As Boolean
b = True
End Get

Set(ByVal s As String)
Static b As Boolean
b = False
End Set
End Property
", @"
internal partial class SurroundingClass
{
private bool _Prop_b;
private bool _Prop_b1;

public string Prop
{
get
{
_Prop_b = true;
return default;
}

set
{
_Prop_b1 = false;
}
}

}");
}

[Fact]
public async Task TestReadOnlyAndWriteOnlyParametrizedPropertyAsync()
{
Expand Down
Loading