Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Calls to function-valued computed properties incorrectly include accessor targets and add the property's Function type to the inferred call result.
5 open issues
Exclude property accessors from direct-call target lookup · New Do not register observer-only overrides as instance members · New Preserve static and class modifiers on extracted accessors · New Treat inout accessor operands as both reads and writes · New Exclude modified type expressions from generic type mentions · New
What changed in this PR
Extends Unified’s type inference to resolve Swift property getters and setters alongside stored fields.
Changes:
- Adds accessor-aware member lookup and read/write resolution.
- Preserves backing-field bindings, unwraps modified types, and distinguishes getter/setter names.
- Adds Swift accessor fixtures and updates type-inference expectations.
| File | Description |
|---|---|
unified/ql/test/library-tests/type-inference/type-inference.ql |
Checks accessor-resolution annotations. |
unified/ql/test/library-tests/type-inference/type-inference.expected |
Updates expected inferred types. |
unified/ql/test/library-tests/type-inference/accessors.swift |
Covers computed, observed, generic, inherited, and protocol properties. |
unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll |
Resolves modified types through their underlying type. |
unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll |
Models accessor fields and exposes accessor resolution. |
unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll |
Includes accessors in instance-member classification. |
unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll |
Prefers backing fields over associated accessors. |
unified/ql/lib/codeql/unified/internal/FacadeAst.qll |
Adds distinct getter and setter fallback names. |
Review effort: Max
06918fe to
16f5ce6
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved Swift accessor defects lose static/class modifiers, confuse static and instance names, and miss inout setters and top-level/local accessor references, breaking resolution and type inference.
3 open issues
5 resolved since last review
Review effort: Max
adec24a to
7ef8de1
Compare
7ef8de1 to
27ba20a
Compare
27ba20a to
5b468f2
Compare
| class InheritedAccessors: GenericAccessors<Int> { | ||
| override var observed: Int { | ||
| didSet { | ||
| let current = observed // $ accessor=InheritedAccessors.observed $ MISSING: type=current:Int field=GenericAccessors.observed -- static name binding is currently unable to resolve the access to the backing field |
There was a problem hiding this comment.
@asgerf : Is this perhaps something you can look into?
There was a problem hiding this comment.
🟡 Changes recommended
Class-valued properties passed directly as inout lose setter dataflow, while incompatible shadowing keys cause stored properties to resolve to spurious inherited setter targets.
2 open issues
2 resolved since last review
Review effort: Max
| name = p.getExternalNameNode().getValue() | ||
| or | ||
| not exists(p.getExternalNameNode()) and | ||
| name = "_" |
There was a problem hiding this comment.
I think we're OK with a spurious setter call
| // conservative value-type check | ||
| not forex(T::Type t | t = T::inferType(expr) | t.asClass().hasModifier("class")) |
There was a problem hiding this comment.
This makes sense although I'd add a boolean parameter to isInWriteBackPosition to remember if it's in direct write-back position.
asgerf
left a comment
There was a problem hiding this comment.
Some minor comments otherwise LGTM 👍
| } | ||
| var q: C { | ||
| get { C() } | ||
| set { sink(newValue.p.0) } // $ SPURIOUS: hasValueFlow=t11.2 |
There was a problem hiding this comment.
Add a comment to explain why it should not flow in here?
| // TODO: Check that 'expr' has a value type. Reference types don't cause writebacks. | ||
| isInWriteBackPosition(expr) and | ||
| // conservative value-type check | ||
| not forex(T::Type t | t = T::inferType(expr) | t.asClass().hasModifier("class")) |
There was a problem hiding this comment.
| not forex(T::Type t | t = T::inferType(expr) | t.asClass().hasModifier("class")) | |
| not forex(T::Type t | t = T::inferType(expr) | t.asClass().hasModifier(["class", "actor"])) |
I feel it would make sense to factor into a predicate isReferenceType(Type t). It should also hold for type parameters bounded by a reference-type but feel free to defer that for later. There should just be a single place that owns this.
| // conservative value-type check | ||
| not forex(T::Type t | t = T::inferType(expr) | t.asClass().hasModifier("class")) |
There was a problem hiding this comment.
This makes sense although I'd add a boolean parameter to isInWriteBackPosition to remember if it's in direct write-back position.
| name = p.getExternalNameNode().getValue() | ||
| or | ||
| not exists(p.getExternalNameNode()) and | ||
| name = "_" |
There was a problem hiding this comment.
I think we're OK with a spurious setter call



No description provided.