@@ -29,6 +29,8 @@ class NameBindingNode extends TNameBindingNode {
2929
3030 Identifier asIdentifier ( ) { this .isIdentifier ( result ) }
3131
32+ predicate isMember ( Member m ) { this .asIdentifier ( ) = m .getNameNode ( ) }
33+
3234 predicate isBulkImport ( BulkImportingPattern p ) { this = TBulkImport ( p ) }
3335
3436 predicate isLocalName ( LocalName local ) { this = TLocalName ( local ) }
@@ -41,6 +43,10 @@ class NameBindingNode extends TNameBindingNode {
4143 this = TInstanceMemberNamespace ( cls )
4244 }
4345
46+ predicate isInstanceOrStaticMemberNamespace ( ClassLikeDeclaration cls ) {
47+ this .isInstanceMemberNamespace ( cls ) or this .isStaticMemberNamespace ( cls )
48+ }
49+
4450 /** Holds if this represents the set of members that can be accessed unqualified within the given scope. */
4551 predicate isLocalNamespace ( AstNode n ) { this = TLocalNamespace ( n ) }
4652
@@ -312,6 +318,10 @@ signature module TrackInputSig {
312318 /** Holds if the forward-flow of `node` should be tracked. */
313319 predicate shouldTrack ( NameBindingNode node ) ;
314320
321+ default NameBindingNode getMember ( NamespaceNode namespace , string name ) {
322+ result = namespace .getMember ( name )
323+ }
324+
315325 default predicate additionalValueStep ( NameBindingNode node1 , NameBindingNode node2 ) { none ( ) }
316326}
317327
@@ -327,6 +337,25 @@ module Track<TrackInputSig Input> {
327337 exists ( NameBindingNode prev | prev = track ( node ) | valueStepEx ( prev , result ) )
328338 }
329339
340+ pragma [ nomagic]
341+ private predicate derivedStoreReadStep0 (
342+ NamespaceNode namespace , string name , NameBindingNode node2
343+ ) {
344+ readStep ( namespace .ref ( ) , name , node2 )
345+ }
346+
347+ /**
348+ * Holds if `node1 -> node2` is derived by combining a store and a read step, with zero or more value steps and inheritance steps in-between.
349+ */
350+ pragma [ nomagic]
351+ private predicate derivedStoreReadStep ( NameBindingNode node1 , NameBindingNode node2 ) {
352+ exists ( NamespaceNode namespace , string name |
353+ node1 = getMember ( namespace , name ) and // getMember() combines a store step with subsequent inheritance steps
354+ derivedStoreReadStep0 ( namespace , name , node2 ) and
355+ node1 != node2
356+ )
357+ }
358+
330359 /** Holds if there is an effective value step `node1 -> node2`. */
331360 pragma [ inline]
332361 private predicate valueStepEx ( NameBindingNode node1 , NameBindingNode node2 ) {
@@ -338,23 +367,6 @@ module Track<TrackInputSig Input> {
338367 }
339368}
340369
341- pragma [ nomagic]
342- private predicate derivedStoreReadStep0 ( NamespaceNode namespace , string name , NameBindingNode node2 ) {
343- readStep ( namespace .ref ( ) , pragma [ only_bind_into ] ( name ) , node2 )
344- }
345-
346- /**
347- * Holds if `node1 -> node2` is derived by combining a store and a read step, with zero or more value steps and inheritance steps in-between.
348- */
349- pragma [ nomagic]
350- private predicate derivedStoreReadStep ( NameBindingNode node1 , NameBindingNode node2 ) {
351- exists ( NamespaceNode namespace , string name |
352- node1 = namespace .getMember ( name ) and // getMember() combines a store step with subsequent inheritance steps
353- derivedStoreReadStep0 ( namespace , name , node2 ) and
354- node1 != node2
355- )
356- }
357-
358370/** Holds if the member represented by `node` can be inherited. */
359371pragma [ nomagic]
360372private predicate isInheritableMemberNode ( NameBindingNode node ) {
@@ -382,10 +394,13 @@ class NamespaceNode extends NameBindingNode {
382394 NameBindingNode ref ( ) { result = TrackNamespace:: track ( this ) }
383395
384396 /** Gets an own (non-inherited) member of this namespace of the given name. */
385- NameBindingNode getOwnMember ( string name ) { storeStep ( result , name , this ) }
397+ NameBindingNode getOwnMember ( string name , ShadowingKeyOpt shadowingKey ) {
398+ storeStep ( result , name , this ) and
399+ shadowingKey = getShadowingKey ( result )
400+ }
386401
387- /** Holds if this namespace has an own- member of the given name */
388- predicate hasOwnMember ( string name ) { exists ( this .getOwnMember ( name ) ) }
402+ /** Gets an own (non-inherited) member of this namespace of the given name. */
403+ NameBindingNode getOwnMember ( string name ) { result = this .getOwnMember ( name , _ ) }
389404
390405 /** If this is the static namespace for a class, gets the corresponding instance namespace. */
391406 NamespaceNode toInstanceNamespace ( ) {
@@ -423,23 +438,58 @@ class NamespaceNode extends NameBindingNode {
423438 /** Gets a namespace that directly inherits from this one. */
424439 NamespaceNode getAnInheritanceChild ( ) { result .getAnInheritanceParent ( ) = this }
425440
426- /** Gets a member of this namespace of the given name. */
427- cached
428- NameBindingNode getMember ( string name ) {
429- CachedStage:: ref ( ) and
430- result = this .getOwnMember ( name )
441+ pragma [ nomagic]
442+ private predicate hasOwnMemberWithShadowingKey ( string name , string shadowingKey ) {
443+ exists ( this .getOwnMember ( name , any ( ShadowingKeyOpt opt | opt .asSome ( ) = shadowingKey ) ) )
444+ }
445+
446+ /**
447+ * Gets a potential member of this namespace of the given name.
448+ *
449+ * Unlike `getMember`, this predicate does not filter away invalid members.
450+ */
451+ pragma [ nomagic]
452+ NameBindingNode getMemberFull ( string name , ShadowingKeyOpt shadowingKey ) {
453+ result = this .getOwnMember ( name ) and
454+ shadowingKey = getShadowingKey ( result )
431455 or
432- not this .hasOwnMember ( name ) and
433- result = this .getAnInheritanceParent ( ) .getMember ( name ) and
456+ not this .hasOwnMemberWithShadowingKey ( name , shadowingKey . asSome ( ) ) and
457+ result = this .getAnInheritanceParent ( ) .getMemberFull ( name , shadowingKey ) and
434458 isInheritableMemberNode ( result )
435459 or
436- result = this .getAnExtension ( ) .getMember ( name )
460+ result = this .getAnExtension ( ) .getMemberFull ( name , shadowingKey )
461+ }
462+
463+ private NameBindingNode getMember ( string name , ShadowingKeyOpt shadowingKey ) {
464+ CachedStage:: ref ( ) and
465+ not exists ( Member m | result .isMember ( m ) | isInvalidMember ( this , m ) ) and
466+ (
467+ result = this .getOwnMember ( name ) and
468+ shadowingKey = getShadowingKey ( result )
469+ or
470+ result = this .getAnExtension ( ) .getMemberFull ( name , shadowingKey )
471+ or
472+ exists ( NamespaceNode parent |
473+ parent = this .getAnInheritanceParent ( ) and
474+ not this .hasOwnMemberWithShadowingKey ( name , shadowingKey .asSome ( ) ) and
475+ result = parent .getMember ( name , shadowingKey ) and
476+ isInheritableMemberNode ( result )
477+ )
478+ )
437479 }
480+
481+ /** Gets a member of this namespace of the given name. */
482+ cached
483+ NameBindingNode getMember ( string name ) { result = this .getMember ( name , _) }
438484}
439485
440486private module TrackNamespaceInput implements TrackInputSig {
441487 predicate shouldTrack ( NameBindingNode node ) { node instanceof NamespaceNode }
442488
489+ NameBindingNode getMember ( NamespaceNode namespace , string name ) {
490+ result = namespace .getMemberFull ( name , _)
491+ }
492+
443493 predicate additionalValueStep ( NameBindingNode node1 , NameBindingNode node2 ) {
444494 // Namespace-tracking goes through aliases, but declaration-tracking does not
445495 exists ( TypeAliasDeclaration decl |
0 commit comments