Skip to content

Unified: Introduce local variables and 'self' bindings - #22547

Merged
asgerf merged 23 commits into
github:mainfrom
asgerf:unified/local-variables-and-self
Sep 11, 2026
Merged

Unified: Introduce local variables and 'self' bindings#22547
asgerf merged 23 commits into
github:mainfrom
asgerf:unified/local-variables-and-self

Conversation

@asgerf

@asgerf asgerf commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Expands name binding with:

  • LocalVariable with many of the usual predicates like getDeclaringCallable() and isCaptured().
  • LocalVariableAccess
  • UnqualifiedMemberAccess.getImplicitQualifierVariable for getting the LocalVariable corresponding to the implicit self access.
  • Some utilities like AstNode.getEnclosingCallable() (moved out from CFG)

Re-declarations of 'self' in Swift

There is a unique complication for Swift due to [weak self] and guard let self statements, which re-introduce new variables called self. I've decided to just treat self as "just another variable" so this doesn't become a weird corner case, but something that "just works" because the extractor inserts the correct variable-declarations:

let closure = { [weak self] in
  // Here, 'self' is an Option<C> referring to .some(<outer self>) if it
  // has not been GC'ed yet. Swift does not allow unqualified self access here.

  print(self) // Prints "Optional(<stringified self>)"

  // Unwrap the 'self' optional to get a strong reference.
  guard let self else { return }

  print(self) // Prints stringified self
}

We used the term declaration both for the identifier and the surrounding node declaring containing one or more such identifiers (possibly nested in a pattern).

It led to code like `getADeclaration().getDeclaration()`. We now use the term "binding" for identifiers in binding position, and declarations as contexts that establish the binding position.
This moves a handful of predicates into a module, parameterised by the
'accessCand' predicate. There are no other changes to predicates, they
are just moved.

This is to allow further variable-lookups at a later stage.
@asgerf asgerf added the no-change-note-required This PR does not need a change note label Sep 11, 2026
This caused name clashes in other languages.
Now that StaticNameBinding also has a module called 'Public' we get
conflicts when importing both. Adding a facade for importing both.
@asgerf
asgerf force-pushed the unified/local-variables-and-self branch from 8906ed2 to 57c071f Compare September 11, 2026 08:22
@asgerf
asgerf marked this pull request as ready for review September 11, 2026 08:44
@asgerf
asgerf requested review from a team as code owners September 11, 2026 08:44
@asgerf
asgerf requested review from hvitved and a balanced review from Copilot September 11, 2026 08:44

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.

Copilot review overview

🟡 Changes recommended

Unresolved moderate issues affect top-level declaration classification and capture detection.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Lite
Findings: 1 Low severity

New issues introduced by this change (1)
Severity Finding
Low severity shared/​namebinding/​codeql/​namebinding/​LocalNameBinding.qll — Correct the LocalAccess spelling in the comment
What changed in this PR

Introduces unified local-variable and implicit-self name binding, including capture tracking, and updates related consumers and tests.

Changes:

  • Adds local-variable, access, capture, and implicit-qualifier APIs.
  • Consolidates name-binding and enclosing-callable utilities.
  • Updates diagnostics, definitions, tests, and debugging tools.
File Changes
unified/​ql/​test/​library-tests/​static-name-binding/​test.ql Updates static binding tests.
unified/​ql/​test/​library-tests/​local-name-binding/​test.swift Adds capture expectations.
unified/​ql/​test/​library-tests/​local-name-binding/​test.ql Tests variables, captures, and implicit qualifiers.
unified/​ql/​test/​library-tests/​local-name-binding/​self_access.swift Tests Swift self scenarios.
unified/​ql/​test/​library-tests/​local-name-binding/​class_scope.swift Tests implicit instance receivers.
unified/​ql/​test/​library-tests/​definitions/​test.ql Updates definition tests.
unified/​ql/​src/​diagnostic/​StaticNameResolution.ql Uses unified bindings.
unified/​ql/​src/​diagnostic/​FilesCoveredByModuleManifest.ql Updates binding usage.
unified/​ql/​lib/​utils/​test/​TestUtils.qll Updates test binding utilities.
unified/​ql/​lib/​utils/​test/​CommentUtil.qll Supports hyphenated expectation tags.
unified/​ql/​lib/​unified.qll Exports the unified binding facade.
unified/​ql/​lib/​ide-contextual-queries/​definitions.ql Updates definition query types.
unified/​ql/​lib/​codeql/​unified/​internal/​StaticNameBinding.qll Renames APIs and resolves implicit receivers.
unified/​ql/​lib/​codeql/​unified/​internal/​NameBindingPluginSwift.qll Adds Swift receiver metadata.
unified/​ql/​lib/​codeql/​unified/​internal/​NameBindingPlugin.qll Adds a receiver plugin hook.
unified/​ql/​lib/​codeql/​unified/​internal/​NameBinding.qll Re-exports binding implementations.
unified/​ql/​lib/​codeql/​unified/​internal/​LocalNameBinding.qll Adds local-variable and capture modeling.
unified/​ql/​lib/​codeql/​unified/​internal/​FacadeAst.qll Adds callable and class ancestry helpers.
unified/​ql/​lib/​codeql/​unified/​internal/​dev/​debugStaticNameBindingGraph.ql Migrates debug graph imports.
unified/​ql/​lib/​codeql/​unified/​internal/​dev/​debugLocalNameBindingGraph.ql Migrates debug graph imports.
unified/​ql/​lib/​codeql/​unified/​internal/​ControlFlowGraph.qll Reuses the AST callable helper.
unified/​ql/​lib/​codeql/​unified/​internal/​AstExtra.qll Updates local declaration classification.
unified/​ql/​lib/​codeql/​unified/​internal/​AnalysisQuality.qll Migrates analysis types.
unified/​ql/​lib/​codeql/​Definitions.qll Updates definition binding types.
shared/​namebinding/​codeql/​namebinding/​LocalNameBinding.qll Adds extensible access resolution.

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

Comment thread shared/namebinding/codeql/namebinding/LocalNameBinding.qll Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@hvitved hvitved 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.

Looks great, some (mostly minor) comments.

Comment thread unified/ql/lib/codeql/unified/internal/FacadeAst.qll Outdated
Comment thread unified/ql/lib/codeql/unified/internal/FacadeAst.qll Outdated
Comment thread unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll Outdated
Comment thread unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll
Comment thread unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll Outdated
Comment thread unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll Outdated
Comment thread unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll
Comment thread unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll
@asgerf

asgerf commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

@asgerf

asgerf commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

@hvitved I pushed two more commits as I noticed AnalysisQuality.qll did not recognise a resolved self access as a success. Starting another DCA run.

@asgerf
asgerf merged commit e452286 into github:main Sep 11, 2026
111 of 112 checks passed
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