1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Diagnostics ;
3
4
using System . Linq ;
4
5
using Antlr4 . Runtime ;
5
6
using Rubberduck . Parsing . Grammar ;
8
9
9
10
namespace Rubberduck . Parsing . Symbols
10
11
{
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
+
11
150
public class IdentifierReferenceResolver
12
151
{
152
+ private readonly DeclarationFinder _declarationFinder ;
153
+
13
154
private enum ContextAccessorType
14
155
{
15
156
GetValueOrReference ,
@@ -41,6 +182,8 @@ private enum ContextAccessorType
41
182
42
183
public IdentifierReferenceResolver ( QualifiedModuleName qualifiedModuleName , IReadOnlyList < Declaration > declarations , IReadOnlyList < CommentNode > comments )
43
184
{
185
+ _declarationFinder = new DeclarationFinder ( declarations , comments ) ;
186
+
44
187
_qualifiedModuleName = qualifiedModuleName ;
45
188
_declarations = declarations ;
46
189
_comments = comments ;
@@ -188,7 +331,7 @@ private string FindAnnotations(int line)
188
331
return null ;
189
332
}
190
333
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 ) ;
192
335
if ( commentAbove != null && commentAbove . CommentText . StartsWith ( "@" ) )
193
336
{
194
337
return commentAbove . CommentText ;
@@ -235,12 +378,7 @@ private void ResolveType(VBAParser.ComplexTypeContext context)
235
378
private void ResolveType ( IList < VBAParser . AmbiguousIdentifierContext > identifiers )
236
379
{
237
380
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 ) ;
244
382
245
383
if ( projectMatch != null )
246
384
{
@@ -252,22 +390,13 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
252
390
// - UDT
253
391
if ( identifiers . Count == 3 )
254
392
{
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 ( ) ) ;
261
394
if ( moduleMatch != null )
262
395
{
263
396
var moduleReference = CreateReference ( identifiers [ 1 ] , moduleMatch ) ;
264
397
265
398
// 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 ( ) ) ;
271
400
if ( udtMatch != null )
272
401
{
273
402
var udtReference = CreateReference ( identifiers [ 2 ] , udtMatch ) ;
@@ -290,12 +419,8 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
290
419
projectMatch . AddReference ( projectReference ) ;
291
420
_alreadyResolved . Add ( projectReference . Context ) ;
292
421
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 ( ) ) ;
299
424
if ( match != null )
300
425
{
301
426
var reference = CreateReference ( identifiers [ 1 ] , match ) ;
@@ -313,22 +438,14 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
313
438
// if there are 3 identifiers, type isn't in current project.
314
439
if ( identifiers . Count != 3 )
315
440
{
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 ( ) ) ;
322
443
if ( moduleMatch != null )
323
444
{
324
445
var moduleReference = CreateReference ( identifiers [ 0 ] , moduleMatch ) ;
325
446
326
447
// 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 ( ) ) ;
332
449
if ( udtMatch != null )
333
450
{
334
451
var udtReference = CreateReference ( identifiers [ 1 ] , udtMatch ) ;
@@ -343,18 +460,9 @@ private void ResolveType(IList<VBAParser.AmbiguousIdentifierContext> identifiers
343
460
}
344
461
}
345
462
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
-
355
463
private Declaration ResolveInScopeType ( string identifier , Declaration scope )
356
464
{
357
- var matches = FindMatchingTypes ( identifier ) . ToList ( ) ;
465
+ var matches = _declarationFinder . MatchTypeName ( identifier ) . ToList ( ) ;
358
466
if ( matches . Count == 1 )
359
467
{
360
468
return matches . Single ( ) ;
@@ -377,10 +485,7 @@ private Declaration ResolveInScopeType(string identifier, Declaration scope)
377
485
378
486
return null ;
379
487
}
380
-
381
-
382
-
383
-
488
+
384
489
private Declaration ResolveType ( Declaration parent )
385
490
{
386
491
if ( parent != null && parent . DeclarationType == DeclarationType . UserDefinedType )
@@ -1019,7 +1124,7 @@ private Declaration FindFunctionOrPropertyGetter(string identifierName, Declarat
1019
1124
localScope = _currentScope ;
1020
1125
}
1021
1126
1022
- var matches = _declarations . Where ( d => d . IdentifierName == identifierName ) ;
1127
+ var matches = _declarationFinder . MatchName ( identifierName ) ;
1023
1128
var parent = matches . SingleOrDefault ( item =>
1024
1129
( item . DeclarationType == DeclarationType . Function || item . DeclarationType == DeclarationType . PropertyGet )
1025
1130
&& item . Equals ( localScope ) ) ;
@@ -1040,7 +1145,7 @@ private Declaration FindLocalScopeDeclaration(string identifierName, Declaration
1040
1145
return null ;
1041
1146
}
1042
1147
1043
- var matches = _declarations . Where ( d => d . IdentifierName == identifierName ) ;
1148
+ var matches = _declarationFinder . MatchName ( identifierName ) ;
1044
1149
1045
1150
var results = matches . Where ( item =>
1046
1151
( item . ParentScope == localScope . Scope || ( isAssignmentTarget && item . Scope == localScope . Scope ) )
@@ -1077,7 +1182,7 @@ private Declaration FindModuleScopeDeclaration(string identifierName, Declaratio
1077
1182
localScope = _currentScope ;
1078
1183
}
1079
1184
1080
- var matches = _declarations . Where ( d => d . IdentifierName == identifierName ) ;
1185
+ var matches = _declarationFinder . MatchName ( identifierName ) ;
1081
1186
var result = matches . Where ( item =>
1082
1187
item . ParentScope == localScope . ParentScope
1083
1188
&& ! item . DeclarationType . HasFlag ( DeclarationType . Member )
@@ -1102,7 +1207,7 @@ private Declaration FindModuleScopeProcedure(string identifierName, Declaration
1102
1207
localScope = _currentScope ;
1103
1208
}
1104
1209
1105
- var matches = _declarations . Where ( d => d . IdentifierName == identifierName ) ;
1210
+ var matches = _declarationFinder . MatchName ( identifierName ) ;
1106
1211
var result = matches . Where ( item =>
1107
1212
item . Project == localScope . Project
1108
1213
&& item . ComponentName == localScope . ComponentName
@@ -1141,8 +1246,13 @@ private Declaration FindProjectScopeDeclaration(string identifierName, Declarati
1141
1246
}
1142
1247
else
1143
1248
{
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
+ }
1146
1256
}
1147
1257
1148
1258
return null ;
0 commit comments