Skip to content

Commit 2128bbc

Browse files
committed
Add the MemberAttributesRecoverer
On this class, one can request the attributes of members to be recovered on the next parse. It will do this after the declarations have been resolved. In order to not process information with the attributes still missing, it cancels the current parse right after requesting its own attributes rewrite.
1 parent 5af7fed commit 2128bbc

File tree

5 files changed

+492
-1
lines changed

5 files changed

+492
-1
lines changed

Rubberduck.Parsing/Rewriter/AttributesRewriteSession.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ protected override IExecutableModuleRewriter ModuleRewriter(QualifiedModuleName
2626
protected override bool TryRewriteInternal()
2727
{
2828
//The suspension ensures that only one parse gets executed instead of two for each rewritten module.
29-
var result = _parseManager.OnSuspendParser(this, new[] {ParserState.Ready}, ExecuteAllRewriters);
29+
var result = _parseManager.OnSuspendParser(this, new[] {ParserState.Ready, ParserState.ResolvedDeclarations}, ExecuteAllRewriters);
3030
if(result != SuspensionResult.Completed)
3131
{
3232
Logger.Warn($"Rewriting attribute modules did not succeed. suspension result = {result}");
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Collections.Generic;
2+
using Rubberduck.VBEditor;
3+
4+
namespace Rubberduck.Parsing.Rewriter
5+
{
6+
public interface IMemberAttributeRecoverer
7+
{
8+
void RecoverCurrentMemberAttributesAfterNextParse(IEnumerable<QualifiedMemberName> members);
9+
void RecoverCurrentMemberAttributesAfterNextParse(IEnumerable<QualifiedModuleName> modules);
10+
}
11+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using NLog;
6+
using Rubberduck.Parsing.Symbols;
7+
using Rubberduck.Parsing.VBA;
8+
using Rubberduck.Parsing.VBA.Extensions;
9+
using Rubberduck.VBEditor;
10+
11+
namespace Rubberduck.Parsing.Rewriter
12+
{
13+
public class MemberAttributeRecoverer : IMemberAttributeRecoverer
14+
{
15+
private readonly IDeclarationFinderProvider _declarationFinderProvider;
16+
private readonly IParseManager _parseManager;
17+
private readonly IAttributesUpdater _attributesUpdater;
18+
private IRewritingManager _rewritingManager;
19+
20+
private readonly
21+
IDictionary<QualifiedModuleName, IDictionary<string, HashSet<AttributeNode>>> _attributesToRecover
22+
= new Dictionary<QualifiedModuleName, IDictionary<string, HashSet<AttributeNode>>>();
23+
24+
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
25+
26+
public MemberAttributeRecoverer(IDeclarationFinderProvider declarationFinderProvider,
27+
IParseManager parseManager, IAttributesUpdater attributesUpdater)
28+
{
29+
_declarationFinderProvider = declarationFinderProvider;
30+
_parseManager = parseManager;
31+
_attributesUpdater = attributesUpdater;
32+
}
33+
34+
//This needs to be property injected because this class will be constructor injected into the RewritingManager that needs to set itself as the dependency here.
35+
public IRewritingManager RewritingManager
36+
{
37+
set => _rewritingManager = value;
38+
}
39+
40+
public void RecoverCurrentMemberAttributesAfterNextParse(IEnumerable<QualifiedMemberName> members)
41+
{
42+
var declarationsByModule = MemberDeclarationsByModule(members);
43+
RecoverCurrentMemberAttributesAfterNextParse(declarationsByModule);
44+
}
45+
46+
private IDictionary<QualifiedModuleName, IEnumerable<Declaration>> MemberDeclarationsByModule(IEnumerable<QualifiedMemberName> members)
47+
{
48+
var membersByModule = members.GroupBy(member => member.QualifiedModuleName)
49+
.ToDictionary(group => group.Key, group => group.ToHashSet());
50+
var declarationFinder = _declarationFinderProvider.DeclarationFinder;
51+
var memberDeclarationsPerModule = new Dictionary<QualifiedModuleName, IEnumerable<Declaration>>();
52+
foreach (var module in membersByModule.Keys)
53+
{
54+
var memberDeclarationsInModule = declarationFinder.Members(module)
55+
.Where(declaration => membersByModule[module].Contains(declaration.QualifiedName)
56+
&& declaration.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Module));
57+
memberDeclarationsPerModule.Add(module, memberDeclarationsInModule);
58+
}
59+
60+
return memberDeclarationsPerModule;
61+
}
62+
63+
private void RecoverCurrentMemberAttributesAfterNextParse(
64+
IDictionary<QualifiedModuleName, IEnumerable<Declaration>> declarationsByModule)
65+
{
66+
SaveAttributesToRecover(declarationsByModule);
67+
68+
if (_attributesToRecover.Any())
69+
{
70+
PrimeRecoveryOfAttributes();
71+
}
72+
}
73+
74+
private void SaveAttributesToRecover(IDictionary<QualifiedModuleName, IEnumerable<Declaration>> declarationsByModule)
75+
{
76+
_attributesToRecover.Clear();
77+
foreach (var module in declarationsByModule.Keys)
78+
{
79+
var attributesByMember = declarationsByModule[module]
80+
.Where(decl => decl.Attributes.Any())
81+
.ToDictionary(
82+
decl => decl.IdentifierName,
83+
decl => (HashSet<AttributeNode>)decl.Attributes);
84+
_attributesToRecover.Add(module, attributesByMember);
85+
}
86+
}
87+
88+
private void PrimeRecoveryOfAttributes()
89+
{
90+
_parseManager.StateChanged += ExecuteRecoveryOfAttributes;
91+
}
92+
93+
private void ExecuteRecoveryOfAttributes(object sender, ParserStateEventArgs e)
94+
{
95+
if (e.State != ParserState.ResolvedDeclarations)
96+
{
97+
return;
98+
}
99+
100+
StopRecoveringAttributesOnNextParse();
101+
CancelTheCurrentParse();
102+
103+
var rewriteSession = _rewritingManager.CheckOutAttributesSession();
104+
foreach (var module in _attributesToRecover.Keys)
105+
{
106+
RecoverAttributes(rewriteSession, module, _attributesToRecover[module]);
107+
}
108+
109+
Task.Run(() => rewriteSession.TryRewrite());
110+
111+
EndTheCurrentParse(e.Token);
112+
}
113+
114+
private void StopRecoveringAttributesOnNextParse()
115+
{
116+
_parseManager.StateChanged -= ExecuteRecoveryOfAttributes;
117+
}
118+
119+
private void CancelTheCurrentParse()
120+
{
121+
_parseManager.OnParseCancellationRequested(this);
122+
}
123+
124+
private void RecoverAttributes(IRewriteSession rewriteSession, QualifiedModuleName module, IDictionary<string, HashSet<AttributeNode>> attributesByMember)
125+
{
126+
var membersWithAttributesToRecover = attributesByMember.Keys.ToHashSet();
127+
var declarationFinder = _declarationFinderProvider.DeclarationFinder;
128+
var declarationsWithAttributesToRecover = declarationFinder.Members(module)
129+
.Where(decl => membersWithAttributesToRecover.Contains(decl.IdentifierName)
130+
&& decl.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Module))
131+
.ToList();
132+
133+
if (membersWithAttributesToRecover.Count != declarationsWithAttributesToRecover.Count)
134+
{
135+
LogFailureToRecoverAllAttributes(module, membersWithAttributesToRecover, declarationsWithAttributesToRecover);
136+
}
137+
138+
foreach (var declaration in declarationsWithAttributesToRecover)
139+
{
140+
RecoverAttributes(rewriteSession, declaration, attributesByMember[declaration.IdentifierName]);
141+
}
142+
}
143+
144+
private void LogFailureToRecoverAllAttributes(QualifiedModuleName module, IEnumerable<string> membersWithAttributesToRecover, List<Declaration> declarationsWithAttributesToRecover)
145+
{
146+
_logger.Warn("Could not recover the attributes for all members because one or more members could no longer be found.");
147+
148+
var membersWithoutDeclarations = membersWithAttributesToRecover.ToHashSet();
149+
membersWithoutDeclarations.ExceptWith(declarationsWithAttributesToRecover.Select(decl => decl.IdentifierName));
150+
foreach (var member in membersWithoutDeclarations)
151+
{
152+
_logger.Trace($"Could not recover the attributes for member {member} in module {module} because a member of that name exists no longer.");
153+
}
154+
}
155+
156+
private void RecoverAttributes(IRewriteSession rewriteSession, Declaration declaration, IEnumerable<AttributeNode> attributes)
157+
{
158+
foreach (var attribute in attributes)
159+
{
160+
if (!declaration.Attributes.Contains(attribute))
161+
{
162+
_attributesUpdater.AddAttribute(rewriteSession, declaration, attribute.Name, attribute.Values);
163+
}
164+
}
165+
}
166+
167+
private void EndTheCurrentParse(CancellationToken currentCancellationToken)
168+
{
169+
currentCancellationToken.ThrowIfCancellationRequested();
170+
}
171+
172+
public void RecoverCurrentMemberAttributesAfterNextParse(IEnumerable<QualifiedModuleName> modules)
173+
{
174+
var declarationsByModule = MemberDeclarationsByModule(modules);
175+
RecoverCurrentMemberAttributesAfterNextParse(declarationsByModule);
176+
}
177+
178+
private IDictionary<QualifiedModuleName, IEnumerable<Declaration>> MemberDeclarationsByModule(IEnumerable<QualifiedModuleName> modules)
179+
{
180+
var declarationFinder = _declarationFinderProvider.DeclarationFinder;
181+
var declarationsByModule = modules.ToDictionary(
182+
module => module,
183+
module => declarationFinder.Members(module)
184+
.Where(decl => !decl.DeclarationType.HasFlag(DeclarationType.Module)
185+
&& decl.ParentScopeDeclaration.DeclarationType.HasFlag(DeclarationType.Module)));
186+
return declarationsByModule;
187+
}
188+
}
189+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using Rubberduck.Parsing.VBA;
5+
6+
namespace RubberduckTests.Mocks
7+
{
8+
public class ParserStateAwaiter
9+
{
10+
private readonly EventWaitHandle _waitHandle;
11+
private readonly ICollection<ParserState> _statesToAwait;
12+
13+
public ParserStateAwaiter(EventWaitHandle waitHandle, ICollection<ParserState> statesToAwait)
14+
{
15+
_waitHandle = waitHandle;
16+
_statesToAwait = statesToAwait;
17+
}
18+
19+
public void ParserStateHandler(object requestor, ParserStateEventArgs e)
20+
{
21+
if (_statesToAwait.Contains(e.State))
22+
{
23+
_waitHandle.Set();
24+
}
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)