@@ -17,7 +17,7 @@ import { FoldingRange, FoldingRangeKind } from '../capabilities/folding';
1717import { SemanticToken , SemanticTokenModifiers , SemanticTokenTypes } from '../capabilities/semanticTokens' ;
1818import { BaseRuleSyntaxElement , BaseIdentifyableSyntaxElement , BaseSyntaxElement , Context , HasSemanticTokenCapability } from '../project/elements/base' ;
1919import { AmbiguousNameDiagnostic , BaseDiagnostic , DuplicateDeclarationDiagnostic , ShadowDeclarationDiagnostic , SubOrFunctionNotDefinedDiagnostic , UnusedDiagnostic , VariableNotDefinedDiagnostic } from './diagnostics' ;
20- import { isPositionInsideRange , isRangeInsideRange } from '../utils/helpers' ;
20+ import { isPositionInsideRange , isRangeInsideRange , rangeEquals } from '../utils/helpers' ;
2121
2222
2323abstract class BaseCapability {
@@ -266,6 +266,10 @@ export class ScopeItemCapability {
266266 return result === '' ? 'NONE' : result ;
267267 }
268268
269+ get range ( ) : Range | undefined {
270+ return this . element ?. context . range ;
271+ }
272+
269273 // Item Properties
270274 locationUri ?: string ;
271275 isPublicScope ?: boolean ;
@@ -287,24 +291,7 @@ export class ScopeItemCapability {
287291 if ( this . type === ScopeType . REFERENCE ) {
288292 // Link to declaration if it exists.
289293 this . resolveLinks ( ) ;
290- if ( ! this . link ) {
291- // TODO:
292- // References to variables should get a diagnostic if they aren't declared.
293- // -- No option explicit: Hint with code action to declare.
294- // GET before declared gets a warning.
295- // -- Option explicit: Error with code action to declare.
296- // -- Subsequent explicit declaration should raise duplicate declaration (current bahaviour).
297- // -- All declarations with no GET references get a warning.
298- // References to function or sub calls should raise an error if they aren't declared.
299- // -- Must always throw even when option explicit not present.
300- // -- Nothing required on first reference as declaration may come later.
301- const severity = this . isOptionExplicitScope
302- ? DiagnosticSeverity . Error
303- : DiagnosticSeverity . Hint ;
304- const _ = this . assignmentType & AssignmentType . CALL
305- ? this . pushDiagnostic ( SubOrFunctionNotDefinedDiagnostic , this , this . name )
306- : this . pushDiagnostic ( VariableNotDefinedDiagnostic , this , this . name , severity ) ;
307- }
294+ this . validateLink ( ) ;
308295 } else {
309296 // Diagnostic checks on declarations.
310297 const ancestors = this . getParentChain ( ) ;
@@ -546,6 +533,27 @@ export class ScopeItemCapability {
546533 this . linkThisToItem ( foundDeclarations [ 0 ] ) ;
547534 }
548535
536+ private validateLink ( ) {
537+ if ( ! this . link ) {
538+ // TODO:
539+ // References to variables should get a diagnostic if they aren't declared.
540+ // -- No option explicit: Hint with code action to declare.
541+ // GET before declared gets a warning.
542+ // -- Option explicit: Error with code action to declare.
543+ // -- Subsequent explicit declaration should raise duplicate declaration (current bahaviour).
544+ // -- All declarations with no GET references get a warning.
545+ // References to function or sub calls should raise an error if they aren't declared.
546+ // -- Must always throw even when option explicit not present.
547+ // -- Nothing required on first reference as declaration may come later.
548+ const severity = this . isOptionExplicitScope
549+ ? DiagnosticSeverity . Error
550+ : DiagnosticSeverity . Hint ;
551+ const _ = this . assignmentType & AssignmentType . CALL
552+ ? this . pushDiagnostic ( SubOrFunctionNotDefinedDiagnostic , this , this . name )
553+ : this . pushDiagnostic ( VariableNotDefinedDiagnostic , this , this . name , severity ) ;
554+ }
555+ }
556+
549557 private linkThisToItem ( linkItem ?: ScopeItemCapability ) : void {
550558 if ( ! linkItem ) {
551559 return ;
@@ -787,28 +795,23 @@ export class ScopeItemCapability {
787795 * Recursively removes all scopes with the passed in uri and
788796 * within the range bounds, including where it is linked.
789797 */
790- invalidate ( uri : string , range : Range ) : void {
791- const isInvalidScope = ( scope : ScopeItemCapability ) =>
792- scope . locationUri === uri
793- && scope . element ?. context . range
794- && isRangeInsideRange ( scope . element . context . range , range ) ;
795-
798+ invalidate ( uri : string , range ?: Range ) : void {
796799 const cleanScopes = ( scopes ?: ScopeItemCapability [ ] ) => {
797800 if ( scopes === undefined ) {
798801 return undefined ;
799802 }
800803
801804 const result : ScopeItemCapability [ ] = [ ] ;
802805 scopes . forEach ( scope => {
803- if ( isInvalidScope ( scope ) ) {
806+ if ( scope . isLocatedAt ( uri , range ) ) {
804807 Services . logger . debug ( `Invalidating ${ scope . name } ` ) ;
805808
806809 // Clean the backlinks on the linked item if we have one.
807810 if ( scope . link ) scope . link . backlinks = cleanScopes (
808811 scope . link . backlinks ) ;
809812
810813 // Clean the invaludated scope.
811- scope . invalidate ( uri , range ) ;
814+ scope . invalidate ( uri , scope . range ) ;
812815
813816 return ;
814817 }
@@ -849,10 +852,24 @@ export class ScopeItemCapability {
849852
850853 // Do a basic clean on backlinks that doesn't trigger recursion.
851854 if ( this . backlinks ) {
852- this . backlinks = this . backlinks . filter ( scope => ! isInvalidScope ( scope ) ) ;
855+ this . backlinks = this . backlinks
856+ . filter ( scope => ! scope . isLocatedAt ( uri , range ) ) ;
853857 }
854858 }
855859
860+ /** Returns true if the uri matches and, if passed, range is fully inside range. */
861+ isLocatedAt ( uri : string , range ?: Range ) : boolean {
862+ if ( uri !== this . locationUri ) {
863+ return false ;
864+ }
865+
866+ if ( ! range ) {
867+ return true ;
868+ }
869+
870+ return isRangeInsideRange ( this . range , range ) ;
871+ }
872+
856873 /** Returns true for public and false for private */
857874 private getVisibility ( item : ScopeItemCapability ) : boolean {
858875 // Classes and modules are always public.
@@ -900,7 +917,7 @@ export class ScopeItemCapability {
900917
901918 // Check all items for whether they have a name overlap or a scope overlap.
902919 scope ?. maps . forEach ( map => map . forEach ( items => items . forEach ( item => {
903- const elementRange = item . element ?. context . range ;
920+ const elementRange = item . range ;
904921 const identifierRange = item . element ?. identifierCapability ?. range ;
905922 if ( identifierRange && isPositionInsideRange ( position , identifierRange ) ) {
906923 // Position is inside the identifier, push to results.
@@ -972,7 +989,7 @@ export class ScopeItemCapability {
972989 link . locationUri ,
973990 link . element . context . range ,
974991 link . element . identifierCapability . range ,
975- this . element ?. context . range
992+ this . range
976993 ) ;
977994 }
978995
@@ -1045,7 +1062,7 @@ export class ScopeItemCapability {
10451062 } ;
10461063
10471064 const m = new Map < string , ScopeItemCapability > ( ) ;
1048- results . forEach ( s => m . set ( rangeString ( s . element ?. context . range ) , s ) ) ;
1065+ results . forEach ( s => m . set ( rangeString ( s . range ) , s ) ) ;
10491066 return Array . from ( m . values ( ) ) ;
10501067 }
10511068}
0 commit comments