From 2208e30823e3fea6f38d7b4a4784602ccb089a04 Mon Sep 17 00:00:00 2001 From: Timur Kelman Date: Tue, 23 Jul 2024 18:16:52 +0200 Subject: [PATCH] differentiate between statics in setters and getters --- CodeConverter/CSharp/ExpressionNodeVisitor.cs | 4 +++ .../HoistedFieldFromVbStaticVariable.cs | 5 +++- .../MethodBodyExecutableStatementVisitor.cs | 4 ++- CodeConverter/CSharp/PerScopeState.cs | 26 +++++++++++++++---- CodeConverter/Common/AnnotationConstants.cs | 6 +++++ CodeConverter/Util/SyntaxNodeExtensions.cs | 5 ++++ 6 files changed, 43 insertions(+), 7 deletions(-) diff --git a/CodeConverter/CSharp/ExpressionNodeVisitor.cs b/CodeConverter/CSharp/ExpressionNodeVisitor.cs index 813f839b1..2ebd978a6 100644 --- a/CodeConverter/CSharp/ExpressionNodeVisitor.cs +++ b/CodeConverter/CSharp/ExpressionNodeVisitor.cs @@ -1544,6 +1544,10 @@ public override async Task VisitIdentifierName(VBasic.Syntax.I var sym = GetSymbolInfoInDocument(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().FirstOrDefault(); if (vbMethodBlock != null && vbMethodBlock.MustReturn() && diff --git a/CodeConverter/CSharp/HoistedFieldFromVbStaticVariable.cs b/CodeConverter/CSharp/HoistedFieldFromVbStaticVariable.cs index b7e61a402..dbbfffc21 100644 --- a/CodeConverter/CSharp/HoistedFieldFromVbStaticVariable.cs +++ b/CodeConverter/CSharp/HoistedFieldFromVbStaticVariable.cs @@ -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; Initializer = initializer; Type = type; IsStatic = isStatic; } public string FieldName => OriginalMethodName != null ? $"_{OriginalMethodName}_{OriginalVariableName}" : $"_{OriginalVariableName}"; + public string PrefixedOriginalVariableName => PerScopeState.GetPrefixedName(OriginalParentAccessorKind, OriginalVariableName); } \ No newline at end of file diff --git a/CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs b/CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs index 9c23f6c6a..672b5f977 100644 --- a/CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs +++ b/CodeConverter/CSharp/MethodBodyExecutableStatementVisitor.cs @@ -110,6 +110,7 @@ public override async Task> VisitLocalDeclarationSta string methodName; SyntaxTokenList methodModifiers; + string parentAccessorKind = null; if (_methodNode is VBSyntax.MethodBlockSyntax methodBlock) { var methodStatement = methodBlock.BlockStatement as VBSyntax.MethodStatementSyntax; methodModifiers = methodStatement.Modifiers; @@ -120,13 +121,14 @@ public override async Task> 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; diff --git a/CodeConverter/CSharp/PerScopeState.cs b/CodeConverter/CSharp/PerScopeState.cs index 9a467a4e5..65866da22 100644 --- a/CodeConverter/CSharp/PerScopeState.cs +++ b/CodeConverter/CSharp/PerScopeState.cs @@ -157,18 +157,22 @@ public async Task> CreateVbStaticFieldsAsync var declarations = new List(); 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 { 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(), CS.SyntaxFactory.TokenList(modifiers), decl)); + var fieldDecl = CS.SyntaxFactory.FieldDeclaration(CS.SyntaxFactory.List(), 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); @@ -185,11 +189,23 @@ public static IEnumerable ReplaceNames(IEnumerable csNodes, Dictionary< public static T ReplaceNames(T csNode, Dictionary newNames) where T : SyntaxNode { return csNode.ReplaceNodes(csNode.DescendantNodesAndSelf().OfType(), (_, 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 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 ConvertExit(VBasic.SyntaxKind vbBlockKeywordKind) diff --git a/CodeConverter/Common/AnnotationConstants.cs b/CodeConverter/Common/AnnotationConstants.cs index d6b16a61d..125021a02 100644 --- a/CodeConverter/Common/AnnotationConstants.cs +++ b/CodeConverter/Common/AnnotationConstants.cs @@ -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) { @@ -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); + } } \ No newline at end of file diff --git a/CodeConverter/Util/SyntaxNodeExtensions.cs b/CodeConverter/Util/SyntaxNodeExtensions.cs index ac4ba8f29..0072c2827 100644 --- a/CodeConverter/Util/SyntaxNodeExtensions.cs +++ b/CodeConverter/Util/SyntaxNodeExtensions.cs @@ -173,6 +173,11 @@ private static T WithoutSourceMappingNonRecursive(this T node) where T : Synt return node.WithoutAnnotations(AnnotationConstants.SourceStartLineAnnotationKind).WithoutAnnotations(AnnotationConstants.SourceEndLineAnnotationKind); } + public static T WithParentPropertyAccessorKind(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;