@@ -10,7 +10,8 @@ private newtype TNameBindingNode =
1010 TIdentifier ( Identifier n ) or
1111 TBulkImport ( BulkImportingPattern p ) or
1212 TLocalName ( LocalName local ) or
13- TExportedNamespace ( ClassLikeDeclaration cls ) or
13+ TStaticMemberNamespace ( ClassLikeDeclaration cls ) or
14+ TInstanceMemberNamespace ( ClassLikeDeclaration cls ) or
1415 TLocalNamespace ( AstNode n ) {
1516 n = any ( TopLevel t ) .getBody ( ) or // Imported names come in scope here
1617 n instanceof ClassLikeDeclaration
@@ -31,8 +32,13 @@ class NameBindingNode extends TNameBindingNode {
3132
3233 predicate isLocalName ( LocalName local ) { this = TLocalName ( local ) }
3334
34- /** Holds if this represents the set of static members available in the given namespace. */
35- predicate isExportedNamespace ( ClassLikeDeclaration cls ) { this = TExportedNamespace ( cls ) }
35+ /** Holds if this represents the set of static members available in the given class. */
36+ predicate isStaticMemberNamespace ( ClassLikeDeclaration cls ) { this = TStaticMemberNamespace ( cls ) }
37+
38+ /** Holds if this represents the set of instance members available in the given class. */
39+ predicate isInstanceMemberNamespace ( ClassLikeDeclaration cls ) {
40+ this = TInstanceMemberNamespace ( cls )
41+ }
3642
3743 /** Holds if this represents the set of members that can be accessed unqualified within the given scope. */
3844 predicate isLocalNamespace ( AstNode n ) { this = TLocalNamespace ( n ) }
@@ -56,7 +62,9 @@ class NameBindingNode extends TNameBindingNode {
5662 or
5763 this .isBulkImport ( result )
5864 or
59- this .isExportedNamespace ( result )
65+ this .isStaticMemberNamespace ( result )
66+ or
67+ this .isInstanceMemberNamespace ( result )
6068 or
6169 this .isLocalNamespace ( result )
6270 or
@@ -71,7 +79,11 @@ class NameBindingNode extends TNameBindingNode {
7179 exists ( LocalName local | this .isLocalName ( local ) and result = "LocalName(" + local + ")" )
7280 or
7381 exists ( ClassLikeDeclaration cls |
74- this .isExportedNamespace ( cls ) and result = "ExportedNamespace(" + cls + ")"
82+ this .isStaticMemberNamespace ( cls ) and result = "StaticMemberNamespace(" + cls + ")"
83+ )
84+ or
85+ exists ( ClassLikeDeclaration cls |
86+ this .isInstanceMemberNamespace ( cls ) and result = "InstanceMemberNamespace(" + cls + ")"
7587 )
7688 or
7789 exists ( AstNode n | this .isLocalNamespace ( n ) and result = "LocalNamespace(" + n + ")" )
@@ -92,7 +104,13 @@ class NameBindingNode extends TNameBindingNode {
92104 or
93105 exists ( LocalName local | this .isLocalName ( local ) and result = local .getLocation ( ) )
94106 or
95- exists ( ClassLikeDeclaration cls | this .isExportedNamespace ( cls ) and result = cls .getLocation ( ) )
107+ exists ( ClassLikeDeclaration cls |
108+ this .isStaticMemberNamespace ( cls ) and result = cls .getLocation ( )
109+ )
110+ or
111+ exists ( ClassLikeDeclaration cls |
112+ this .isInstanceMemberNamespace ( cls ) and result = cls .getLocation ( )
113+ )
96114 or
97115 exists ( AstNode n | this .isLocalNamespace ( n ) and result = n .getLocation ( ) )
98116 or
@@ -157,12 +175,13 @@ predicate readStep(NameBindingNode node1, string name, NameBindingNode node2) {
157175predicate storeStep ( NameBindingNode node1 , string name , NameBindingNode node2 ) {
158176 exists ( ClassLikeDeclaration cls , Member member , NameDeclaration nameDecl |
159177 member = cls .getAMember ( ) and
160- not isInstanceMember ( member ) and
161178 not isPrivateToLocalScope ( nameDecl ) and
162179 nameDecl .getDeclaration ( ) = member and
163180 node1 .isIdentifier ( nameDecl ) and
164181 name = nameDecl .getName ( ) and
165- node2 .isExportedNamespace ( cls )
182+ if isInstanceMember ( member )
183+ then node2 .isInstanceMemberNamespace ( cls )
184+ else node2 .isStaticMemberNamespace ( cls )
166185 )
167186 or
168187 exists ( TopLevel top , Stmt stmt , NameDeclaration nameDecl |
@@ -195,12 +214,12 @@ predicate valueStep(NameBindingNode node1, NameBindingNode node2) {
195214 )
196215 or
197216 exists ( ClassLikeDeclaration cls |
198- node1 .isExportedNamespace ( cls ) and
217+ node1 .isStaticMemberNamespace ( cls ) and
199218 node2 .isIdentifier ( cls .getName ( ) )
200219 )
201220 or
202221 exists ( ClassLikeDeclaration cls |
203- node1 .isExportedNamespace ( cls ) and
222+ node1 .isStaticMemberNamespace ( cls ) and
204223 node2 .isLocalNamespace ( cls )
205224 )
206225 or
@@ -249,7 +268,7 @@ predicate inheritanceStep(NameBindingNode supertype, NameBindingNode subtype) {
249268 exists ( ClassLikeDeclaration cls , BaseType base |
250269 base = cls .getABaseType ( ) and
251270 supertype = getNodeFromRef ( base .getType ( ) ) and
252- subtype .isExportedNamespace ( cls )
271+ subtype .isStaticMemberNamespace ( cls )
253272 )
254273}
255274
@@ -295,9 +314,23 @@ private predicate derivedStoreReadStep(NameBindingNode node1, NameBindingNode no
295314 )
296315}
297316
298- /** A name-binding node that has members. */
317+ /** Holds if the member represented by `node` can be inherited. */
318+ pragma [ nomagic]
319+ private predicate isInheritableMemberNode ( NameBindingNode node ) {
320+ exists ( NameDeclaration decl |
321+ node .isIdentifier ( decl ) and
322+ isInheritableMember ( decl .getDeclaration ( ) )
323+ )
324+ }
325+
326+ /** A name-binding node that can have members. */
299327class NamespaceNode extends NameBindingNode {
300- NamespaceNode ( ) { storeStep ( _, _, this ) or inheritanceStep ( _, this ) }
328+ NamespaceNode ( ) {
329+ storeStep ( _, _, this ) or
330+ inheritanceStep ( _, this ) or
331+ this .isInstanceMemberNamespace ( _) or
332+ this .isStaticMemberNamespace ( _)
333+ }
301334
302335 /** Gets a name-binding node that may refer to this namespace. */
303336 NameBindingNode ref ( ) { result = TrackNamespace:: track ( this ) }
@@ -308,8 +341,27 @@ class NamespaceNode extends NameBindingNode {
308341 /** Holds if this namespace has an own-member of the given name */
309342 predicate hasOwnMember ( string name ) { exists ( this .getOwnMember ( name ) ) }
310343
344+ /** If this is the static namespace for a class, gets the corresponding instance namespace. */
345+ NamespaceNode toInstanceNamespace ( ) {
346+ exists ( ClassLikeDeclaration cls |
347+ this .isStaticMemberNamespace ( cls ) and
348+ result .isInstanceMemberNamespace ( cls )
349+ )
350+ }
351+
352+ /** If this is the instance namespace for a class, gets the corresponding static namespace. */
353+ NamespaceNode toStaticNamespace ( ) { result .toInstanceNamespace ( ) = this }
354+
355+ private NamespaceNode getAnInheritanceParent1 ( ) { inheritanceStep ( result .ref ( ) , this ) }
356+
311357 /** Gets a namespace from which this namespace inherits directly. */
312- NamespaceNode getAnInheritanceParent ( ) { inheritanceStep ( result .ref ( ) , this ) }
358+ NamespaceNode getAnInheritanceParent ( ) {
359+ result = this .getAnInheritanceParent1 ( )
360+ or
361+ // `inheritanceStep` connects the static namespaces of classes.
362+ // Add the corresponding inheritance relation between the instance namespaces.
363+ result = this .toStaticNamespace ( ) .getAnInheritanceParent1 ( ) .toInstanceNamespace ( )
364+ }
313365
314366 /** Gets a namespace that directly inherits from this one. */
315367 NamespaceNode getAnInheritanceChild ( ) { result .getAnInheritanceParent ( ) = this }
@@ -320,7 +372,8 @@ class NamespaceNode extends NameBindingNode {
320372 result = this .getOwnMember ( name )
321373 or
322374 not this .hasOwnMember ( name ) and
323- result = this .getAnInheritanceParent ( ) .getMember ( name )
375+ result = this .getAnInheritanceParent ( ) .getMember ( name ) and
376+ isInheritableMemberNode ( result )
324377 }
325378}
326379
@@ -506,3 +559,94 @@ private module FolderHeuristic {
506559 )
507560 }
508561}
562+
563+ /**
564+ * Holds if `access` may resolve to `target` through the enclosing `accessingClass`.
565+ *
566+ * `instanceAccess` indicates whether this member should be accessed as an instance of `accessingClass`
567+ * or as a static member.
568+ */
569+ private predicate unqualifiedMemberAccessCand (
570+ PotentialLocalNameAccess access , boolean instanceAccess , NameDeclaration target ,
571+ ClassLikeDeclaration accessingClass
572+ ) {
573+ not access instanceof NameDeclaration and
574+ (
575+ // Resolved by local scoping
576+ exists ( LocalName local |
577+ target .getLocalName ( ) = local and
578+ access .getLocalName ( ) = local and
579+ target .getDeclaration ( ) = accessingClass .getAMember ( )
580+ |
581+ instanceAccess = true and
582+ isInstanceMember ( target .getDeclaration ( ) )
583+ or
584+ instanceAccess = false and
585+ isStaticMember ( target .getDeclaration ( ) )
586+ )
587+ or
588+ // Resolved in an uncertain scope
589+ exists ( NamespaceNode namespace , string name |
590+ name = access .getName ( ) and
591+ accessingClass = LocalNameBindingOutput:: getAnUncertainScope ( access , name )
592+ |
593+ instanceAccess = true and
594+ namespace .isInstanceMemberNamespace ( accessingClass ) and
595+ namespace .getMember ( name ) .isIdentifier ( target )
596+ or
597+ instanceAccess = false and
598+ namespace .isStaticMemberNamespace ( accessingClass ) and
599+ namespace .getMember ( name ) .isIdentifier ( target )
600+ )
601+ )
602+ }
603+
604+ private int unqualifiedMemberAccessDepth ( PotentialLocalNameAccess access ) {
605+ result = max ( AstNode scope | unqualifiedMemberAccessCand ( access , _, _, scope ) | scope .getDepth ( ) )
606+ }
607+
608+ /**
609+ * Holds if `access` is an unqualified access to `target`.
610+ *
611+ * `accessingClass` is the enclosing class in which the member was found, and
612+ * `instanceAccess` indicates if it is an instance member or static member.
613+ */
614+ predicate unqualifiedMemberAccess (
615+ PotentialLocalNameAccess access , boolean instanceAccess , NameDeclaration target ,
616+ ClassLikeDeclaration accessingClass
617+ ) {
618+ unqualifiedMemberAccessCand ( access , instanceAccess , target , accessingClass ) and
619+ accessingClass .getDepth ( ) = unqualifiedMemberAccessDepth ( access )
620+ }
621+
622+ /**
623+ * An identifier appearing in a unqualified position, referring to a member of an enclosing class.
624+ */
625+ class UnqualifiedMemberAccess extends Identifier {
626+ private boolean instanceAccess ;
627+ private NameDeclaration target ;
628+ private ClassLikeDeclaration accessingClass ;
629+
630+ UnqualifiedMemberAccess ( ) {
631+ unqualifiedMemberAccess ( this , instanceAccess , target , accessingClass )
632+ }
633+
634+ /** Gets the name declaration of the member being accessed. */
635+ NameDeclaration getTarget ( ) { result = target }
636+
637+ /** Gets the enclosing class whose (possibly inherited) member is being accessed. */
638+ ClassLikeDeclaration getAccessingClass ( ) { result = accessingClass }
639+
640+ /** Holds if this is an instance access on the accessing class. */
641+ predicate isInstanceAccess ( ) { instanceAccess = true }
642+ }
643+
644+ /** Gets the declaration being accessed by `access`, as determined by static name binding. */
645+ NameDeclaration getStaticBindingTarget ( Identifier access ) {
646+ // For unqualified accesses, use the shadowing-aware lookup
647+ result = access .( UnqualifiedMemberAccess ) .getTarget ( )
648+ or
649+ // For others, just follow the name binding graph
650+ not access instanceof UnqualifiedMemberAccess and
651+ trackNameDeclaration ( result ) .asIdentifier ( ) = access
652+ }
0 commit comments