Skip to content

unified: Build instance-member namespace and resolve unqualified lookups - #22420

Draft
asgerf wants to merge 10 commits into
github:mainfrom
asgerf:unified/instance-members
Draft

unified: Build instance-member namespace and resolve unqualified lookups#22420
asgerf wants to merge 10 commits into
github:mainfrom
asgerf:unified/instance-members

Conversation

@asgerf

@asgerf asgerf commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

This PR does a few things:

  • Uses the name-binding graph to build up the namespace of instance members in a class. We can't find accesses to these in this phase, but we can provide lookup capability in a given class.
  • Unqualified accesses are now resolved more generally, including instance-member accesses (implicit self access).
  • Exposes the UnqualifiedMemberAccess class, which should provide the needed information to consumers (type inference and data flow), without leaking internal details about name-binding nodes.
  • Static name binding stats are more precise as the set of candidates now excludes identifiers that we can positively identify as depending to type inference, such as the x in foo().x.

asgerf added 10 commits August 24, 2026 10:46
I thought there was a bug here, but it turns out this works as intended. Makes sense to keep the test though.
Originally this node was intended to cover TopLevels as well, but things evolved differently, and this name works better since it's only for classes.
Avoids surprising edges cases when a class has no instance/static members
UnqualifiedMemberAccess might go into Public one day
@asgerf asgerf added the no-change-note-required This PR does not need a change note label Aug 25, 2026
* `instanceAccess` indicates if this this member should be accessed as an instance of `accessingClass`
* or as a static member.
*/
private predicate unqualifiedMemberAccessCand(

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds instance-member namespaces and shadowing-aware unqualified name resolution to unified Swift analysis.

Changes:

  • Models static and instance namespaces, including inheritance.
  • Exposes unqualified member bindings through definitions and tests.
  • Refines static name-resolution statistics.
Show a summary per file
File Description
unified/ql/test/library-tests/static-name-binding/unqualified-access.swift Updates inherited lookup expectations.
unified/ql/test/library-tests/static-name-binding/test.swift Adds enum-case binding coverage.
unified/ql/test/library-tests/static-name-binding/test.ql Tests the new binding API.
unified/ql/test/library-tests/static-name-binding/implicit-instance-field-access.swift Tests implicit instance-member access.
unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll Implements namespaces and unqualified lookup.
unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll Defines Swift inheritance behavior.
unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll Adds member classification extension points.
unified/ql/lib/codeql/unified/internal/FacadeAst.qll Adds AST depth calculation.
unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll Refines resolution statistics.
unified/ql/lib/codeql/Definitions.qll Uses shadowing-aware bindings for definitions.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Suppressed comments (2)

unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll:623

  • Use “an” before “unqualified.”
 * An identifier appearing in a unqualified position, referring to a member of an enclosing class.

unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll:36

  • Remove the extraneous article in this sentence.
      // Base expression is a confirmed to depend on type inference
  • Files reviewed: 10/10 changed files
  • Comments generated: 8
  • Review effort level: Balanced

this.isBulkImport(result)
or
this.isExportedNamespace(result)
this.isStaticMemberNamespace(result)
)
}

/** Holds if the member represented by `node` cannot be inherited. */
Comment on lines +43 to +45
exists(AstNode ref |
this = getIdentifierFromRef(ref) and
not memberAccessDependsOnTypeInference(ref)
Comment on lines +34 to +36
override predicate isInheritableMember(ClassLikeDeclaration cls, Member member) {
exists(cls) and
not member.hasModifier("private")
class BSub : B { // $ access=A.B
let x3: B = nil; // $ access=A.B
let x4: C = nil; // $ access=A.B.C SPURIOUS: access=Target3.C // spurious result from folder-based heuristic
let x4: C = nil; // $ access=A.B.C // spurious result from folder-based heuristic
Comment on lines +32 to +33
* The caller has already restricted `member` to be a member of `cls`, and
* ensured that `member` is a `VariableDeclaration` or `FunctionDeclaration`.
/**
* Holds if `access` may resolve to `target` through the enclosing `accessingClass`.
*
* `instanceAccess` indicates if this this member should be accessed as an instance of `accessingClass`
Comment on lines +10 to +11
* Holds if `name` has been positively identified as something that refer to a value, and static name binding
* is thus not expected to resolve its members.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-change-note-required This PR does not need a change note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants