From 9fd04e62cfe3b4a0dabccaf7a121b09c6b22ac9d Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 29 Oct 2026 10:40:19 +0100 Subject: [PATCH 1/3] Unified: Add accessor FP/FN flow test --- .../dataflow/accessors_instance.swift | 9 ++++-- .../test/library-tests/dataflow/test.expected | 28 ++++++++++++++----- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/unified/ql/test/library-tests/dataflow/accessors_instance.swift b/unified/ql/test/library-tests/dataflow/accessors_instance.swift index 4771c48dc2a6..b4bcd8125c1b 100644 --- a/unified/ql/test/library-tests/dataflow/accessors_instance.swift +++ b/unified/ql/test/library-tests/dataflow/accessors_instance.swift @@ -131,11 +131,16 @@ func t11() { class C { var p: (String, String) { get { ("safe", "safe") } - set { sink(newValue.0) } // $ hasValueFlow=t11.1 + set { sink(newValue.0) } // $ hasValueFlow=t11.1 $ MISSING: hasValueFlow=t11.2 + } + var q: C { + get { C() } + set { sink(newValue.p.0) } // $ SPURIOUS: hasValueFlow=t11.2 } } let c = C() c.p.0 = source("t11.1") + c.q.p = (source("t11.2"), source("t11.3")) } func t12() { @@ -150,5 +155,5 @@ func t12() { } } let c = C() - c.s.update() // MISSING: the call to 'update' is not resolved + c.s.update() // MISSING: the call to 'update' is not resolved } diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 6d237e15124b..5b3c0d3e6065 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -32,9 +32,15 @@ edges | accessors_instance.swift:119:13:119:17 | field | accessors_instance.swift:125:18:125:22 | field | provenance | | | accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:119:13:119:17 | field | provenance | | | accessors_instance.swift:134:24:134:31 | newValue [0] | accessors_instance.swift:134:24:134:33 | ... .0 | provenance | | -| accessors_instance.swift:138:5:138:7 | [post] ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | -| accessors_instance.swift:138:5:138:9 | ... .0 | accessors_instance.swift:138:5:138:7 | [post] ... .p [0] | provenance | | -| accessors_instance.swift:138:13:138:27 | source(...) | accessors_instance.swift:138:5:138:9 | ... .0 | provenance | | +| accessors_instance.swift:138:24:138:31 | newValue [p, 0] | accessors_instance.swift:138:24:138:33 | ... .p [0] | provenance | | +| accessors_instance.swift:138:24:138:33 | ... .p [0] | accessors_instance.swift:138:24:138:35 | ... .0 | provenance | | +| accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | +| accessors_instance.swift:142:5:142:9 | ... .0 | accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | provenance | | +| accessors_instance.swift:142:13:142:27 | source(...) | accessors_instance.swift:142:5:142:9 | ... .0 | provenance | | +| accessors_instance.swift:143:5:143:7 | [post] ... .q [p, 0] | accessors_instance.swift:138:24:138:31 | newValue [p, 0] | provenance | | +| accessors_instance.swift:143:5:143:9 | ... .p [0] | accessors_instance.swift:143:5:143:7 | [post] ... .q [p, 0] | provenance | | +| accessors_instance.swift:143:13:143:46 | TupleExpr [0] | accessors_instance.swift:143:5:143:9 | ... .p [0] | provenance | | +| accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:143:13:143:46 | TupleExpr [0] | provenance | | | accessors_local.swift:7:21:7:34 | source(...) | accessors_local.swift:8:10:8:10 | x | provenance | | | accessors_local.swift:15:15:15:28 | source(...) | accessors_local.swift:17:10:17:10 | x | provenance | | | accessors_local.swift:22:15:22:28 | source(...) | accessors_local.swift:25:10:25:10 | x | provenance | | @@ -558,9 +564,16 @@ nodes | accessors_instance.swift:125:18:125:22 | field | semmle.label | field | | accessors_instance.swift:134:24:134:31 | newValue [0] | semmle.label | newValue [0] | | accessors_instance.swift:134:24:134:33 | ... .0 | semmle.label | ... .0 | -| accessors_instance.swift:138:5:138:7 | [post] ... .p [0] | semmle.label | [post] ... .p [0] | -| accessors_instance.swift:138:5:138:9 | ... .0 | semmle.label | ... .0 | -| accessors_instance.swift:138:13:138:27 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:138:24:138:31 | newValue [p, 0] | semmle.label | newValue [p, 0] | +| accessors_instance.swift:138:24:138:33 | ... .p [0] | semmle.label | ... .p [0] | +| accessors_instance.swift:138:24:138:35 | ... .0 | semmle.label | ... .0 | +| accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | semmle.label | [post] ... .p [0] | +| accessors_instance.swift:142:5:142:9 | ... .0 | semmle.label | ... .0 | +| accessors_instance.swift:142:13:142:27 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:143:5:143:7 | [post] ... .q [p, 0] | semmle.label | [post] ... .q [p, 0] | +| accessors_instance.swift:143:5:143:9 | ... .p [0] | semmle.label | ... .p [0] | +| accessors_instance.swift:143:13:143:46 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| accessors_instance.swift:143:14:143:28 | source(...) | semmle.label | source(...) | | accessors_local.swift:7:21:7:34 | source(...) | semmle.label | source(...) | | accessors_local.swift:8:10:8:10 | x | semmle.label | x | | accessors_local.swift:15:15:15:28 | source(...) | semmle.label | source(...) | @@ -1191,7 +1204,8 @@ testFailures | accessors_instance.swift:99:10:99:21 | ... .otherField | accessors_instance.swift:97:15:97:28 | source(...) | accessors_instance.swift:99:10:99:21 | ... .otherField | $@ | accessors_instance.swift:97:15:97:28 | source(...) | source(...) | | accessors_instance.swift:112:10:112:16 | ... .field | accessors_instance.swift:106:21:106:34 | source(...) | accessors_instance.swift:112:10:112:16 | ... .field | $@ | accessors_instance.swift:106:21:106:34 | source(...) | source(...) | | accessors_instance.swift:125:18:125:22 | field | accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:125:18:125:22 | field | $@ | accessors_instance.swift:119:21:119:35 | source(...) | source(...) | -| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:138:13:138:27 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:138:13:138:27 | source(...) | source(...) | +| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:142:13:142:27 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:142:13:142:27 | source(...) | source(...) | +| accessors_instance.swift:138:24:138:35 | ... .0 | accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:138:24:138:35 | ... .0 | $@ | accessors_instance.swift:143:14:143:28 | source(...) | source(...) | | accessors_local.swift:8:10:8:10 | x | accessors_local.swift:7:21:7:34 | source(...) | accessors_local.swift:8:10:8:10 | x | $@ | accessors_local.swift:7:21:7:34 | source(...) | source(...) | | accessors_local.swift:17:10:17:10 | x | accessors_local.swift:15:15:15:28 | source(...) | accessors_local.swift:17:10:17:10 | x | $@ | accessors_local.swift:15:15:15:28 | source(...) | source(...) | | accessors_local.swift:25:10:25:10 | x | accessors_local.swift:22:15:22:28 | source(...) | accessors_local.swift:25:10:25:10 | x | $@ | accessors_local.swift:22:15:22:28 | source(...) | source(...) | From 5b468f21a53a0c1ea6fdb187639da51bdf79befc Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Oct 2026 20:58:22 +0100 Subject: [PATCH 2/3] Unified: Resolve accessor accesses in type inference --- .../internal/NameBindingPluginSwift.qll | 12 +- .../unified/internal/dataflow/CallGraph.qll | 21 +- .../internal/dataflow/DataFlowCall.qll | 15 +- .../internal/dataflow/DataFlowPluginSwift.qll | 7 +- .../internal/typeinference/TypeInference.qll | 86 ++++-- .../internal/typeinference/TypeMention.qll | 14 +- .../dataflow/accessors_instance.swift | 8 +- .../dataflow/accessors_local.swift | 4 +- .../test/library-tests/dataflow/test.expected | 31 +- .../type-inference/accessors.swift | 192 ++++++++++++ .../type-inference/type-inference.expected | 281 ++++++++++++++++++ .../type-inference/type-inference.ql | 14 +- 12 files changed, 609 insertions(+), 76 deletions(-) create mode 100644 unified/ql/test/library-tests/type-inference/accessors.swift diff --git a/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll index 103c8ff7d0a7..846bb2ba8c67 100644 --- a/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/NameBindingPluginSwift.qll @@ -88,10 +88,16 @@ class NameBindingPluginSwift extends NameBindingPlugin { or not m instanceof GeneratedConstructor and result = - concat(int i, Parameter p | - p = m.(Callable).getParameter(i) + concat(int i, Parameter p, string name | + p = m.(Callable).getParameter(i) and + ( + name = p.getExternalNameNode().getValue() + or + not exists(p.getExternalNameNode()) and + name = "_" + ) | - p.getExternalNameNode().getValue(), "," order by i + name, "," order by i ) } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll index bd8314dcaee6..b035a03c817a 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll @@ -7,26 +7,9 @@ private import codeql.unified.internal.ExprPositions DataFlowCallable viableCallable(DataFlowCall c) { result.asSourceCallable() = T::resolveCallTarget(c.asExplicitCall()) or - result.asSourceCallable() = resolveAccessor(c.asGetterCall()).(GetterDeclaration) + result.asSourceCallable() = T::resolveAccessorAccess(c.asGetterCall()).(GetterDeclaration) or - result.asSourceCallable() = resolveAccessor(c.asSetterCall()).(SetterDeclaration) + result.asSourceCallable() = T::resolveAccessorAccess(c.asSetterCall()).(SetterDeclaration) or result.asSummarizedCallable().matchesCall(c.asExplicitCall()) } - -bindingset[access] -pragma[inline_late] -private N::NamespaceNode resolveBase(MemberAccessExpr access) { - result.isInstanceMemberNamespace(T::inferType(access.getBase()).asClass()) -} - -private AccessorDeclaration resolveAccessor(Expr access) { - result.getNameNode() = N::getStaticBindingTargetFromRef(access) - or - not exists(N::getStaticBindingTargetFromRef(access)) and - exists(MemberAccessExpr m | - access = m and - // TODO: Make type inference own accessor-resolution. It also needs it internally. - resolveBase(m).getMember(m.getMemberName()).isMember(result) - ) -} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll index f99f1f90e37a..3d4b32e191fc 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll @@ -2,6 +2,7 @@ private import unified private import AllDataFlow private import codeql.unified.internal.ExprPositions private import codeql.unified.internal.NameBinding as N +private import codeql.unified.internal.typeinference.TypeInference as T private newtype TDataFlowCall = TExplicitCall(CallExpr call) { @@ -9,12 +10,7 @@ private newtype TDataFlowCall = } or TGetterCall(Expr expr) { hasResultValue(expr) and - ( - N::getStaticBindingTargetFromRef(expr) = any(GetterDeclaration g).getNameNode() - or - // Assume a qualified member accesses might resolve to a getter. - expr instanceof MemberAccessExpr - ) + T::resolveAccessorAccess(expr) instanceof GetterDeclaration } or TSetterCall(Expr expr) { ( @@ -24,12 +20,7 @@ private newtype TDataFlowCall = hasPostUpdateAfterCallOrAssign(expr, _) and mayWriteBackViaSetterCall(expr) ) and - ( - N::getStaticBindingTargetFromRef(expr) = any(SetterDeclaration g).getNameNode() - or - // Assume a qualified member accesses might resolve to a setter. - expr instanceof MemberAccessExpr - ) + T::resolveAccessorAccess(expr) instanceof SetterDeclaration } or TSummaryCall(SummarizedCallable callable, SummaryNode callee) { FlowSummaryOutput::summaryCallbackRange(callable, callee) diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index 3d021a2f7aa5..b71fbf16120c 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -13,11 +13,12 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { contents.asSingleton().asNamedMember() = "some.0" } - // Swift writes back mutated values of computed properties via their setter + // Swift writes back mutated values of computed value-type properties via their setter bindingset[expr] override predicate mayWriteBackViaSetterCall(Expr expr) { - isInWriteBackPosition(expr) - // 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")) } override predicate exprStep(Expr expr1, Expr expr2, boolean mustFlow) { diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll index c5572af2ae34..85a1c0281cfb 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll @@ -123,6 +123,8 @@ module Input3 implements InputSig3 { predicate cacheRevRef() { (exists(resolveFieldAccess(_)) implies any()) or + (exists(resolveAccessorAccess(_)) implies any()) + or (exists(resolveCallTarget(_)) implies any()) } @@ -252,20 +254,38 @@ module Input3 implements InputSig3 { override Location getLocation() { result = decl.getLocation() } } - class Field extends AstVariableDeclaration { + final class Field = FieldImpl; + + abstract private class FieldImpl extends DeclarationImpl { + abstract AstNode getAstNode(); + } + + private class VariableDeclarationField extends FieldImpl, AstVariableDeclaration { ClassLikeDeclaration cls; - Field() { decl = cls.getAMember() } + VariableDeclarationField() { decl = cls.getAMember() } + + override AstNode getAstNode() { result = this.getDecl() } override TypeMention getDeclaringType() { result = getClassLikeDeclarationTypeMention(cls) } } - private class EnumField_ extends Field { + private class EnumField_ extends VariableDeclarationField { override EnumField decl; override TypeMention getType() { result = decl.getEnum().getNameNode() } } + private class AccessorField extends FieldImpl, Callable { + private AccessorDeclaration accessor; + + AccessorField() { accessor = this.getCallable() } + + override TypeMention getType() { result = accessor.getReturnType() } + + override AstNode getAstNode() { result = accessor } + } + abstract class FieldAccess extends Expr { abstract Expr getReceiver(); @@ -275,7 +295,7 @@ module Input3 implements InputSig3 { pragma[nomagic] private predicate lookupMember0(MemberAccessExpr mae, ClassLikeDeclaration cls, string name) { not exists(getStaticBindingTargetFromRef(mae)) and // if a static member can be resolved, use it - cls = inferType(mae.getBase()).(ClassLikeDeclarationType).getClassLikeDeclaration() and + cls = inferType(mae.getBase()).asClass() and name = mae.getMemberName() } @@ -303,7 +323,7 @@ module Input3 implements InputSig3 { // also mutual recursion exists(TupleType tuple, string name | this.isTupleProjection(tuple, name) and - result.getDecl() = tuple.getField(name) + result.getAstNode() = tuple.getField(name) ) or // no mutual recursion; can be resolved directly with static name binding @@ -325,6 +345,18 @@ module Input3 implements InputSig3 { override Field getField() { result.getNameNode() = this.getTarget() } } + private class LocalAccessorFieldAccess extends FieldAccess, LocalNameAccess { + AccessorDeclaration target; + + LocalAccessorFieldAccess() { + target = getStaticBindingTargetFromIdentifier(this).getDeclaration() + } + + override Expr getReceiver() { none() } + + override Field getField() { result.getAstNode() = target } + } + Type inferFieldAccessReceiverType(FieldAccess fa, TypePath path) { // instance field with explicit receiver result = inferType(fa.getReceiver(), path) @@ -506,17 +538,21 @@ module Input3 implements InputSig3 { } Callable getTargetImpl(boolean isFunctionExprInvoke) { - // mutual recursion; call with explicit receiver: `obj.foo(42)` - result = lookupInstanceMember(this.getCallee()) and - isFunctionExprInvoke = false - or - // mutual recursion; call to a function expression: `callback(42)` - result.asCallable() = this.getInvokeTarget(_) and - isFunctionExprInvoke = true - or - // no mutual recursion; can be resolved directly with static name binding - result = this.getTargetViaStaticNameBinding() and - isFunctionExprInvoke = false + ( + // mutual recursion; call with explicit receiver: `obj.foo(42)` + result = lookupInstanceMember(this.getCallee()) and + isFunctionExprInvoke = false + or + // mutual recursion; call to a function expression: `callback(42)` + result.asCallable() = this.getInvokeTarget(_) and + isFunctionExprInvoke = true + or + // no mutual recursion; can be resolved directly with static name binding + result = this.getTargetViaStaticNameBinding() and + isFunctionExprInvoke = false + ) and + // handled as a field access + not result.asCallable() instanceof AccessorDeclaration } Callable getTarget(InvocationResolutionContext c) { @@ -835,7 +871,23 @@ module CachedStage = M3::CachedStage; cached VariableDeclaration resolveFieldAccess(Expr access) { CachedStage::ref() and - result = access.(Input3::FieldAccess).getField().getDecl() + result = access.(Input3::FieldAccess).getField().getAstNode() +} + +/** + * Gets the accessor that is accessed by the given expression. + * + * Note that we do not attempt to distinguish between different kinds of + * accessors; all accessors belonging to the same declaration will be + * returned (for example both the getter and setter of a property). + * + * `access` is either a `MemberAccessExpr` or an `Identifier` (when + * the field is accessed with an implicit receiver). + */ +cached +AccessorDeclaration resolveAccessorAccess(Expr access) { + CachedStage::ref() and + result = access.(Input3::FieldAccess).getField().getAstNode() } /** diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll index aa48459feeb0..baef8f970e26 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeMention.qll @@ -24,7 +24,11 @@ abstract private class TypeMentionImpl extends AstNode { final class ExprTypeMention = ExprTypeMentionImpl; abstract private class ExprTypeMentionImpl extends TypeMentionImpl, Expr { - ExprTypeMentionImpl() { isInTypeContext(this) and not this instanceof GenericTypeExpr } + ExprTypeMentionImpl() { + isInTypeContext(this) and + not this instanceof GenericTypeExpr and + not this instanceof ModifiedTypeExpr + } pragma[nomagic] abstract TypePath getTypeArgumentPath(int i); @@ -191,6 +195,14 @@ private class ClassLikeDeclarationTypeMention extends TypeMentionImpl, Identifie } } +private class ModifiedTypeExprMention extends TypeMentionImpl instanceof ModifiedTypeExpr { + private TypeMention base; + + ModifiedTypeExprMention() { base = ModifiedTypeExpr.super.getType() } + + override Type getTypeAt(TypePath path) { result = base.getTypeAt(path) } +} + TypeMention getClassLikeDeclarationTypeMention(ClassLikeDeclaration c) { result = c.getNameNode() or diff --git a/unified/ql/test/library-tests/dataflow/accessors_instance.swift b/unified/ql/test/library-tests/dataflow/accessors_instance.swift index b4bcd8125c1b..b9daa47f45b5 100644 --- a/unified/ql/test/library-tests/dataflow/accessors_instance.swift +++ b/unified/ql/test/library-tests/dataflow/accessors_instance.swift @@ -131,11 +131,11 @@ func t11() { class C { var p: (String, String) { get { ("safe", "safe") } - set { sink(newValue.0) } // $ hasValueFlow=t11.1 $ MISSING: hasValueFlow=t11.2 + set { sink(newValue.0) } // $ hasValueFlow=t11.1 hasValueFlow=t11.2 } var q: C { get { C() } - set { sink(newValue.p.0) } // $ SPURIOUS: hasValueFlow=t11.2 + set { sink(newValue.p.0) } } } let c = C() @@ -151,9 +151,9 @@ func t12() { class C { var s: S { get { S() } - set { sink(newValue.field) } // $ MISSING: hasValueFlow=t12.1 + set { sink(newValue.field) } // $ hasValueFlow=t12.1 } } let c = C() - c.s.update() // MISSING: the call to 'update' is not resolved + c.s.update() } diff --git a/unified/ql/test/library-tests/dataflow/accessors_local.swift b/unified/ql/test/library-tests/dataflow/accessors_local.swift index 51c5a62e2894..0cfa68c3abce 100644 --- a/unified/ql/test/library-tests/dataflow/accessors_local.swift +++ b/unified/ql/test/library-tests/dataflow/accessors_local.swift @@ -100,9 +100,7 @@ func t12() { } var p: S { get { S() } - set { sink(newValue.field) } // $ MISSING: hasValueFlow=t12.1 + set { sink(newValue.field) } // $ hasValueFlow=t12.1 } - // TODO: test fails because the call to 'mutate' is not yet resolved - // At the time of writing, the test passes if the call graph is manually patched p.mutate() } diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 5b3c0d3e6065..2af1a83d8f56 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -32,15 +32,16 @@ edges | accessors_instance.swift:119:13:119:17 | field | accessors_instance.swift:125:18:125:22 | field | provenance | | | accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:119:13:119:17 | field | provenance | | | accessors_instance.swift:134:24:134:31 | newValue [0] | accessors_instance.swift:134:24:134:33 | ... .0 | provenance | | -| accessors_instance.swift:138:24:138:31 | newValue [p, 0] | accessors_instance.swift:138:24:138:33 | ... .p [0] | provenance | | -| accessors_instance.swift:138:24:138:33 | ... .p [0] | accessors_instance.swift:138:24:138:35 | ... .0 | provenance | | | accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | | accessors_instance.swift:142:5:142:9 | ... .0 | accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | provenance | | | accessors_instance.swift:142:13:142:27 | source(...) | accessors_instance.swift:142:5:142:9 | ... .0 | provenance | | -| accessors_instance.swift:143:5:143:7 | [post] ... .q [p, 0] | accessors_instance.swift:138:24:138:31 | newValue [p, 0] | provenance | | -| accessors_instance.swift:143:5:143:9 | ... .p [0] | accessors_instance.swift:143:5:143:7 | [post] ... .q [p, 0] | provenance | | +| accessors_instance.swift:143:5:143:9 | ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | | accessors_instance.swift:143:13:143:46 | TupleExpr [0] | accessors_instance.swift:143:5:143:9 | ... .p [0] | provenance | | | accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:143:13:143:46 | TupleExpr [0] | provenance | | +| accessors_instance.swift:149:34:149:38 | field | accessors_instance.swift:158:5:158:7 | [post] ... .s [field] | provenance | | +| accessors_instance.swift:149:42:149:56 | source(...) | accessors_instance.swift:149:34:149:38 | field | provenance | | +| accessors_instance.swift:154:24:154:31 | newValue [field] | accessors_instance.swift:154:24:154:37 | ... .field | provenance | | +| accessors_instance.swift:158:5:158:7 | [post] ... .s [field] | accessors_instance.swift:154:24:154:31 | newValue [field] | provenance | | | accessors_local.swift:7:21:7:34 | source(...) | accessors_local.swift:8:10:8:10 | x | provenance | | | accessors_local.swift:15:15:15:28 | source(...) | accessors_local.swift:17:10:17:10 | x | provenance | | | accessors_local.swift:22:15:22:28 | source(...) | accessors_local.swift:25:10:25:10 | x | provenance | | @@ -64,6 +65,10 @@ edges | accessors_local.swift:93:5:93:5 | [post] p [0] | accessors_local.swift:91:20:91:27 | newValue [0] | provenance | | | accessors_local.swift:93:5:93:7 | ... .0 | accessors_local.swift:93:5:93:5 | [post] p [0] | provenance | | | accessors_local.swift:93:11:93:25 | source(...) | accessors_local.swift:93:5:93:7 | ... .0 | provenance | | +| accessors_local.swift:99:34:99:38 | field | accessors_local.swift:105:5:105:5 | [post] p [field] | provenance | | +| accessors_local.swift:99:42:99:56 | source(...) | accessors_local.swift:99:34:99:38 | field | provenance | | +| accessors_local.swift:103:20:103:27 | newValue [field] | accessors_local.swift:103:20:103:33 | ... .field | provenance | | +| accessors_local.swift:105:5:105:5 | [post] p [field] | accessors_local.swift:103:20:103:27 | newValue [field] | provenance | | | accessors_static.swift:8:32:8:45 | source(...) | accessors_static.swift:10:10:10:12 | ... .x | provenance | | | accessors_static.swift:18:19:18:32 | source(...) | accessors_static.swift:21:10:21:12 | ... .x | provenance | | | accessors_static.swift:27:19:27:32 | source(...) | accessors_static.swift:31:10:31:12 | ... .x | provenance | | @@ -564,16 +569,17 @@ nodes | accessors_instance.swift:125:18:125:22 | field | semmle.label | field | | accessors_instance.swift:134:24:134:31 | newValue [0] | semmle.label | newValue [0] | | accessors_instance.swift:134:24:134:33 | ... .0 | semmle.label | ... .0 | -| accessors_instance.swift:138:24:138:31 | newValue [p, 0] | semmle.label | newValue [p, 0] | -| accessors_instance.swift:138:24:138:33 | ... .p [0] | semmle.label | ... .p [0] | -| accessors_instance.swift:138:24:138:35 | ... .0 | semmle.label | ... .0 | | accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | semmle.label | [post] ... .p [0] | | accessors_instance.swift:142:5:142:9 | ... .0 | semmle.label | ... .0 | | accessors_instance.swift:142:13:142:27 | source(...) | semmle.label | source(...) | -| accessors_instance.swift:143:5:143:7 | [post] ... .q [p, 0] | semmle.label | [post] ... .q [p, 0] | | accessors_instance.swift:143:5:143:9 | ... .p [0] | semmle.label | ... .p [0] | | accessors_instance.swift:143:13:143:46 | TupleExpr [0] | semmle.label | TupleExpr [0] | | accessors_instance.swift:143:14:143:28 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:149:34:149:38 | field | semmle.label | field | +| accessors_instance.swift:149:42:149:56 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:154:24:154:31 | newValue [field] | semmle.label | newValue [field] | +| accessors_instance.swift:154:24:154:37 | ... .field | semmle.label | ... .field | +| accessors_instance.swift:158:5:158:7 | [post] ... .s [field] | semmle.label | [post] ... .s [field] | | accessors_local.swift:7:21:7:34 | source(...) | semmle.label | source(...) | | accessors_local.swift:8:10:8:10 | x | semmle.label | x | | accessors_local.swift:15:15:15:28 | source(...) | semmle.label | source(...) | @@ -608,6 +614,11 @@ nodes | accessors_local.swift:93:5:93:5 | [post] p [0] | semmle.label | [post] p [0] | | accessors_local.swift:93:5:93:7 | ... .0 | semmle.label | ... .0 | | accessors_local.swift:93:11:93:25 | source(...) | semmle.label | source(...) | +| accessors_local.swift:99:34:99:38 | field | semmle.label | field | +| accessors_local.swift:99:42:99:56 | source(...) | semmle.label | source(...) | +| accessors_local.swift:103:20:103:27 | newValue [field] | semmle.label | newValue [field] | +| accessors_local.swift:103:20:103:33 | ... .field | semmle.label | ... .field | +| accessors_local.swift:105:5:105:5 | [post] p [field] | semmle.label | [post] p [field] | | accessors_static.swift:8:32:8:45 | source(...) | semmle.label | source(...) | | accessors_static.swift:10:10:10:12 | ... .x | semmle.label | ... .x | | accessors_static.swift:18:19:18:32 | source(...) | semmle.label | source(...) | @@ -1205,7 +1216,8 @@ testFailures | accessors_instance.swift:112:10:112:16 | ... .field | accessors_instance.swift:106:21:106:34 | source(...) | accessors_instance.swift:112:10:112:16 | ... .field | $@ | accessors_instance.swift:106:21:106:34 | source(...) | source(...) | | accessors_instance.swift:125:18:125:22 | field | accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:125:18:125:22 | field | $@ | accessors_instance.swift:119:21:119:35 | source(...) | source(...) | | accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:142:13:142:27 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:142:13:142:27 | source(...) | source(...) | -| accessors_instance.swift:138:24:138:35 | ... .0 | accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:138:24:138:35 | ... .0 | $@ | accessors_instance.swift:143:14:143:28 | source(...) | source(...) | +| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:143:14:143:28 | source(...) | source(...) | +| accessors_instance.swift:154:24:154:37 | ... .field | accessors_instance.swift:149:42:149:56 | source(...) | accessors_instance.swift:154:24:154:37 | ... .field | $@ | accessors_instance.swift:149:42:149:56 | source(...) | source(...) | | accessors_local.swift:8:10:8:10 | x | accessors_local.swift:7:21:7:34 | source(...) | accessors_local.swift:8:10:8:10 | x | $@ | accessors_local.swift:7:21:7:34 | source(...) | source(...) | | accessors_local.swift:17:10:17:10 | x | accessors_local.swift:15:15:15:28 | source(...) | accessors_local.swift:17:10:17:10 | x | $@ | accessors_local.swift:15:15:15:28 | source(...) | source(...) | | accessors_local.swift:25:10:25:10 | x | accessors_local.swift:22:15:22:28 | source(...) | accessors_local.swift:25:10:25:10 | x | $@ | accessors_local.swift:22:15:22:28 | source(...) | source(...) | @@ -1217,6 +1229,7 @@ testFailures | accessors_local.swift:70:14:70:14 | x | accessors_local.swift:72:9:72:22 | source(...) | accessors_local.swift:70:14:70:14 | x | $@ | accessors_local.swift:72:9:72:22 | source(...) | source(...) | | accessors_local.swift:82:14:82:15 | x3 | accessors_local.swift:84:9:84:22 | source(...) | accessors_local.swift:82:14:82:15 | x3 | $@ | accessors_local.swift:84:9:84:22 | source(...) | source(...) | | accessors_local.swift:91:20:91:29 | ... .0 | accessors_local.swift:93:11:93:25 | source(...) | accessors_local.swift:91:20:91:29 | ... .0 | $@ | accessors_local.swift:93:11:93:25 | source(...) | source(...) | +| accessors_local.swift:103:20:103:33 | ... .field | accessors_local.swift:99:42:99:56 | source(...) | accessors_local.swift:103:20:103:33 | ... .field | $@ | accessors_local.swift:99:42:99:56 | source(...) | source(...) | | accessors_static.swift:10:10:10:12 | ... .x | accessors_static.swift:8:32:8:45 | source(...) | accessors_static.swift:10:10:10:12 | ... .x | $@ | accessors_static.swift:8:32:8:45 | source(...) | source(...) | | accessors_static.swift:21:10:21:12 | ... .x | accessors_static.swift:18:19:18:32 | source(...) | accessors_static.swift:21:10:21:12 | ... .x | $@ | accessors_static.swift:18:19:18:32 | source(...) | source(...) | | accessors_static.swift:31:10:31:12 | ... .x | accessors_static.swift:27:19:27:32 | source(...) | accessors_static.swift:31:10:31:12 | ... .x | $@ | accessors_static.swift:27:19:27:32 | source(...) | source(...) | diff --git a/unified/ql/test/library-tests/type-inference/accessors.swift b/unified/ql/test/library-tests/type-inference/accessors.swift new file mode 100644 index 000000000000..eee4b8e14207 --- /dev/null +++ b/unified/ql/test/library-tests/type-inference/accessors.swift @@ -0,0 +1,192 @@ +// --- Computed properties --- + +class AccessorContainer { + var stored: Int = 0 + + var implicitGetter: Int { + return stored // $ type=stored:Int field=AccessorContainer.stored + } + + var explicitGetter: String { + let value = "accessor" // $ type=value:String + return value // $ type=value:String + } + + var writable: Int { + get { + return stored // $ type=stored:Int field=AccessorContainer.stored + } + set { + stored = newValue // $ field=AccessorContainer.stored type=newValue:Int + } + } + + var namedSetter: Int { + get { + return stored // $ type=stored:Int field=AccessorContainer.stored + } + set(value) { + stored = value // $ field=AccessorContainer.stored type=value:Int + } + } +} + +func testComputedProperties() { + let container = AccessorContainer() // $ target=AccessorContainer.init + let implicitValue = container.implicitGetter // $ type=implicitValue:Int accessor=AccessorContainer.implicitGetter + let explicitValue = container.explicitGetter // $ type=explicitValue:String accessor=AccessorContainer.explicitGetter + let writableValue = container.writable // $ type=writableValue:Int accessor=AccessorContainer.writable + container.writable = 42 // $ accessor=AccessorContainer.writable + container.writable += 0 // $ accessor=AccessorContainer.writable accessor=AccessorContainer.writable + let namedValue = container.namedSetter // $ type=namedValue:Int accessor=AccessorContainer.namedSetter + container.namedSetter = 43 // $ accessor=AccessorContainer.namedSetter +} + +// --- Stored properties with observers --- + +class ObservedProperties { + var observed: Int = 0 { + willSet { + let current = observed // $ type=current:Int field=ObservedProperties.observed accessor=ObservedProperties.observed + let incoming = newValue // $ type=incoming:Int + } + didSet { + let current = observed // $ type=current:Int field=ObservedProperties.observed accessor=ObservedProperties.observed + let previous = oldValue // $ type=previous:Int + } + } + + var namedObservers: String = "" { + willSet(incoming) { + let value = incoming // $ type=value:String + } + didSet(previous) { + let value = previous // $ type=value:String + } + } +} + +func testObservedProperties() { + let container = ObservedProperties() // $ target=ObservedProperties.init + let observed = container.observed // $ type=observed:Int accessor=ObservedProperties.observed field=ObservedProperties.observed + container.observed = 42 // $ accessor=ObservedProperties.observed field=ObservedProperties.observed + let named = container.namedObservers // $ type=named:String accessor=ObservedProperties.namedObservers field=ObservedProperties.namedObservers + container.namedObservers = "updated" // $ accessor=ObservedProperties.namedObservers field=ObservedProperties.namedObservers +} + +// --- Generic and inherited accessors --- + +class GenericAccessors { + var stored: T + + init(value: T) { + stored = value // $ type=value:T field=GenericAccessors.stored + } + + var value: T { + get { + return stored // $ type=stored:T field=GenericAccessors.stored + } + set { + stored = newValue // $ field=GenericAccessors.stored type=newValue:T + } + } + + var observed: Int = 0 { + didSet { + let current = observed // $ type=current:Int field=GenericAccessors.observed accessor=GenericAccessors.observed + let previous = oldValue // $ type=previous:Int + } + } +} + +class InheritedAccessors: GenericAccessors { + 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 + let current2 = self.observed // $ accessor=InheritedAccessors.observed type=current2:Int field=GenericAccessors.observed + let previous = oldValue // $ type=previous:Int + } + } +} + +extension GenericAccessors { + var extensionValue: T { + return stored // $ field=GenericAccessors.stored $ MISSING: type=stored:T + } +} + +func testGenericAccessors() { + let container = GenericAccessors(value: "value") // $ target=GenericAccessors.init + let value = container.value // $ type=value:String accessor=GenericAccessors.value + let extensionValue = container.extensionValue // $ accessor=GenericAccessors.extensionValue $ MISSING: type=extensionValue:String + let inherited = InheritedAccessors(value: 42) // $ target=GenericAccessors.init + let inheritedValue = inherited.value // $ type=inheritedValue:Int accessor=GenericAccessors.value + let inheritedObserver = inherited.observed // $ type=inheritedObserver:Int field=GenericAccessors.observed accessor=InheritedAccessors.observed +} + +// --- Protocol accessor requirements --- + +protocol AccessorRequirements { + var readOnly: Int { get } + var readWrite: String { get set } +} + +func testAccessorRequirements(_ value: inout AccessorRequirements) { + let readOnly = value.readOnly // $ type=readOnly:Int accessor=AccessorRequirements.readOnly + let readWrite = value.readWrite // $ type=readWrite:String accessor=AccessorRequirements.readWrite + value.readWrite = "updated" // $ accessor=AccessorRequirements.readWrite +} + +// --- Function-valued properties --- +class C { + var callback: () -> String { { "x" } } + + func f() { + let result = callback() // $ type=result:String accessor=C.callback target=Function.callAsFunction + let result2 = self.callback() // $ type=result2:String accessor=C.callback target=Function.callAsFunction + } +} + +// --- Static and class accessors --- + +class StaticAccessors { + static var stored: Int = 0 + + static var value: Int { + get { + return stored // $ type=stored:Int field=StaticAccessors.stored + } + set { + stored = newValue // $ field=StaticAccessors.stored type=newValue:Int + } + } +} + +func testStaticAccessors() { + let value = StaticAccessors.value // $ type=value:Int accessor=StaticAccessors.value + StaticAccessors.value = 42 // $ accessor=StaticAccessors.value +} + +class ClassAccessors { + static var stored: Int = 0 + + class var value: Int { + get { + return stored // $ type=stored:Int field=ClassAccessors.stored + } + set { + stored = newValue // $ field=ClassAccessors.stored type=newValue:Int + } + } +} + +func testClassAccessors() { + let value = ClassAccessors.value // $ type=value:Int accessor=ClassAccessors.value + ClassAccessors.value = 42 // $ accessor=ClassAccessors.value +} + +// --- Top-level accessors --- + +var answer: Int { 42 } +let x = answer // $ type=x:Int accessor=answer diff --git a/unified/ql/test/library-tests/type-inference/type-inference.expected b/unified/ql/test/library-tests/type-inference/type-inference.expected index 3ce1618a9164..b88ac707d220 100644 --- a/unified/ql/test/library-tests/type-inference/type-inference.expected +++ b/unified/ql/test/library-tests/type-inference/type-inference.expected @@ -1,4 +1,59 @@ inferCertainType +| accessors.swift:4:7:4:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:7:12:7:17 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:17:14:17:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:20:7:20:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:20:16:20:23 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:26:14:26:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:28:9:28:13 | value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:29:7:29:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:29:16:29:20 | value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:48:7:48:14 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:50:11:50:17 | current | | {EXTERNAL LOCATION} | Int | +| accessors.swift:50:21:50:28 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:51:11:51:18 | incoming | | {EXTERNAL LOCATION} | Int | +| accessors.swift:51:22:51:29 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:54:11:54:17 | current | | {EXTERNAL LOCATION} | Int | +| accessors.swift:54:21:54:28 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:55:11:55:18 | previous | | {EXTERNAL LOCATION} | Int | +| accessors.swift:55:22:55:29 | oldValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:59:7:59:20 | namedObservers | | {EXTERNAL LOCATION} | String | +| accessors.swift:60:13:60:20 | incoming | | {EXTERNAL LOCATION} | String | +| accessors.swift:61:11:61:15 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:61:19:61:26 | incoming | | {EXTERNAL LOCATION} | String | +| accessors.swift:63:12:63:19 | previous | | {EXTERNAL LOCATION} | String | +| accessors.swift:64:11:64:15 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:64:19:64:26 | previous | | {EXTERNAL LOCATION} | String | +| accessors.swift:80:7:80:12 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:82:8:82:12 | value | | accessors.swift:79:24:79:24 | T | +| accessors.swift:83:5:83:10 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:83:14:83:18 | value | | accessors.swift:79:24:79:24 | T | +| accessors.swift:88:14:88:19 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:91:7:91:12 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:91:16:91:23 | newValue | | accessors.swift:79:24:79:24 | T | +| accessors.swift:95:7:95:14 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:97:11:97:17 | current | | {EXTERNAL LOCATION} | Int | +| accessors.swift:97:21:97:28 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:98:11:98:18 | previous | | {EXTERNAL LOCATION} | Int | +| accessors.swift:98:22:98:29 | oldValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:107:22:107:25 | self | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:108:11:108:18 | previous | | {EXTERNAL LOCATION} | Int | +| accessors.swift:108:22:108:29 | oldValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:135:33:135:37 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:136:18:136:22 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:137:19:137:23 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:138:3:138:7 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:143:30:143:40 | Block | | {EXTERNAL LOCATION} | Function | +| accessors.swift:143:32:143:38 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| accessors.swift:147:19:147:22 | self | | accessors.swift:142:1:149:1 | C | +| accessors.swift:154:14:154:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:158:14:158:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:161:7:161:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:161:16:161:23 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:172:14:172:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:176:14:176:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:179:7:179:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:179:16:179:23 | newValue | | {EXTERNAL LOCATION} | Int | | basics.swift:1:5:1:16 | topLevelDecl | | {EXTERNAL LOCATION} | Int | | basics.swift:3:1:3:12 | topLevelDecl | | {EXTERNAL LOCATION} | Int | | basics.swift:6:7:6:11 | myInt | | {EXTERNAL LOCATION} | Int | @@ -103,8 +158,20 @@ inferCertainType | closures.swift:38:9:38:10 | f2 | Args | {EXTERNAL LOCATION} | Tuple1 | | closures.swift:38:9:38:10 | f2 | Args.T0 | {EXTERNAL LOCATION} | Int | | closures.swift:38:9:38:10 | f2 | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:40:10:40:10 | f | | {EXTERNAL LOCATION} | Function | +| closures.swift:40:10:40:10 | f | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:40:10:40:10 | f | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:40:10:40:10 | f | Return | {EXTERNAL LOCATION} | Int | | closures.swift:41:9:41:12 | self | | closures.swift:31:1:50:1 | ClosureField | +| closures.swift:41:18:41:18 | f | | {EXTERNAL LOCATION} | Function | +| closures.swift:41:18:41:18 | f | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:41:18:41:18 | f | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:41:18:41:18 | f | Return | {EXTERNAL LOCATION} | Int | | closures.swift:42:9:42:12 | self | | closures.swift:31:1:50:1 | ClosureField | +| closures.swift:42:19:42:19 | f | | {EXTERNAL LOCATION} | Function | +| closures.swift:42:19:42:19 | f | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:42:19:42:19 | f | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:42:19:42:19 | f | Return | {EXTERNAL LOCATION} | Int | | closures.swift:45:24:45:28 | value | | {EXTERNAL LOCATION} | Int | | closures.swift:47:9:47:12 | self | | closures.swift:31:1:50:1 | ClosureField | | closures.swift:47:16:47:20 | value | | {EXTERNAL LOCATION} | Int | @@ -686,6 +753,196 @@ inferCertainType | type_constraints.swift:256:29:256:33 | upper | | type_constraints.swift:254:12:254:24 | T | | type_constraints.swift:257:10:257:14 | value | | type_constraints.swift:254:12:254:24 | T | inferType +| accessors.swift:4:7:4:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:4:21:4:21 | 0 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:6:27:8:3 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:7:12:7:17 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:10:30:13:3 | Block | | {EXTERNAL LOCATION} | String | +| accessors.swift:11:9:11:13 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:11:17:11:26 | "accessor" | | {EXTERNAL LOCATION} | String | +| accessors.swift:12:12:12:16 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:16:9:18:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:17:14:17:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:19:9:21:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:20:7:20:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:20:7:20:23 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:20:16:20:23 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:25:9:27:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:26:14:26:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:28:9:28:13 | value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:28:16:30:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:29:7:29:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:29:7:29:20 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:29:16:29:20 | value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:34:31:43:1 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:35:7:35:15 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:35:19:35:35 | AccessorContainer | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:35:19:35:37 | AccessorContainer(...) | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:36:7:36:19 | implicitValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:36:23:36:31 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:36:23:36:46 | ... .implicitGetter | | {EXTERNAL LOCATION} | Int | +| accessors.swift:37:7:37:19 | explicitValue | | {EXTERNAL LOCATION} | String | +| accessors.swift:37:23:37:31 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:37:23:37:46 | ... .explicitGetter | | {EXTERNAL LOCATION} | String | +| accessors.swift:38:7:38:19 | writableValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:38:23:38:31 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:38:23:38:40 | ... .writable | | {EXTERNAL LOCATION} | Int | +| accessors.swift:39:3:39:11 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:39:3:39:20 | ... .writable | | {EXTERNAL LOCATION} | Int | +| accessors.swift:39:3:39:25 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:39:24:39:25 | 42 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:40:3:40:11 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:40:3:40:20 | ... .writable | | {EXTERNAL LOCATION} | Int | +| accessors.swift:40:3:40:25 | ... += ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:40:25:40:25 | 0 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:41:7:41:16 | namedValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:41:20:41:28 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:41:20:41:40 | ... .namedSetter | | {EXTERNAL LOCATION} | Int | +| accessors.swift:42:3:42:11 | container | | accessors.swift:3:1:32:1 | AccessorContainer | +| accessors.swift:42:3:42:23 | ... .namedSetter | | {EXTERNAL LOCATION} | Int | +| accessors.swift:42:3:42:28 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:42:27:42:28 | 43 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:48:7:48:14 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:48:23:48:23 | 0 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:50:11:50:17 | current | | {EXTERNAL LOCATION} | Int | +| accessors.swift:50:21:50:28 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:51:11:51:18 | incoming | | {EXTERNAL LOCATION} | Int | +| accessors.swift:51:22:51:29 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:54:11:54:17 | current | | {EXTERNAL LOCATION} | Int | +| accessors.swift:54:21:54:28 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:55:11:55:18 | previous | | {EXTERNAL LOCATION} | Int | +| accessors.swift:55:22:55:29 | oldValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:59:7:59:20 | namedObservers | | {EXTERNAL LOCATION} | String | +| accessors.swift:59:32:59:33 | "" | | {EXTERNAL LOCATION} | String | +| accessors.swift:60:13:60:20 | incoming | | {EXTERNAL LOCATION} | String | +| accessors.swift:61:11:61:15 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:61:19:61:26 | incoming | | {EXTERNAL LOCATION} | String | +| accessors.swift:63:12:63:19 | previous | | {EXTERNAL LOCATION} | String | +| accessors.swift:64:11:64:15 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:64:19:64:26 | previous | | {EXTERNAL LOCATION} | String | +| accessors.swift:69:31:75:1 | Block | | {EXTERNAL LOCATION} | String | +| accessors.swift:70:7:70:15 | container | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:70:19:70:36 | ObservedProperties | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:70:19:70:38 | ObservedProperties(...) | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:71:7:71:14 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:71:18:71:26 | container | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:71:18:71:35 | ... .observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:72:3:72:11 | container | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:72:3:72:20 | ... .observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:72:3:72:25 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:72:24:72:25 | 42 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:73:7:73:11 | named | | {EXTERNAL LOCATION} | String | +| accessors.swift:73:15:73:23 | container | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:73:15:73:38 | ... .namedObservers | | {EXTERNAL LOCATION} | String | +| accessors.swift:74:3:74:11 | container | | accessors.swift:47:1:67:1 | ObservedProperties | +| accessors.swift:74:3:74:26 | ... .namedObservers | | {EXTERNAL LOCATION} | String | +| accessors.swift:74:3:74:38 | ... = ... | | {EXTERNAL LOCATION} | String | +| accessors.swift:74:30:74:38 | "updated" | | {EXTERNAL LOCATION} | String | +| accessors.swift:80:7:80:12 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:82:8:82:12 | value | | accessors.swift:79:24:79:24 | T | +| accessors.swift:82:18:84:3 | Block | | accessors.swift:79:24:79:24 | T | +| accessors.swift:83:5:83:10 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:83:5:83:18 | ... = ... | | accessors.swift:79:24:79:24 | T | +| accessors.swift:83:14:83:18 | value | | accessors.swift:79:24:79:24 | T | +| accessors.swift:87:9:89:5 | Block | | accessors.swift:79:24:79:24 | T | +| accessors.swift:88:14:88:19 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:90:9:92:5 | Block | | accessors.swift:79:24:79:24 | T | +| accessors.swift:91:7:91:12 | stored | | accessors.swift:79:24:79:24 | T | +| accessors.swift:91:7:91:23 | ... = ... | | accessors.swift:79:24:79:24 | T | +| accessors.swift:91:16:91:23 | newValue | | accessors.swift:79:24:79:24 | T | +| accessors.swift:95:7:95:14 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:95:23:95:23 | 0 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:97:11:97:17 | current | | {EXTERNAL LOCATION} | Int | +| accessors.swift:97:21:97:28 | observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:98:11:98:18 | previous | | {EXTERNAL LOCATION} | Int | +| accessors.swift:98:22:98:29 | oldValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:107:11:107:18 | current2 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:107:22:107:25 | self | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:107:22:107:34 | ... .observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:108:11:108:18 | previous | | {EXTERNAL LOCATION} | Int | +| accessors.swift:108:22:108:29 | oldValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:120:7:120:15 | container | | accessors.swift:79:1:101:1 | GenericAccessors | +| accessors.swift:120:7:120:15 | container | T | {EXTERNAL LOCATION} | String | +| accessors.swift:120:19:120:34 | GenericAccessors | | accessors.swift:79:1:101:1 | GenericAccessors | +| accessors.swift:120:19:120:34 | GenericAccessors | T | {EXTERNAL LOCATION} | String | +| accessors.swift:120:19:120:50 | GenericAccessors(...) | | accessors.swift:79:1:101:1 | GenericAccessors | +| accessors.swift:120:19:120:50 | GenericAccessors(...) | T | {EXTERNAL LOCATION} | String | +| accessors.swift:120:43:120:49 | "value" | | {EXTERNAL LOCATION} | String | +| accessors.swift:121:7:121:11 | value | | {EXTERNAL LOCATION} | String | +| accessors.swift:121:15:121:23 | container | | accessors.swift:79:1:101:1 | GenericAccessors | +| accessors.swift:121:15:121:23 | container | T | {EXTERNAL LOCATION} | String | +| accessors.swift:121:15:121:29 | ... .value | | {EXTERNAL LOCATION} | String | +| accessors.swift:122:24:122:32 | container | | accessors.swift:79:1:101:1 | GenericAccessors | +| accessors.swift:122:24:122:32 | container | T | {EXTERNAL LOCATION} | String | +| accessors.swift:123:7:123:15 | inherited | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:123:19:123:36 | InheritedAccessors | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:123:19:123:47 | InheritedAccessors(...) | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:123:45:123:46 | 42 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:124:7:124:20 | inheritedValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:124:24:124:32 | inherited | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:124:24:124:38 | ... .value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:125:7:125:23 | inheritedObserver | | {EXTERNAL LOCATION} | Int | +| accessors.swift:125:27:125:35 | inherited | | accessors.swift:103:1:111:1 | InheritedAccessors | +| accessors.swift:125:27:125:44 | ... .observed | | {EXTERNAL LOCATION} | Int | +| accessors.swift:135:33:135:37 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:135:68:139:1 | Block | | {EXTERNAL LOCATION} | String | +| accessors.swift:136:7:136:14 | readOnly | | {EXTERNAL LOCATION} | Int | +| accessors.swift:136:18:136:22 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:136:18:136:31 | ... .readOnly | | {EXTERNAL LOCATION} | Int | +| accessors.swift:137:7:137:15 | readWrite | | {EXTERNAL LOCATION} | String | +| accessors.swift:137:19:137:23 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:137:19:137:33 | ... .readWrite | | {EXTERNAL LOCATION} | String | +| accessors.swift:138:3:138:7 | value | | accessors.swift:130:1:133:1 | AccessorRequirements | +| accessors.swift:138:3:138:17 | ... .readWrite | | {EXTERNAL LOCATION} | String | +| accessors.swift:138:3:138:29 | ... = ... | | {EXTERNAL LOCATION} | String | +| accessors.swift:138:21:138:29 | "updated" | | {EXTERNAL LOCATION} | String | +| accessors.swift:143:30:143:40 | Block | | {EXTERNAL LOCATION} | Function | +| accessors.swift:143:30:143:40 | Block | Return | {EXTERNAL LOCATION} | String | +| accessors.swift:143:32:143:38 | FunctionExpr | | {EXTERNAL LOCATION} | Function | +| accessors.swift:143:32:143:38 | FunctionExpr | Return | {EXTERNAL LOCATION} | String | +| accessors.swift:143:34:143:36 | "x" | | {EXTERNAL LOCATION} | String | +| accessors.swift:143:34:143:36 | Block | | {EXTERNAL LOCATION} | String | +| accessors.swift:146:9:146:14 | result | | {EXTERNAL LOCATION} | String | +| accessors.swift:146:18:146:25 | callback | | {EXTERNAL LOCATION} | Function | +| accessors.swift:146:18:146:25 | callback | Return | {EXTERNAL LOCATION} | String | +| accessors.swift:146:18:146:27 | callback(...) | | {EXTERNAL LOCATION} | String | +| accessors.swift:147:9:147:15 | result2 | | {EXTERNAL LOCATION} | String | +| accessors.swift:147:19:147:22 | self | | accessors.swift:142:1:149:1 | C | +| accessors.swift:147:19:147:31 | ... .callback | | {EXTERNAL LOCATION} | Function | +| accessors.swift:147:19:147:31 | ... .callback | Return | {EXTERNAL LOCATION} | String | +| accessors.swift:147:19:147:33 | ... .callback(...) | | {EXTERNAL LOCATION} | String | +| accessors.swift:154:14:154:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:154:28:154:28 | 0 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:157:9:159:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:158:14:158:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:160:9:162:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:161:7:161:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:161:7:161:23 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:161:16:161:23 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:166:28:169:1 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:167:7:167:11 | value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:167:15:167:35 | ... .value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:168:3:168:23 | ... .value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:168:3:168:28 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:168:27:168:28 | 42 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:172:14:172:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:172:28:172:28 | 0 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:175:9:177:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:176:14:176:19 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:178:9:180:5 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:179:7:179:12 | stored | | {EXTERNAL LOCATION} | Int | +| accessors.swift:179:7:179:23 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:179:16:179:23 | newValue | | {EXTERNAL LOCATION} | Int | +| accessors.swift:184:27:187:1 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:185:7:185:11 | value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:185:15:185:34 | ... .value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:186:3:186:22 | ... .value | | {EXTERNAL LOCATION} | Int | +| accessors.swift:186:3:186:27 | ... = ... | | {EXTERNAL LOCATION} | Int | +| accessors.swift:186:26:186:27 | 42 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:191:17:191:22 | Block | | {EXTERNAL LOCATION} | Int | +| accessors.swift:191:19:191:20 | 42 | | {EXTERNAL LOCATION} | Int | +| accessors.swift:192:5:192:5 | x | | {EXTERNAL LOCATION} | Int | +| accessors.swift:192:9:192:14 | answer | | {EXTERNAL LOCATION} | Int | | basics.swift:1:5:1:16 | topLevelDecl | | {EXTERNAL LOCATION} | Int | | basics.swift:1:25:1:25 | 0 | | {EXTERNAL LOCATION} | Int | | basics.swift:2:1:2:1 | 0 | | {EXTERNAL LOCATION} | Int | @@ -1050,16 +1307,40 @@ inferType | closures.swift:38:9:38:10 | f2 | Args | {EXTERNAL LOCATION} | Tuple1 | | closures.swift:38:9:38:10 | f2 | Args.T0 | {EXTERNAL LOCATION} | Int | | closures.swift:38:9:38:10 | f2 | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:40:10:40:10 | f | | {EXTERNAL LOCATION} | Function | +| closures.swift:40:10:40:10 | f | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:40:10:40:10 | f | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:40:10:40:10 | f | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:40:37:43:5 | Block | | {EXTERNAL LOCATION} | Function | +| closures.swift:40:37:43:5 | Block | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:40:37:43:5 | Block | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:40:37:43:5 | Block | Return | {EXTERNAL LOCATION} | Int | | closures.swift:41:9:41:12 | self | | closures.swift:31:1:50:1 | ClosureField | | closures.swift:41:9:41:14 | ... .f | | {EXTERNAL LOCATION} | Function | | closures.swift:41:9:41:14 | ... .f | Args | {EXTERNAL LOCATION} | Tuple1 | | closures.swift:41:9:41:14 | ... .f | Args.T0 | {EXTERNAL LOCATION} | Int | | closures.swift:41:9:41:14 | ... .f | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:41:9:41:18 | ... = ... | | {EXTERNAL LOCATION} | Function | +| closures.swift:41:9:41:18 | ... = ... | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:41:9:41:18 | ... = ... | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:41:9:41:18 | ... = ... | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:41:18:41:18 | f | | {EXTERNAL LOCATION} | Function | +| closures.swift:41:18:41:18 | f | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:41:18:41:18 | f | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:41:18:41:18 | f | Return | {EXTERNAL LOCATION} | Int | | closures.swift:42:9:42:12 | self | | closures.swift:31:1:50:1 | ClosureField | | closures.swift:42:9:42:15 | ... .f2 | | {EXTERNAL LOCATION} | Function | | closures.swift:42:9:42:15 | ... .f2 | Args | {EXTERNAL LOCATION} | Tuple1 | | closures.swift:42:9:42:15 | ... .f2 | Args.T0 | {EXTERNAL LOCATION} | Int | | closures.swift:42:9:42:15 | ... .f2 | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:42:9:42:19 | ... = ... | | {EXTERNAL LOCATION} | Function | +| closures.swift:42:9:42:19 | ... = ... | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:42:9:42:19 | ... = ... | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:42:9:42:19 | ... = ... | Return | {EXTERNAL LOCATION} | Int | +| closures.swift:42:19:42:19 | f | | {EXTERNAL LOCATION} | Function | +| closures.swift:42:19:42:19 | f | Args | {EXTERNAL LOCATION} | Tuple1 | +| closures.swift:42:19:42:19 | f | Args.T0 | {EXTERNAL LOCATION} | Int | +| closures.swift:42:19:42:19 | f | Return | {EXTERNAL LOCATION} | Int | | closures.swift:45:24:45:28 | value | | {EXTERNAL LOCATION} | Int | | closures.swift:45:43:49:5 | Block | | {EXTERNAL LOCATION} | Int | | closures.swift:47:9:47:12 | self | | closures.swift:31:1:50:1 | ClosureField | diff --git a/unified/ql/test/library-tests/type-inference/type-inference.ql b/unified/ql/test/library-tests/type-inference/type-inference.ql index eab27d53c9ea..75eab8be8b30 100644 --- a/unified/ql/test/library-tests/type-inference/type-inference.ql +++ b/unified/ql/test/library-tests/type-inference/type-inference.ql @@ -20,11 +20,7 @@ query predicate inferType(AstNode n, TypePath path, Type t) { } module ResolveTest implements TestSig { - string getARelevantTag() { - result = "target" - or - result = "field" - } + string getARelevantTag() { result = ["target", "field", "accessor"] } predicate hasActualResult(Location location, string element, string tag, string value) { exists(Callable c, CallExpr ce | @@ -42,6 +38,14 @@ module ResolveTest implements TestSig { nameBinding(field.getPattern(), value) and tag = "field" ) + or + exists(AccessorDeclaration accessor, Expr access | + accessor = resolveAccessorAccess(access) and + location = access.getLocation() and + element = access.toString() and + callableName(accessor, value) and + tag = "accessor" + ) } } From 6263dc95825534496d436549bb31a573fc905193 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 29 Oct 2026 13:43:17 +0100 Subject: [PATCH 3/3] Address review comments --- .../typeinference/internal/TypeInference.qll | 10 ++++ .../internal/dataflow/DataFlowPluginSwift.qll | 27 +++++++---- .../internal/typeinference/TypeInference.qll | 4 ++ .../dataflow/accessors_instance.swift | 2 + .../test/library-tests/dataflow/test.expected | 48 +++++++++---------- 5 files changed, 58 insertions(+), 33 deletions(-) diff --git a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll index c69f1b1890e0..1bc9591607e8 100644 --- a/shared/typeinference/codeql/typeinference/internal/TypeInference.qll +++ b/shared/typeinference/codeql/typeinference/internal/TypeInference.qll @@ -3683,6 +3683,16 @@ module Make1 Input1> { pragma[nomagic] Type inferType(AstNode n) { result = inferType(n, TypePath::nil()) } + /** Gets the unique non-pseudo type inferred at `path` for `n`, if any. */ + Type inferTypeUnique(AstNode n, TypePath path) { + result = unique(Type t | t = inferType(n, path) and not t instanceof PseudoType | t) + } + + /** + * Gets the unique non-pseudo type inferred at the root for `n`, if any. + */ + Type inferTypeUnique(AstNode n) { result = inferTypeUnique(n, TypePath::nil()) } + /** * The cached stage of this module. * diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index b71fbf16120c..f845a5f20f3c 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -7,6 +7,11 @@ private import AllDataFlow private import codeql.unified.internal.ExprPositions private import codeql.unified.internal.typeinference.TypeInference as T +private predicate isReferenceType(T::Type t) { + // todo: type parameters with type constraints + t.asClass().hasModifier(["actor", "class"]) +} + private class SwiftDataFlowPlugin extends DataFlowPlugin { // Note: For now we assume all code is Swift, but in the future we must restrict these rules to Swift-files override predicate bypassContentSet(ContentSet contents) { @@ -16,9 +21,9 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { // Swift writes back mutated values of computed value-type properties via their setter bindingset[expr] override predicate mayWriteBackViaSetterCall(Expr expr) { - isInWriteBackPosition(expr) and - // conservative value-type check - not forex(T::Type t | t = T::inferType(expr) | t.asClass().hasModifier("class")) + exists(boolean direct | isInWriteBackPosition(expr, direct) | + if direct = false then not isReferenceType(T::inferTypeUnique(expr)) else any() + ) } override predicate exprStep(Expr expr1, Expr expr2, boolean mustFlow) { @@ -101,25 +106,29 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { * Holds if `expr` is target of an assignment or 'inout' argument, * or the base of such an expression, e.g. `x` in `x.f1.f2 = foo()` */ -private predicate isInWriteBackPosition(Expr expr) { - isInAssignmentContext(expr, _) +private predicate isInWriteBackPosition(Expr expr, boolean direct) { + isInAssignmentContext(expr, _) and + direct = false or // Arguments to an 'inout' parameter must be prefixed with '&' exists(UnaryExpr unary | unary.getOperator().(PrefixOperator).getValue() = "&" and unary.getOperand() = expr // TODO: support custom operators with an 'inout' parameter, which lack the '&' indicator - ) + ) and + direct = true or // The receiver of a call to a 'mutating' function counts as an 'inout' argument exists(CallExpr call, Callable target | target = T::resolveCallTarget(call) and target.hasModifier("mutating") and expr = call.getCallee().(MemberAccessExpr).getBase() - ) + ) and + direct = true or exists(MemberAccessExpr member | - isInWriteBackPosition(member) and - expr = member.getBase() + isInWriteBackPosition(member, _) and + expr = member.getBase() and + direct = false ) } diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll index 85a1c0281cfb..2dd7596e29cc 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/TypeInference.qll @@ -856,6 +856,10 @@ predicate inferType = M3::inferType/1; predicate inferType = M3::inferType/2; +predicate inferTypeUnique = M3::inferTypeUnique/1; + +predicate inferTypeUnique = M3::inferTypeUnique/2; + predicate inferTypeCertain = M3::inferTypeCertain/2; module Consistency = M3::Consistency; diff --git a/unified/ql/test/library-tests/dataflow/accessors_instance.swift b/unified/ql/test/library-tests/dataflow/accessors_instance.swift index b9daa47f45b5..6c76138c5506 100644 --- a/unified/ql/test/library-tests/dataflow/accessors_instance.swift +++ b/unified/ql/test/library-tests/dataflow/accessors_instance.swift @@ -139,7 +139,9 @@ func t11() { } } let c = C() + // `p` has value type, so this calls `p`'s setter c.p.0 = source("t11.1") + // `q` has reference type, so this calls `q`'s getter followed by `p`'s setter c.q.p = (source("t11.2"), source("t11.3")) } diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 2af1a83d8f56..40fb146a8fde 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -32,16 +32,16 @@ edges | accessors_instance.swift:119:13:119:17 | field | accessors_instance.swift:125:18:125:22 | field | provenance | | | accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:119:13:119:17 | field | provenance | | | accessors_instance.swift:134:24:134:31 | newValue [0] | accessors_instance.swift:134:24:134:33 | ... .0 | provenance | | -| accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | -| accessors_instance.swift:142:5:142:9 | ... .0 | accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | provenance | | -| accessors_instance.swift:142:13:142:27 | source(...) | accessors_instance.swift:142:5:142:9 | ... .0 | provenance | | -| accessors_instance.swift:143:5:143:9 | ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | -| accessors_instance.swift:143:13:143:46 | TupleExpr [0] | accessors_instance.swift:143:5:143:9 | ... .p [0] | provenance | | -| accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:143:13:143:46 | TupleExpr [0] | provenance | | -| accessors_instance.swift:149:34:149:38 | field | accessors_instance.swift:158:5:158:7 | [post] ... .s [field] | provenance | | -| accessors_instance.swift:149:42:149:56 | source(...) | accessors_instance.swift:149:34:149:38 | field | provenance | | -| accessors_instance.swift:154:24:154:31 | newValue [field] | accessors_instance.swift:154:24:154:37 | ... .field | provenance | | -| accessors_instance.swift:158:5:158:7 | [post] ... .s [field] | accessors_instance.swift:154:24:154:31 | newValue [field] | provenance | | +| accessors_instance.swift:143:5:143:7 | [post] ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | +| accessors_instance.swift:143:5:143:9 | ... .0 | accessors_instance.swift:143:5:143:7 | [post] ... .p [0] | provenance | | +| accessors_instance.swift:143:13:143:27 | source(...) | accessors_instance.swift:143:5:143:9 | ... .0 | provenance | | +| accessors_instance.swift:145:5:145:9 | ... .p [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | provenance | | +| accessors_instance.swift:145:13:145:46 | TupleExpr [0] | accessors_instance.swift:145:5:145:9 | ... .p [0] | provenance | | +| accessors_instance.swift:145:14:145:28 | source(...) | accessors_instance.swift:145:13:145:46 | TupleExpr [0] | provenance | | +| accessors_instance.swift:151:34:151:38 | field | accessors_instance.swift:160:5:160:7 | [post] ... .s [field] | provenance | | +| accessors_instance.swift:151:42:151:56 | source(...) | accessors_instance.swift:151:34:151:38 | field | provenance | | +| accessors_instance.swift:156:24:156:31 | newValue [field] | accessors_instance.swift:156:24:156:37 | ... .field | provenance | | +| accessors_instance.swift:160:5:160:7 | [post] ... .s [field] | accessors_instance.swift:156:24:156:31 | newValue [field] | provenance | | | accessors_local.swift:7:21:7:34 | source(...) | accessors_local.swift:8:10:8:10 | x | provenance | | | accessors_local.swift:15:15:15:28 | source(...) | accessors_local.swift:17:10:17:10 | x | provenance | | | accessors_local.swift:22:15:22:28 | source(...) | accessors_local.swift:25:10:25:10 | x | provenance | | @@ -569,17 +569,17 @@ nodes | accessors_instance.swift:125:18:125:22 | field | semmle.label | field | | accessors_instance.swift:134:24:134:31 | newValue [0] | semmle.label | newValue [0] | | accessors_instance.swift:134:24:134:33 | ... .0 | semmle.label | ... .0 | -| accessors_instance.swift:142:5:142:7 | [post] ... .p [0] | semmle.label | [post] ... .p [0] | -| accessors_instance.swift:142:5:142:9 | ... .0 | semmle.label | ... .0 | -| accessors_instance.swift:142:13:142:27 | source(...) | semmle.label | source(...) | -| accessors_instance.swift:143:5:143:9 | ... .p [0] | semmle.label | ... .p [0] | -| accessors_instance.swift:143:13:143:46 | TupleExpr [0] | semmle.label | TupleExpr [0] | -| accessors_instance.swift:143:14:143:28 | source(...) | semmle.label | source(...) | -| accessors_instance.swift:149:34:149:38 | field | semmle.label | field | -| accessors_instance.swift:149:42:149:56 | source(...) | semmle.label | source(...) | -| accessors_instance.swift:154:24:154:31 | newValue [field] | semmle.label | newValue [field] | -| accessors_instance.swift:154:24:154:37 | ... .field | semmle.label | ... .field | -| accessors_instance.swift:158:5:158:7 | [post] ... .s [field] | semmle.label | [post] ... .s [field] | +| accessors_instance.swift:143:5:143:7 | [post] ... .p [0] | semmle.label | [post] ... .p [0] | +| accessors_instance.swift:143:5:143:9 | ... .0 | semmle.label | ... .0 | +| accessors_instance.swift:143:13:143:27 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:145:5:145:9 | ... .p [0] | semmle.label | ... .p [0] | +| accessors_instance.swift:145:13:145:46 | TupleExpr [0] | semmle.label | TupleExpr [0] | +| accessors_instance.swift:145:14:145:28 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:151:34:151:38 | field | semmle.label | field | +| accessors_instance.swift:151:42:151:56 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:156:24:156:31 | newValue [field] | semmle.label | newValue [field] | +| accessors_instance.swift:156:24:156:37 | ... .field | semmle.label | ... .field | +| accessors_instance.swift:160:5:160:7 | [post] ... .s [field] | semmle.label | [post] ... .s [field] | | accessors_local.swift:7:21:7:34 | source(...) | semmle.label | source(...) | | accessors_local.swift:8:10:8:10 | x | semmle.label | x | | accessors_local.swift:15:15:15:28 | source(...) | semmle.label | source(...) | @@ -1215,9 +1215,9 @@ testFailures | accessors_instance.swift:99:10:99:21 | ... .otherField | accessors_instance.swift:97:15:97:28 | source(...) | accessors_instance.swift:99:10:99:21 | ... .otherField | $@ | accessors_instance.swift:97:15:97:28 | source(...) | source(...) | | accessors_instance.swift:112:10:112:16 | ... .field | accessors_instance.swift:106:21:106:34 | source(...) | accessors_instance.swift:112:10:112:16 | ... .field | $@ | accessors_instance.swift:106:21:106:34 | source(...) | source(...) | | accessors_instance.swift:125:18:125:22 | field | accessors_instance.swift:119:21:119:35 | source(...) | accessors_instance.swift:125:18:125:22 | field | $@ | accessors_instance.swift:119:21:119:35 | source(...) | source(...) | -| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:142:13:142:27 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:142:13:142:27 | source(...) | source(...) | -| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:143:14:143:28 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:143:14:143:28 | source(...) | source(...) | -| accessors_instance.swift:154:24:154:37 | ... .field | accessors_instance.swift:149:42:149:56 | source(...) | accessors_instance.swift:154:24:154:37 | ... .field | $@ | accessors_instance.swift:149:42:149:56 | source(...) | source(...) | +| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:143:13:143:27 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:143:13:143:27 | source(...) | source(...) | +| accessors_instance.swift:134:24:134:33 | ... .0 | accessors_instance.swift:145:14:145:28 | source(...) | accessors_instance.swift:134:24:134:33 | ... .0 | $@ | accessors_instance.swift:145:14:145:28 | source(...) | source(...) | +| accessors_instance.swift:156:24:156:37 | ... .field | accessors_instance.swift:151:42:151:56 | source(...) | accessors_instance.swift:156:24:156:37 | ... .field | $@ | accessors_instance.swift:151:42:151:56 | source(...) | source(...) | | accessors_local.swift:8:10:8:10 | x | accessors_local.swift:7:21:7:34 | source(...) | accessors_local.swift:8:10:8:10 | x | $@ | accessors_local.swift:7:21:7:34 | source(...) | source(...) | | accessors_local.swift:17:10:17:10 | x | accessors_local.swift:15:15:15:28 | source(...) | accessors_local.swift:17:10:17:10 | x | $@ | accessors_local.swift:15:15:15:28 | source(...) | source(...) | | accessors_local.swift:25:10:25:10 | x | accessors_local.swift:22:15:22:28 | source(...) | accessors_local.swift:25:10:25:10 | x | $@ | accessors_local.swift:22:15:22:28 | source(...) | source(...) |