Skip to content

Commit edb6ce7

Browse files
committed
Completely unreal improvements to resolver performance (fixes #1166)
1 parent 89807b5 commit edb6ce7

File tree

1 file changed

+165
-55
lines changed

1 file changed

+165
-55
lines changed

Rubberduck.Parsing/Symbols/IdentifierReferenceResolver.cs

Lines changed: 165 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics;
34
using System.Linq;
45
using Antlr4.Runtime;
56
using Rubberduck.Parsing.Grammar;
@@ -8,8 +9,148 @@
89

910
namespace Rubberduck.Parsing.Symbols
1011
{
12+
public class DeclarationFinder
13+
{
14+
private readonly IDictionary<DeclarationType, Declaration[]> _declarationsByType;
15+
private readonly IDictionary<QualifiedModuleName, CommentNode[]> _comments;
16+
private readonly IDictionary<string, Declaration[]> _declarationsByName;
17+
18+
private readonly IReadOnlyList<Declaration> _types;
19+
20+
public DeclarationFinder(IReadOnlyList<Declaration> declarations, IEnumerable<CommentNode> comments)
21+
{
22+
_comments = comments.GroupBy(node => node.QualifiedSelection.QualifiedName)
23+
.ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray());
24+
25+
_declarationsByType = declarations.GroupBy(declaration => declaration.DeclarationType)
26+
.ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray());
27+
28+
_declarationsByName = declarations.GroupBy(declaration => declaration.IdentifierName)
29+
.ToDictionary(grouping => grouping.Key, grouping => grouping.ToArray());
30+
31+
_types = _declarationsByType[DeclarationType.Class].Union(
32+
_declarationsByType[DeclarationType.UserDefinedType]).ToList();
33+
}
34+
35+
private readonly HashSet<Accessibility> _projectScopePublicModifiers =
36+
new HashSet<Accessibility>(new[]
37+
{
38+
Accessibility.Public,
39+
Accessibility.Global,
40+
Accessibility.Friend,
41+
Accessibility.Implicit,
42+
});
43+
44+
public IEnumerable<CommentNode> ModuleComments(QualifiedModuleName module)
45+
{
46+
CommentNode[] result;
47+
if (_comments.TryGetValue(module, out result))
48+
{
49+
return result;
50+
}
51+
52+
return new List<CommentNode>();
53+
}
54+
55+
public IEnumerable<Declaration> MatchTypeName(string name)
56+
{
57+
return _types.Where(declaration => declaration.IdentifierName == name);
58+
}
59+
60+
public IEnumerable<Declaration> MatchName(string name)
61+
{
62+
Declaration[] result;
63+
if (_declarationsByName.TryGetValue(name, out result))
64+
{
65+
return result;
66+
}
67+
68+
return new List<Declaration>();
69+
}
70+
71+
public Declaration FindProject(Declaration currentScope, string name)
72+
{
73+
Declaration result = null;
74+
try
75+
{
76+
result = _declarationsByType[DeclarationType.Project].SingleOrDefault(project =>
77+
(currentScope == null || project.Project == currentScope.Project)
78+
&& project.IdentifierName == name);
79+
}
80+
catch (InvalidOperationException exception)
81+
{
82+
Debug.WriteLine("Multiple matches found for project '{0}'.\n{1}", name, exception);
83+
}
84+
85+
return result;
86+
}
87+
88+
public Declaration FindStdModule(Declaration parent, string name, bool includeBuiltIn = false)
89+
{
90+
Declaration result = null;
91+
try
92+
{
93+
result = _declarationsByType[DeclarationType.Module].SingleOrDefault(declaration =>
94+
declaration.IdentifierName == name
95+
&& (parent == null || parent.Equals(declaration.ParentDeclaration))
96+
&& (includeBuiltIn || !declaration.IsBuiltIn));
97+
}
98+
catch (InvalidOperationException exception)
99+
{
100+
Debug.WriteLine("Multiple matches found for std.module '{0}'.\n{1}", name, exception);
101+
}
102+
103+
return result;
104+
}
105+
106+
public Declaration FindUserDefinedType(Declaration parent, string name, bool includeBuiltIn = false)
107+
{
108+
Declaration result = null;
109+
try
110+
{
111+
result = _declarationsByType[DeclarationType.UserDefinedType].SingleOrDefault(declaration =>
112+
declaration.IdentifierName == name
113+
&& parent == null
114+
? _projectScopePublicModifiers.Contains(declaration.Accessibility)
115+
: parent.Equals(declaration.ParentDeclaration)
116+
&& (includeBuiltIn || !declaration.IsBuiltIn));
117+
}
118+
catch (InvalidOperationException exception)
119+
{
120+
Debug.WriteLine("Multiple matches found for user-defined type '{0}'.\n{1}", name, exception);
121+
}
122+
123+
return result;
124+
}
125+
126+
public Declaration FindClass(Declaration parent, string name, bool includeBuiltIn = false)
127+
{
128+
if (parent == null)
129+
{
130+
throw new ArgumentNullException("parent");
131+
}
132+
133+
Declaration result = null;
134+
try
135+
{
136+
result = _declarationsByType[DeclarationType.Class].SingleOrDefault(declaration =>
137+
declaration.IdentifierName == name
138+
&& parent.Equals(declaration.ParentDeclaration)
139+
&& (includeBuiltIn || !declaration.IsBuiltIn));
140+
}
141+
catch (InvalidOperationException exception)
142+
{
143+
Debug.WriteLine("Multiple matches found for class '{0}'.\n{1}", name, exception);
144+
}
145+
146+
return result;
147+
}
148+
}
149+
11150
public class IdentifierReferenceResolver
12151
{
152+
private readonly DeclarationFinder _declarationFinder;
153+
13154
private enum ContextAccessorType
14155
{
15156
GetValueOrReference,
@@ -41,6 +182,8 @@ private enum ContextAccessorType
41182

42183
public IdentifierReferenceResolver(QualifiedModuleName qualifiedModuleName, IReadOnlyList<Declaration> declarations, IReadOnlyList<CommentNode> comments)
43184
{
185+
_declarationFinder = new DeclarationFinder(declarations, comments);
186+
44187
_qualifiedModuleName = qualifiedModuleName;
45188
_declarations = declarations;
46189
_comments = comments;
@@ -188,7 +331,7 @@ private string FindAnnotations(int line)
188331
return null;
189332
}
190333

191-
var commentAbove = _comments.SingleOrDefault(comment => comment.QualifiedSelection.QualifiedName == _qualifiedModuleName && comment.QualifiedSelection.Selection.EndLine == line - 1);
334+
var commentAbove = _declarationFinder.ModuleComments(_qualifiedModuleName).SingleOrDefault(comment => comment.QualifiedSelection.Selection.EndLine == line - 1);
192335
if (commentAbove != null && commentAbove.CommentText.StartsWith("@"))
193336
{
194337
return commentAbove.CommentText;
@@ -235,12 +378,7 @@ private void ResolveType(VBAParser.ComplexTypeContext context)
235378
private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers)
236379
{
237380
var first = identifiers[0].GetText();
238-
var projectMatch = _currentScope.ProjectName == first
239-
? _declarations.SingleOrDefault(declaration =>
240-
declaration.DeclarationType == DeclarationType.Project
241-
&& declaration.Project == _currentScope.Project // todo: account for project references!
242-
&& declaration.IdentifierName == first)
243-
: null;
381+
var projectMatch = _declarationFinder.FindProject(_currentScope, first);
244382

245383
if (projectMatch != null)
246384
{
@@ -252,22 +390,13 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
252390
// - UDT
253391
if (identifiers.Count == 3)
254392
{
255-
var moduleMatch = _declarations.SingleOrDefault(declaration =>
256-
!declaration.IsBuiltIn && declaration.ParentDeclaration != null
257-
&& declaration.ParentDeclaration.Equals(projectMatch)
258-
&& declaration.DeclarationType == DeclarationType.Module
259-
&& declaration.IdentifierName == identifiers[1].GetText());
260-
393+
var moduleMatch = _declarationFinder.FindStdModule(_currentScope, identifiers[1].GetText());
261394
if (moduleMatch != null)
262395
{
263396
var moduleReference = CreateReference(identifiers[1], moduleMatch);
264397

265398
// 3rd identifier can only be a UDT
266-
var udtMatch = _declarations.SingleOrDefault(declaration =>
267-
!declaration.IsBuiltIn && declaration.ParentDeclaration != null
268-
&& declaration.ParentDeclaration.Equals(moduleMatch)
269-
&& declaration.DeclarationType == DeclarationType.UserDefinedType
270-
&& declaration.IdentifierName == identifiers[2].GetText());
399+
var udtMatch = _declarationFinder.FindUserDefinedType(moduleMatch, identifiers[2].GetText());
271400
if (udtMatch != null)
272401
{
273402
var udtReference = CreateReference(identifiers[2], udtMatch);
@@ -290,12 +419,8 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
290419
projectMatch.AddReference(projectReference);
291420
_alreadyResolved.Add(projectReference.Context);
292421

293-
var match = _declarations.SingleOrDefault(declaration =>
294-
!declaration.IsBuiltIn && declaration.ParentDeclaration != null
295-
&& declaration.ParentDeclaration.Equals(projectMatch)
296-
&& declaration.IdentifierName == identifiers[1].GetText()
297-
&& (declaration.DeclarationType == DeclarationType.Class ||
298-
declaration.DeclarationType == DeclarationType.UserDefinedType));
422+
var match = _declarationFinder.FindClass(projectMatch, identifiers[1].GetText())
423+
?? _declarationFinder.FindUserDefinedType(null, identifiers[1].GetText());
299424
if (match != null)
300425
{
301426
var reference = CreateReference(identifiers[1], match);
@@ -313,22 +438,14 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
313438
// if there are 3 identifiers, type isn't in current project.
314439
if (identifiers.Count != 3)
315440
{
316-
var moduleMatch = _declarations.SingleOrDefault(declaration =>
317-
!declaration.IsBuiltIn && declaration.ParentDeclaration != null
318-
&& declaration.ParentDeclaration.Equals(projectMatch)
319-
&& declaration.DeclarationType == DeclarationType.Module
320-
&& declaration.IdentifierName == identifiers[0].GetText());
321-
441+
442+
var moduleMatch = _declarationFinder.FindStdModule(projectMatch, identifiers[0].GetText());
322443
if (moduleMatch != null)
323444
{
324445
var moduleReference = CreateReference(identifiers[0], moduleMatch);
325446

326447
// 2nd identifier can only be a UDT
327-
var udtMatch = _declarations.SingleOrDefault(declaration =>
328-
!declaration.IsBuiltIn && declaration.ParentDeclaration != null
329-
&& declaration.ParentDeclaration.Equals(moduleMatch)
330-
&& declaration.DeclarationType == DeclarationType.UserDefinedType
331-
&& declaration.IdentifierName == identifiers[1].GetText());
448+
var udtMatch = _declarationFinder.FindUserDefinedType(moduleMatch, identifiers[1].GetText());
332449
if (udtMatch != null)
333450
{
334451
var udtReference = CreateReference(identifiers[1], udtMatch);
@@ -343,18 +460,9 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
343460
}
344461
}
345462

346-
private IEnumerable<Declaration> FindMatchingTypes(string identifier)
347-
{
348-
return _declarations.Where(declaration =>
349-
declaration.IdentifierName == identifier
350-
&& (declaration.DeclarationType == DeclarationType.Class
351-
|| declaration.DeclarationType == DeclarationType.UserDefinedType))
352-
.ToList();
353-
}
354-
355463
private Declaration ResolveInScopeType(string identifier, Declaration scope)
356464
{
357-
var matches = FindMatchingTypes(identifier).ToList();
465+
var matches = _declarationFinder.MatchTypeName(identifier).ToList();
358466
if (matches.Count == 1)
359467
{
360468
return matches.Single();
@@ -377,10 +485,7 @@ private Declaration ResolveInScopeType(string identifier, Declaration scope)
377485

378486
return null;
379487
}
380-
381-
382-
383-
488+
384489
private Declaration ResolveType(Declaration parent)
385490
{
386491
if (parent != null && parent.DeclarationType == DeclarationType.UserDefinedType)
@@ -1019,7 +1124,7 @@ private Declaration FindFunctionOrPropertyGetter(string identifierName, Declarat
10191124
localScope = _currentScope;
10201125
}
10211126

1022-
var matches = _declarations.Where(d => d.IdentifierName == identifierName);
1127+
var matches = _declarationFinder.MatchName(identifierName);
10231128
var parent = matches.SingleOrDefault(item =>
10241129
(item.DeclarationType == DeclarationType.Function || item.DeclarationType == DeclarationType.PropertyGet)
10251130
&& item.Equals(localScope));
@@ -1040,7 +1145,7 @@ private Declaration FindLocalScopeDeclaration(string identifierName, Declaration
10401145
return null;
10411146
}
10421147

1043-
var matches = _declarations.Where(d => d.IdentifierName == identifierName);
1148+
var matches = _declarationFinder.MatchName(identifierName);
10441149

10451150
var results = matches.Where(item =>
10461151
(item.ParentScope == localScope.Scope || (isAssignmentTarget && item.Scope == localScope.Scope))
@@ -1077,7 +1182,7 @@ private Declaration FindModuleScopeDeclaration(string identifierName, Declaratio
10771182
localScope = _currentScope;
10781183
}
10791184

1080-
var matches = _declarations.Where(d => d.IdentifierName == identifierName);
1185+
var matches = _declarationFinder.MatchName(identifierName);
10811186
var result = matches.Where(item =>
10821187
item.ParentScope == localScope.ParentScope
10831188
&& !item.DeclarationType.HasFlag(DeclarationType.Member)
@@ -1102,7 +1207,7 @@ private Declaration FindModuleScopeProcedure(string identifierName, Declaration
11021207
localScope = _currentScope;
11031208
}
11041209

1105-
var matches = _declarations.Where(d => d.IdentifierName == identifierName);
1210+
var matches = _declarationFinder.MatchName(identifierName);
11061211
var result = matches.Where(item =>
11071212
item.Project == localScope.Project
11081213
&& item.ComponentName == localScope.ComponentName
@@ -1141,8 +1246,13 @@ private Declaration FindProjectScopeDeclaration(string identifierName, Declarati
11411246
}
11421247
else
11431248
{
1144-
return result.SingleOrDefault(item => !_moduleTypes.Contains(item.DeclarationType)
1145-
&& item.DeclarationType == (accessorType == ContextAccessorType.GetValueOrReference ? DeclarationType.PropertyGet : item.DeclarationType));
1249+
var temp = result.Where(item => !_moduleTypes.Contains(item.DeclarationType)
1250+
&& item.DeclarationType == (accessorType == ContextAccessorType.GetValueOrReference ? DeclarationType.PropertyGet : item.DeclarationType))
1251+
.ToList();
1252+
if (temp.Count != 1)
1253+
{
1254+
Debug.WriteLine("Ambiguous match in '{0}': '{1}'", localScope == null ? "(unknown)" : localScope.IdentifierName, identifierName);
1255+
}
11461256
}
11471257

11481258
return null;

0 commit comments

Comments
 (0)