1212using System . Globalization ;
1313using Rubberduck . Parsing . Preprocessing ;
1414using System . Diagnostics ;
15+ using Rubberduck . Parsing . Annotations ;
1516using Rubberduck . Parsing . Grammar ;
1617using Rubberduck . Parsing . Nodes ;
1718using Rubberduck . VBEditor . Extensions ;
@@ -33,10 +34,6 @@ public RubberduckParserState State
3334 private readonly ConcurrentDictionary < VBComponent , Tuple < Task , CancellationTokenSource > > _currentTasks =
3435 new ConcurrentDictionary < VBComponent , Tuple < Task , CancellationTokenSource > > ( ) ;
3536
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 > > ( ) ;
4037 private readonly IDictionary < VBComponent , IDictionary < Tuple < string , DeclarationType > , Attributes > > _componentAttributes
4138 = new Dictionary < VBComponent , IDictionary < Tuple < string , DeclarationType > , Attributes > > ( ) ;
4239
@@ -134,25 +131,26 @@ private void ParseAll()
134131 }
135132
136133 var projects = _state . Projects . ToList ( ) ;
137-
138134 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 ( ) ;
140137 var unchanged = components . Where ( c => ! _state . IsNewOrModified ( c ) ) . ToList ( ) ;
141138
142- SyncComReferences ( projects ) ;
139+ AddBuiltInDeclarations ( projects ) ;
143140
144- if ( ! modified . Any ( ) )
141+ if ( ! toParse . Any ( ) )
145142 {
146143 return ;
147144 }
148145
149- foreach ( var component in modified )
146+ foreach ( var component in toParse )
150147 {
151148 _state . SetModuleState ( component , ParserState . Pending ) ;
152149 }
153150 foreach ( var component in unchanged )
154151 {
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 ) ;
156154 }
157155
158156 // invalidation cleanup should go into ParseAsync?
@@ -161,19 +159,55 @@ private void ParseAll()
161159 _componentAttributes . Remove ( invalidated ) ;
162160 }
163161
164- foreach ( var vbComponent in modified )
162+ foreach ( var vbComponent in toParse )
165163 {
166164 ParseAsync ( vbComponent , CancellationToken . None ) ;
167165 }
168166 }
169167
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+
170202 private readonly HashSet < ReferencePriorityMap > _references = new HashSet < ReferencePriorityMap > ( ) ;
171203
172204 private void SyncComReferences ( IReadOnlyList < VBProject > projects )
173205 {
174206 foreach ( var vbProject in projects )
175207 {
176208 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.
177211 for ( var priority = 1 ; priority <= vbProject . References . Count ; priority ++ )
178212 {
179213 var reference = vbProject . References . Item ( priority ) ;
@@ -368,7 +402,7 @@ private void ResolveDeclarations(VBComponent component, IParseTree tree)
368402 emptyStringLiteralListener ,
369403 argListWithOneByRefParamListener ,
370404 } ) , 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
372406 _state . ArgListsWithOneByRefParam = argListWithOneByRefParamListener . Contexts . Select ( context => new QualifiedContext ( qualifiedModuleName , context ) ) ;
373407 _state . EmptyStringLiterals = emptyStringLiteralListener . Contexts . Select ( context => new QualifiedContext ( qualifiedModuleName , context ) ) ;
374408 _state . ObsoleteLetContexts = obsoleteLetStatementListener . Contexts . Select ( context => new QualifiedContext ( qualifiedModuleName , context ) ) ;
@@ -382,10 +416,13 @@ private void ResolveDeclarations(VBComponent component, IParseTree tree)
382416 declarationsListener . NewDeclaration += ( sender , e ) => _state . AddDeclaration ( e . Declaration ) ;
383417 declarationsListener . CreateModuleDeclarations ( ) ;
384418 // rewalk parse tree for second declaration level
419+
420+ Debug . WriteLine ( "Walking parse tree for '{0}'... (acquiring declarations)" , qualifiedModuleName . Name ) ;
385421 ParseTreeWalker . Default . Walk ( declarationsListener , tree ) ;
422+
386423 } catch ( Exception exception )
387424 {
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 ) ;
389426 _state . SetModuleState ( component , ParserState . ResolverError ) ;
390427 }
391428
@@ -399,8 +436,8 @@ private void ResolveReferences(DeclarationFinder finder, VBComponent component,
399436 return ;
400437 }
401438
402- Debug . WriteLine ( "Resolving '{0}'... (thread {1})" , component . Name , Thread . CurrentThread . ManagedThreadId ) ;
403439 var qualifiedName = new QualifiedModuleName ( component ) ;
440+ Debug . WriteLine ( "Resolving identifier references in '{0}'... (thread {1})" , qualifiedName . Name , Thread . CurrentThread . ManagedThreadId ) ;
404441 var resolver = new IdentifierReferenceResolver ( qualifiedName , finder ) ;
405442 var listener = new IdentifierReferenceListener ( resolver ) ;
406443 if ( ! string . IsNullOrWhiteSpace ( tree . GetText ( ) . Trim ( ) ) )
0 commit comments