12
12
using System . Globalization ;
13
13
using Rubberduck . Parsing . Preprocessing ;
14
14
using System . Diagnostics ;
15
+ using Rubberduck . Parsing . Annotations ;
15
16
using Rubberduck . Parsing . Grammar ;
16
17
using Rubberduck . Parsing . Nodes ;
17
18
using Rubberduck . VBEditor . Extensions ;
@@ -33,10 +34,6 @@ public RubberduckParserState State
33
34
private readonly ConcurrentDictionary < VBComponent , Tuple < Task , CancellationTokenSource > > _currentTasks =
34
35
new ConcurrentDictionary < VBComponent , Tuple < Task , CancellationTokenSource > > ( ) ;
35
36
36
- private readonly Dictionary < VBComponent , IParseTree > _parseTrees = new Dictionary < VBComponent , IParseTree > ( ) ;
37
- private readonly Dictionary < QualifiedModuleName , Dictionary < Declaration , byte > > _declarations = new Dictionary < QualifiedModuleName , Dictionary < Declaration , byte > > ( ) ;
38
- private readonly Dictionary < VBComponent , ITokenStream > _tokenStreams = new Dictionary < VBComponent , ITokenStream > ( ) ;
39
- private readonly Dictionary < VBComponent , IList < CommentNode > > _comments = new Dictionary < VBComponent , IList < CommentNode > > ( ) ;
40
37
private readonly IDictionary < VBComponent , IDictionary < Tuple < string , DeclarationType > , Attributes > > _componentAttributes
41
38
= new Dictionary < VBComponent , IDictionary < Tuple < string , DeclarationType > , Attributes > > ( ) ;
42
39
@@ -134,25 +131,26 @@ private void ParseAll()
134
131
}
135
132
136
133
var projects = _state . Projects . ToList ( ) ;
137
-
138
134
var components = projects . SelectMany ( p => p . VBComponents . Cast < VBComponent > ( ) ) . ToList ( ) ;
139
- var modified = components . Where ( c => _state . IsNewOrModified ( c ) ) . ToList ( ) ;
135
+
136
+ var toParse = components . Where ( c => _state . IsNewOrModified ( c ) ) . ToList ( ) ;
140
137
var unchanged = components . Where ( c => ! _state . IsNewOrModified ( c ) ) . ToList ( ) ;
141
138
142
- SyncComReferences ( projects ) ;
139
+ AddBuiltInDeclarations ( projects ) ;
143
140
144
- if ( ! modified . Any ( ) )
141
+ if ( ! toParse . Any ( ) )
145
142
{
146
143
return ;
147
144
}
148
145
149
- foreach ( var component in modified )
146
+ foreach ( var component in toParse )
150
147
{
151
148
_state . SetModuleState ( component , ParserState . Pending ) ;
152
149
}
153
150
foreach ( var component in unchanged )
154
151
{
155
- _state . SetModuleState ( component , ParserState . Parsed ) ;
152
+ // note: seting to 'Parsed' would include them in the resolver walk. 'Ready' excludes them.
153
+ _state . SetModuleState ( component , ParserState . Ready ) ;
156
154
}
157
155
158
156
// invalidation cleanup should go into ParseAsync?
@@ -161,19 +159,55 @@ private void ParseAll()
161
159
_componentAttributes . Remove ( invalidated ) ;
162
160
}
163
161
164
- foreach ( var vbComponent in modified )
162
+ foreach ( var vbComponent in toParse )
165
163
{
166
164
ParseAsync ( vbComponent , CancellationToken . None ) ;
167
165
}
168
166
}
169
167
168
+ private void AddBuiltInDeclarations ( IReadOnlyList < VBProject > projects )
169
+ {
170
+ SyncComReferences ( projects ) ;
171
+
172
+ var finder = new DeclarationFinder ( _state . AllDeclarations , new CommentNode [ ] { } , new IAnnotation [ ] { } ) ;
173
+ if ( finder . MatchName ( Tokens . Err ) . Any ( item => item . IsBuiltIn
174
+ && item . DeclarationType == DeclarationType . Variable
175
+ && item . Accessibility == Accessibility . Global ) )
176
+ {
177
+ return ;
178
+ }
179
+
180
+ var vba = finder . FindProject ( "VBA" ) ;
181
+ Debug . Assert ( vba != null ) ;
182
+
183
+ var errObject = finder . FindClass ( vba , "ErrObject" , true ) ;
184
+ Debug . Assert ( errObject != null ) ;
185
+
186
+ var qualifiedName = new QualifiedModuleName ( vba . IdentifierName , vba . IdentifierName , errObject . IdentifierName ) ;
187
+ var err = new Declaration ( new QualifiedMemberName ( qualifiedName , Tokens . Err ) , vba , "Global" , errObject . IdentifierName , true , false , Accessibility . Global , DeclarationType . Variable ) ;
188
+ _state . AddDeclaration ( err ) ;
189
+
190
+ var debugClassName = new QualifiedModuleName ( vba . IdentifierName , vba . IdentifierName , "DebugClass" ) ;
191
+ var debugClass = new Declaration ( new QualifiedMemberName ( debugClassName , "DebugClass" ) , vba , "Global" , "DebugClass" , false , false , Accessibility . Global , DeclarationType . Class ) ;
192
+ var debugObject = new Declaration ( new QualifiedMemberName ( debugClassName , "Debug" ) , vba , "Global" , "DebugClass" , true , false , Accessibility . Global , DeclarationType . Variable ) ;
193
+ var debugAssert = new Declaration ( new QualifiedMemberName ( debugClassName , "Assert" ) , debugObject , debugObject . Scope , null , false , false , Accessibility . Global , DeclarationType . Procedure ) ;
194
+ var debugPrint = new Declaration ( new QualifiedMemberName ( debugClassName , "Print" ) , debugObject , debugObject . Scope , null , false , false , Accessibility . Global , DeclarationType . Procedure ) ;
195
+
196
+ _state . AddDeclaration ( debugClass ) ;
197
+ _state . AddDeclaration ( debugObject ) ;
198
+ _state . AddDeclaration ( debugAssert ) ;
199
+ _state . AddDeclaration ( debugPrint ) ;
200
+ }
201
+
170
202
private readonly HashSet < ReferencePriorityMap > _references = new HashSet < ReferencePriorityMap > ( ) ;
171
203
172
204
private void SyncComReferences ( IReadOnlyList < VBProject > projects )
173
205
{
174
206
foreach ( var vbProject in projects )
175
207
{
176
208
var projectId = QualifiedModuleName . GetProjectId ( vbProject ) ;
209
+ // use a 'for' loop to store the order of references as a 'priority'.
210
+ // reference resolver needs this to know which declaration to prioritize when a global identifier exists in multiple libraries.
177
211
for ( var priority = 1 ; priority <= vbProject . References . Count ; priority ++ )
178
212
{
179
213
var reference = vbProject . References . Item ( priority ) ;
@@ -368,7 +402,7 @@ private void ResolveDeclarations(VBComponent component, IParseTree tree)
368
402
emptyStringLiteralListener ,
369
403
argListWithOneByRefParamListener ,
370
404
} ) , tree ) ;
371
- // TODO: these are actually (almost) isnpection results.. we should handle them as such
405
+ // TODO: these are actually (almost) inspection results.. we should handle them as such
372
406
_state . ArgListsWithOneByRefParam = argListWithOneByRefParamListener . Contexts . Select ( context => new QualifiedContext ( qualifiedModuleName , context ) ) ;
373
407
_state . EmptyStringLiterals = emptyStringLiteralListener . Contexts . Select ( context => new QualifiedContext ( qualifiedModuleName , context ) ) ;
374
408
_state . ObsoleteLetContexts = obsoleteLetStatementListener . Contexts . Select ( context => new QualifiedContext ( qualifiedModuleName , context ) ) ;
@@ -382,10 +416,13 @@ private void ResolveDeclarations(VBComponent component, IParseTree tree)
382
416
declarationsListener . NewDeclaration += ( sender , e ) => _state . AddDeclaration ( e . Declaration ) ;
383
417
declarationsListener . CreateModuleDeclarations ( ) ;
384
418
// rewalk parse tree for second declaration level
419
+
420
+ Debug . WriteLine ( "Walking parse tree for '{0}'... (acquiring declarations)" , qualifiedModuleName . Name ) ;
385
421
ParseTreeWalker . Default . Walk ( declarationsListener , tree ) ;
422
+
386
423
} catch ( Exception exception )
387
424
{
388
- Debug . Print ( "Exception thrown resolving '{0}' (thread {2}): {1}" , component . Name , exception , Thread . CurrentThread . ManagedThreadId ) ;
425
+ Debug . Print ( "Exception thrown acquiring declarations for '{0}' (thread {2}): {1}" , component . Name , exception , Thread . CurrentThread . ManagedThreadId ) ;
389
426
_state . SetModuleState ( component , ParserState . ResolverError ) ;
390
427
}
391
428
@@ -399,8 +436,8 @@ private void ResolveReferences(DeclarationFinder finder, VBComponent component,
399
436
return ;
400
437
}
401
438
402
- Debug . WriteLine ( "Resolving '{0}'... (thread {1})" , component . Name , Thread . CurrentThread . ManagedThreadId ) ;
403
439
var qualifiedName = new QualifiedModuleName ( component ) ;
440
+ Debug . WriteLine ( "Resolving identifier references in '{0}'... (thread {1})" , qualifiedName . Name , Thread . CurrentThread . ManagedThreadId ) ;
404
441
var resolver = new IdentifierReferenceResolver ( qualifiedName , finder ) ;
405
442
var listener = new IdentifierReferenceListener ( resolver ) ;
406
443
if ( ! string . IsNullOrWhiteSpace ( tree . GetText ( ) . Trim ( ) ) )
0 commit comments