Skip to content

Commit c86a359

Browse files
committed
Add ReplacePrivateUDTMemberReferencesRefactoringAction
1 parent 925a245 commit c86a359

File tree

7 files changed

+576
-0
lines changed

7 files changed

+576
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+

2+
namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences
3+
{
4+
public struct PrivateUDTMemberReferenceReplacementExpressions
5+
{
6+
public PrivateUDTMemberReferenceReplacementExpressions(string memberAccessExpression)
7+
{
8+
MemberAccesExpression = memberAccessExpression;
9+
_localReferenceExpression = memberAccessExpression;
10+
}
11+
12+
public string MemberAccesExpression { set; get; }
13+
14+
private string _localReferenceExpression;
15+
public string LocalReferenceExpression
16+
{
17+
set => _localReferenceExpression = value;
18+
get => _localReferenceExpression ?? MemberAccesExpression;
19+
}
20+
}
21+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Rubberduck.Parsing.Symbols;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences
6+
{
7+
public class ReplacePrivateUDTMemberReferencesModel : IRefactoringModel
8+
{
9+
private Dictionary<(VariableDeclaration, Declaration), PrivateUDTMemberReferenceReplacementExpressions> _udtTargets
10+
= new Dictionary<(VariableDeclaration, Declaration), PrivateUDTMemberReferenceReplacementExpressions>();
11+
12+
private Dictionary<VariableDeclaration, UserDefinedTypeInstance> _fieldToUserDefinedTypeInstance;
13+
14+
public ReplacePrivateUDTMemberReferencesModel(Dictionary<VariableDeclaration, UserDefinedTypeInstance> fieldToUserDefinedTypeInstance, IEnumerable<Declaration> userDefinedTypeMembers)
15+
{
16+
_fieldToUserDefinedTypeInstance = fieldToUserDefinedTypeInstance;
17+
_udtMembers = userDefinedTypeMembers.ToList();
18+
}
19+
20+
public IReadOnlyCollection<VariableDeclaration> Targets => _fieldToUserDefinedTypeInstance.Keys;
21+
22+
private List<Declaration> _udtMembers;
23+
public IReadOnlyCollection<Declaration> UDTMembers => _udtMembers;
24+
25+
public UserDefinedTypeInstance UserDefinedTypeInstance(VariableDeclaration field)
26+
=> _fieldToUserDefinedTypeInstance[field];
27+
28+
public void AssignUDTMemberReferenceExpressions(VariableDeclaration field, Declaration udtMember, PrivateUDTMemberReferenceReplacementExpressions expressions)
29+
{
30+
_udtTargets.Add((field,udtMember), expressions);
31+
}
32+
33+
public (bool HasValue, string Expression) LocalReferenceExpression(VariableDeclaration field, Declaration udtMember)
34+
{
35+
if (_udtTargets.TryGetValue((field, udtMember), out var result))
36+
{
37+
return (true, result.LocalReferenceExpression);
38+
}
39+
return (false, null);
40+
}
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Rubberduck.Parsing.Symbols;
2+
using Rubberduck.Parsing.VBA;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
6+
namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences
7+
{
8+
public interface IReplacePrivateUDTMemberReferencesModelFactory
9+
{
10+
ReplacePrivateUDTMemberReferencesModel Create(IEnumerable<VariableDeclaration> targets );
11+
}
12+
13+
public class ReplacePrivateUDTMemberReferencesModelFactory : IReplacePrivateUDTMemberReferencesModelFactory
14+
{
15+
private readonly IDeclarationFinderProvider _declarationFinderProvider;
16+
public ReplacePrivateUDTMemberReferencesModelFactory(IDeclarationFinderProvider declarationFinderProvider)
17+
{
18+
_declarationFinderProvider = declarationFinderProvider;
19+
}
20+
21+
public ReplacePrivateUDTMemberReferencesModel Create(IEnumerable<VariableDeclaration> targets)
22+
{
23+
var allUDTMembers = new List<Declaration>();
24+
var fieldsToUDTMembers = new Dictionary<VariableDeclaration, IEnumerable<Declaration>>();
25+
foreach (var target in targets)
26+
{
27+
var udtMembers = _declarationFinderProvider.DeclarationFinder.UserDeclarations(DeclarationType.UserDefinedTypeMember)
28+
.Where(udtm => udtm.ParentDeclaration == target.AsTypeDeclaration);
29+
30+
allUDTMembers.AddRange(udtMembers);
31+
fieldsToUDTMembers.Add(target as VariableDeclaration, udtMembers);
32+
}
33+
34+
var fieldToUDTInstance = new Dictionary<VariableDeclaration, UserDefinedTypeInstance>();
35+
foreach (var fieldToUDTMembers in fieldsToUDTMembers)
36+
{
37+
fieldToUDTInstance.Add(fieldToUDTMembers.Key, new UserDefinedTypeInstance(fieldToUDTMembers.Key, fieldToUDTMembers.Value));
38+
}
39+
40+
return new ReplacePrivateUDTMemberReferencesModel(fieldToUDTInstance, allUDTMembers.Distinct());
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Parsing;
3+
using Rubberduck.Parsing.Grammar;
4+
using Rubberduck.Parsing.Rewriter;
5+
using Rubberduck.Parsing.Symbols;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
9+
namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences
10+
{
11+
/// <summary>
12+
/// Replaces UserDefinedTypeMember <c>IdentifierReference</c>s of a Private <c>UserDefinedType</c>
13+
/// with a Property <c>IdentifierReference</c>.
14+
/// </summary>
15+
public class ReplacePrivateUDTMemberReferencesRefactoringAction : CodeOnlyRefactoringActionBase<ReplacePrivateUDTMemberReferencesModel>
16+
{
17+
private Dictionary<IdentifierReference, (ParserRuleContext, string)> IdentifierReplacements { get; } = new Dictionary<IdentifierReference, (ParserRuleContext, string)>();
18+
19+
public ReplacePrivateUDTMemberReferencesRefactoringAction(IRewritingManager rewritingManager)
20+
: base(rewritingManager)
21+
{ }
22+
23+
public override void Refactor(ReplacePrivateUDTMemberReferencesModel model, IRewriteSession rewriteSession)
24+
{
25+
if (!(model.UDTMembers?.Any() ?? false))
26+
{
27+
return;
28+
}
29+
30+
foreach (var target in model.Targets)
31+
{
32+
SetRewriteContent(target, model);
33+
}
34+
35+
RewriteReferences(rewriteSession);
36+
}
37+
38+
private void SetRewriteContent(VariableDeclaration target, ReplacePrivateUDTMemberReferencesModel model)
39+
{
40+
var udtInstance = model.UserDefinedTypeInstance(target);
41+
foreach (var idRef in udtInstance.UDTMemberReferences)
42+
{
43+
var internalExpression = model.LocalReferenceExpression(target, idRef.Declaration);
44+
if (internalExpression.HasValue)
45+
{
46+
SetUDTMemberReferenceRewriteContent(target, idRef, internalExpression.Expression);
47+
}
48+
}
49+
}
50+
51+
private void SetUDTMemberReferenceRewriteContent(VariableDeclaration instanceField, IdentifierReference idRef, string replacementText, bool moduleQualify = false)
52+
{
53+
if (idRef.Context.TryGetAncestor<VBAParser.MemberAccessExprContext>(out var maec))
54+
{
55+
if (maec.TryGetChildContext<VBAParser.MemberAccessExprContext>(out var childMaec))
56+
{
57+
if (childMaec.TryGetChildContext<VBAParser.SimpleNameExprContext>(out var smp))
58+
{
59+
AddIdentifierReplacement(idRef, maec, $"{smp.GetText()}.{replacementText}");
60+
}
61+
}
62+
else if (maec.TryGetChildContext<VBAParser.WithMemberAccessExprContext>(out var wm))
63+
{
64+
AddIdentifierReplacement(idRef, maec, $".{replacementText}");
65+
}
66+
else
67+
{
68+
AddIdentifierReplacement(idRef, maec, replacementText);
69+
}
70+
}
71+
else if (idRef.Context.TryGetAncestor<VBAParser.WithMemberAccessExprContext>(out var wmac))
72+
{
73+
AddIdentifierReplacement(idRef, wmac, replacementText);
74+
}
75+
}
76+
77+
private void AddIdentifierReplacement(IdentifierReference idRef, ParserRuleContext context, string replacementText)
78+
{
79+
if (IdentifierReplacements.ContainsKey(idRef))
80+
{
81+
IdentifierReplacements[idRef] = (context, replacementText);
82+
return;
83+
}
84+
IdentifierReplacements.Add(idRef, (context, replacementText));
85+
}
86+
87+
private void RewriteReferences(IRewriteSession rewriteSession)
88+
{
89+
foreach (var replacement in IdentifierReplacements)
90+
{
91+
(ParserRuleContext Context, string Text) = replacement.Value;
92+
var rewriter = rewriteSession.CheckOutModuleRewriter(replacement.Key.QualifiedModuleName);
93+
rewriter.Replace(Context, Text);
94+
}
95+
}
96+
}
97+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using Antlr4.Runtime;
2+
using Rubberduck.Parsing;
3+
using Rubberduck.Parsing.Grammar;
4+
using Rubberduck.Parsing.Symbols;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Diagnostics;
8+
using System.Linq;
9+
10+
namespace Rubberduck.Refactorings.ReplacePrivateUDTMemberReferences
11+
{
12+
public class UserDefinedTypeInstance
13+
{
14+
public UserDefinedTypeInstance(VariableDeclaration field, IEnumerable<Declaration> udtMembers)
15+
{
16+
if (!(field.AsTypeDeclaration?.DeclarationType.HasFlag(DeclarationType.UserDefinedType) ?? false))
17+
{
18+
throw new ArgumentException();
19+
}
20+
21+
InstanceField = field;
22+
_udtMemberReferences = udtMembers.SelectMany(m => m.References)
23+
.Where(rf => IsRelatedReference(rf, InstanceField.References)).ToList();
24+
}
25+
26+
public VariableDeclaration InstanceField { get; }
27+
28+
public string UserDefinedTypeIdentifier => InstanceField.AsTypeDeclaration.IdentifierName;
29+
30+
private List<IdentifierReference> _udtMemberReferences;
31+
public IReadOnlyCollection<IdentifierReference> UDTMemberReferences => _udtMemberReferences;
32+
33+
private bool IsRelatedReference(IdentifierReference idRef, IEnumerable<IdentifierReference> fieldReferences)
34+
{
35+
if (idRef.Context.TryGetAncestor<VBAParser.WithMemberAccessExprContext>(out var wmac))
36+
{
37+
var goalContext = wmac.GetAncestor<VBAParser.WithStmtContext>();
38+
return fieldReferences.Any(rf => HasSameAncestor<VBAParser.WithStmtContext>(rf, goalContext));
39+
}
40+
else if (idRef.Context.TryGetAncestor<VBAParser.MemberAccessExprContext>(out var memberAccessExprContext))
41+
{
42+
return fieldReferences.Any(rf => HasSameAncestor<VBAParser.MemberAccessExprContext>(rf, memberAccessExprContext));
43+
}
44+
throw new ArgumentOutOfRangeException();
45+
}
46+
47+
private bool HasSameAncestor<T>(IdentifierReference idRef, ParserRuleContext goalContext) where T : ParserRuleContext
48+
{
49+
Debug.Assert(goalContext != null);
50+
Debug.Assert(goalContext is VBAParser.MemberAccessExprContext || goalContext is VBAParser.WithStmtContext);
51+
52+
var guard = 0;
53+
var accessExprContext = idRef.Context.GetAncestor<T>();
54+
while (accessExprContext != null && ++guard < 100)
55+
{
56+
var prCtxt = accessExprContext as ParserRuleContext;
57+
if (prCtxt == goalContext)
58+
{
59+
return true;
60+
}
61+
accessExprContext = accessExprContext.GetAncestor<T>();
62+
}
63+
64+
Debug.Assert(guard < 100);
65+
return false;
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)