@@ -262,6 +262,13 @@ predicate valueStep(NameBindingNode node1, NameBindingNode node2) {
262262 node2 = getNodeFromRef ( p .getSubPattern ( ) )
263263 )
264264 or
265+ // Extensions have access to the members of the entity they extend.
266+ // TODO: The type parameters of the target type should also be in the local scope (for Swift).
267+ exists ( ClassLikeDeclaration extension |
268+ node1 = getNodeFromRef ( extension .getExtensionTarget ( ) ) and
269+ node2 .isLocalNamespace ( extension )
270+ )
271+ or
265272 FolderHeuristic:: valueStep ( node1 , node2 )
266273 or
267274 exists ( ClassLikeDeclaration cls , LocalNameBindingOutput:: ImplicitLocal self |
@@ -288,6 +295,13 @@ predicate inheritanceStep(NameBindingNode supertype, NameBindingNode subtype) {
288295 )
289296}
290297
298+ predicate extensionStep ( NameBindingNode extension , NameBindingNode targetClass ) {
299+ exists ( ClassLikeDeclaration cls |
300+ targetClass = getNodeFromRef ( cls .getExtensionTarget ( ) ) and
301+ extension .isStaticMemberNamespace ( cls )
302+ )
303+ }
304+
291305signature module TrackInputSig {
292306 /** Holds if the forward-flow of `node` should be tracked. */
293307 predicate shouldTrack ( NameBindingNode node ) ;
@@ -368,6 +382,17 @@ class NamespaceNode extends NameBindingNode {
368382 /** If this is the instance namespace for a class, gets the corresponding static namespace. */
369383 NamespaceNode toStaticNamespace ( ) { result .toInstanceNamespace ( ) = this }
370384
385+ private NamespaceNode getAnExtension1 ( ) { extensionStep ( result , this .ref ( ) ) }
386+
387+ /** Gets a namespace that is an extension (i.e. containing extension methods) of this node. */
388+ NamespaceNode getAnExtension ( ) {
389+ result = this .getAnExtension1 ( )
390+ or
391+ // `extensionStep` connects the static namespaces of classes.
392+ // Add the corresponding extension relation between the instance namespaces.
393+ result = this .toStaticNamespace ( ) .getAnExtension1 ( ) .toInstanceNamespace ( )
394+ }
395+
371396 private NamespaceNode getAnInheritanceParent1 ( ) { inheritanceStep ( result .ref ( ) , this ) }
372397
373398 /** Gets a namespace from which this namespace inherits directly. */
@@ -390,6 +415,8 @@ class NamespaceNode extends NameBindingNode {
390415 not this .hasOwnMember ( name ) and
391416 result = this .getAnInheritanceParent ( ) .getMember ( name ) and
392417 isInheritableMemberNode ( result )
418+ or
419+ result = this .getAnExtension ( ) .getMember ( name )
393420 }
394421}
395422
@@ -486,6 +513,9 @@ module DebugGraph<relevantNodeSig/1 relevantNode> {
486513 or
487514 inheritanceStep ( node1 , node2 ) and
488515 value = "inheritedBy"
516+ or
517+ extensionStep ( node1 , node2 ) and
518+ value = "extensionOf"
489519 )
490520 }
491521}
@@ -582,6 +612,17 @@ private module FolderHeuristic {
582612 }
583613}
584614
615+ private ClassLikeDeclaration resolveExtensionTarget ( ClassLikeDeclaration cls ) {
616+ trackNameBinding ( result .getNameNode ( ) ) = getNodeFromRef ( cls .getExtensionTarget ( ) )
617+ }
618+
619+ private ClassLikeDeclaration tryResolveExtensionTarget ( ClassLikeDeclaration cls ) {
620+ result = resolveExtensionTarget ( cls )
621+ or
622+ not exists ( resolveExtensionTarget ( cls ) ) and
623+ result = cls
624+ }
625+
585626/**
586627 * Holds if `access` may resolve to `target` through the enclosing `accessingClass`.
587628 *
@@ -610,7 +651,8 @@ private predicate unqualifiedMemberAccessCand(
610651 // Resolved in an uncertain scope
611652 exists ( NamespaceNode namespace , string name |
612653 name = access .getName ( ) and
613- accessingClass = LocalNameBindingOutput:: getAnUncertainScope ( access , name )
654+ accessingClass =
655+ tryResolveExtensionTarget ( LocalNameBindingOutput:: getAnUncertainScope ( access , name ) )
614656 |
615657 instanceAccess = true and
616658 namespace .isInstanceMemberNamespace ( accessingClass ) and
0 commit comments