diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index 0eaf1e8f02f1..fb9d88333087 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -534,7 +534,7 @@ named: name_node: identifier accessor_kind: accessor_kind parameter*: parameter - type?: expr + return_type?: expr body?: block # "get", "set", or a language-specific kind like "didSet" diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 75b7c4e9c5c5..a1dab86b626e 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -12,15 +12,15 @@ struct SwiftContext { /// `variableDecl` rule before translating the accessor block; read by the /// inner `accessorDecl` rules to name each `accessor_declaration`. property_name_node: Option, - /// Translated type node for the property type. Set (for computed - /// properties) by the accessor-bearing `variableDecl` rule; read by the - /// inner `accessorDecl` rules. Left `None` for stored properties with - /// observers, so their `willSet`/`didSet` accessors carry no type. + /// Translated type node for the property type. Set by the + /// accessor-bearing `variableDecl` rule; read by the inner `accessorDecl` + /// rules for both the getter return type and its `newValue`/`oldValue` + /// parameter type, when available. property_type: Option, /// Translated outer modifiers to attach to each child of a flattening - /// outer rule — e.g. the `let`/`var` binding modifier on each - /// `patternBinding` of a `variableDecl`, or the binding modifier on each - /// accessor of a property. + /// outer rule — e.g. the `let`/`var` binding modifier, attributes and + /// modifiers (`static`, `public`, …) on each `patternBinding` of a + /// `variableDecl`, or on each accessor of a property. outer_modifiers: Vec, /// True when the current child of a flattening outer rule is not /// the first one — its inner rule should emit a @@ -325,6 +325,8 @@ fn translation_rules() -> Vec> { // block. rule!( (variableDecl + attributes: _* @attrs + modifiers: _* @mods bindingSpecifier: @@spec bindings: (patternBinding pattern: (identifierPattern identifier: @@name) @@ -333,8 +335,10 @@ fn translation_rules() -> Vec> { => (accessor_declaration modifier: (modifier #{spec}) + modifier: {attrs} + modifier: {mods} name_node: (identifier #{name}) - type: {ty} + return_type: {ty} accessor_kind: {ctx.literal_at_start_of("accessor_kind", "get", body)} body: {body}) ), @@ -345,16 +349,18 @@ fn translation_rules() -> Vec> { // * With an initializer (`var x: T = e { willSet {…} didSet {…} }`) it is // a *stored* property with observers: emit the backing // `variable_declaration` first, then one `accessor_declaration` per - // observer (observers carry no type). + // observer. // * Without an initializer (`var v: T { get set }`, incl. protocol - // requirements) it is a *computed* property: no backing variable; the - // type is published so the get/set accessors carry it. + // requirements) it is a *computed* property: no backing variable. // - // In both cases the first emitted declaration is unchained and every + // In both cases the type is published so every accessor carries it, and + // the first emitted declaration is unchained and every // subsequent one is tagged `chained_declaration` (the `!result.is_empty()` // test). Must precede the plain `variableDecl` rules. rule!( (variableDecl + attributes: _* @attrs + modifiers: _* @mods bindingSpecifier: @@spec bindings: (patternBinding pattern: (identifierPattern identifier: @@name) @@ -363,8 +369,14 @@ fn translation_rules() -> Vec> { accessorBlock: (accessorBlock accessors: (accessorDecl)+ @@accessors))) => member* { - ctx.outer_modifiers = vec![tree!((modifier #{spec}))]; + // The binding (`let`/`var`) leads, then attributes then modifiers, + // matching the plain `variableDecl` rule. + ctx.outer_modifiers = std::iter::once(tree!((modifier #{spec}))) + .chain(attrs) + .chain(mods) + .collect(); ctx.property_name_node = Some(tree!((identifier #{name}))); + ctx.property_type = Some(ty); let mut result = Vec::new(); if let Some(val) = val { // Stored property with observers: the initializer is not part @@ -380,9 +392,6 @@ fn translation_rules() -> Vec> { type: {ty} value: {val}) )); - } else { - // Computed property: the accessors carry the type. - ctx.property_type = Some(ty); } for acc in accessors.into_iter() { ctx.is_chained = !result.is_empty(); @@ -392,35 +401,74 @@ fn translation_rules() -> Vec> { } ), // Each `accessorDecl` becomes an `accessor_declaration`, reading the - // property name/type and the binding/chained modifiers from `ctx`. The + // property name/type and the modifiers from `ctx`. The // accessor kind comes straight from the specifier keyword // (`get`/`set`/`willSet`/`didSet`). The body is optional: a body-bearing // accessor (a computed getter/setter, or a `willSet`/`didSet` observer) - // carries the binding modifier and a translated body, whereas a bodyless - // one (a protocol requirement) carries neither. The property context is + // has a translated body, whereas a bodyless one (a protocol requirement) + // has none. The property context is // read out *before* translating the body, which resets `ctx` so the // accessor's context does not leak into the body subtree. - rule!( - (accessorDecl accessorSpecifier: @@spec body: _? @@body) - => - (accessor_declaration - modifier: { - if body.is_some() { - ctx.outer_modifiers.clone() - } else { - Vec::new() + // + // `set` and `willSet` accessors get a parameter holding the incoming + // value, and `didSet` gets one holding the previous value. It is named + // explicitly (`set(v)`) or else implicitly `newValue`/`oldValue`. + rule!( + (accessorDecl + accessorSpecifier: @@spec + parameters: (accessorParameters name: @@param_name)? + body: _? @@body) + => + accessor_declaration { + let spec_text = ctx.source_text(spec); + let implicit_param_name = match spec_text.as_str() { + "set" | "willSet" => Some("newValue"), + "didSet" => Some("oldValue"), + _ => None, + }; + let param_ty = ctx.property_type; + let param = match (param_name, implicit_param_name) { + (Some(n), _) => { + Some(tree!((parameter pattern: (identifier #{n}) type: {param_ty}))) } - } - modifier: {chained_modifier(&mut ctx)} - name_node: { - ctx.property_name_node - .ok_or("accessor outside property context")? - } - type: {ctx.property_type} - accessor_kind: (accessor_kind #{spec}) - body: {ctx.reset(); body} - location: {spec} - location: {body}) + (None, Some(n)) => { + let n = n.to_string(); + Some(tree!((parameter + modifier: (modifier "generated") + pattern: (identifier #{n} location: {spec}) + type: {param_ty} + location: {spec}))) + } + (None, None) => None, + }; + let modifiers = ctx.outer_modifiers.clone(); + let chained = chained_modifier(&mut ctx); + let name = ctx + .property_name_node + .ok_or("accessor outside property context")?; + let return_ty = if spec_text == "get" { + ctx.property_type + } else { + None + }; + let body = match body { + Some(block) => { + ctx.reset(); + ctx.translate(block)?.into_iter().next() + } + None => None, + }; + tree!((accessor_declaration + modifier: {modifiers} + modifier: {chained} + name_node: {name} + return_type: {return_ty} + accessor_kind: (accessor_kind #{spec}) + parameter: {param} + body: {body} + location: {spec} + location: {body})) + } ), // ---- Variables ---- // The individual bindings of a `variableDecl`. The binding modifier and diff --git a/unified/extractor/tests/corpus/swift/types/binding-modifier-does-not-leak-into-accessor-body.output b/unified/extractor/tests/corpus/swift/types/binding-modifier-does-not-leak-into-accessor-body.output index 88674d76efbd..f3ce25b05608 100644 --- a/unified/extractor/tests/corpus/swift/types/binding-modifier-does-not-leak-into-accessor-body.output +++ b/unified/extractor/tests/corpus/swift/types/binding-modifier-does-not-leak-into-accessor-body.output @@ -100,7 +100,7 @@ top_level source="var p: Int {\n ⟨body⟩\n}" modifier: modifier "var" source="var" (external) name_node: identifier "p" source="p" (external) accessor_kind: accessor_kind "get" source="get" - type: identifier "Int" source="Int" (external) + return_type: identifier "Int" source="Int" (external) body: block source="{\n ⟨stmt⟩\n }" stmt: diff --git a/unified/extractor/tests/corpus/swift/types/computed-property.output b/unified/extractor/tests/corpus/swift/types/computed-property.output index 4ed7a0e5d096..83e7c84a1a3a 100644 --- a/unified/extractor/tests/corpus/swift/types/computed-property.output +++ b/unified/extractor/tests/corpus/swift/types/computed-property.output @@ -113,11 +113,11 @@ top_level source="⟨body⟩" modifier: modifier "var" source="var" pattern: identifier "h" source="h" type: identifier "Double" source="Double" - accessor_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨type⟩ ⟨body⟩" + accessor_declaration source="⟨modifier⟩ ⟨name_node⟩: ⟨return_type⟩ ⟨body⟩" modifier: modifier "var" source="var" name_node: identifier "area" source="area" accessor_kind: accessor_kind "get" source="" - type: identifier "Double" source="Double" + return_type: identifier "Double" source="Double" body: block source="{\n ⟨stmt⟩\n }" stmt: diff --git a/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output index ce8d1d6f9bd2..b315af33e2b5 100644 --- a/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output +++ b/unified/extractor/tests/corpus/swift/types/property-with-getter-and-setter.output @@ -120,19 +120,23 @@ top_level source="⟨body⟩" modifier: modifier "var" source="var" (external) name_node: identifier "v" source="v" (external) accessor_kind: accessor_kind "get" source="get" - type: identifier "Int" source="Int" (external) + return_type: identifier "Int" source="Int" (external) body: block source="{ ⟨stmt⟩ }" stmt: return_expr source="return ⟨value⟩" value: identifier "_v" source="_v" - accessor_declaration source="⟨accessor_kind⟩ ⟨body⟩" + accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩ ⟨body⟩" modifier: modifier "var" source="var" (external) modifier "chained_declaration" source="" name_node: identifier "v" source="v" (external) accessor_kind: accessor_kind "set" source="set" - type: identifier "Int" source="Int" (external) + parameter: + parameter source="⟨pattern⟩" + modifier: modifier "generated" source="" + type: identifier "Int" source="Int" (external) + pattern: identifier "newValue" source="set" body: block source="{ ⟨stmt⟩ }" stmt: diff --git a/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output index 8a43a3c70588..07fbf97a94fe 100644 --- a/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output +++ b/unified/extractor/tests/corpus/swift/types/protocol-with-read-only-and-read-write-property-requirements.output @@ -111,19 +111,28 @@ top_level source="⟨body⟩" name_node: identifier "P" source="P" member: accessor_declaration source="⟨accessor_kind⟩" + modifier: modifier "var" source="var" (external) name_node: identifier "foo" source="foo" (external) accessor_kind: accessor_kind "get" source="get" - type: identifier "Int" source="Int" (external) + return_type: identifier "Int" source="Int" (external) accessor_declaration source="⟨accessor_kind⟩" + modifier: modifier "var" source="var" (external) name_node: identifier "bar" source="bar" (external) accessor_kind: accessor_kind "get" source="get" - type: identifier "String" source="String" (external) - accessor_declaration source="⟨accessor_kind⟩" - modifier: modifier "chained_declaration" source="" + return_type: identifier "String" source="String" (external) + accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩" + modifier: + modifier "var" source="var" (external) + modifier "chained_declaration" source="" name_node: identifier "bar" source="bar" (external) accessor_kind: accessor_kind "set" source="set" - type: identifier "String" source="String" (external) + parameter: + parameter source="⟨pattern⟩" + modifier: modifier "generated" source="" + type: identifier "String" source="String" (external) + pattern: identifier "newValue" source="set" accessor_declaration source="⟨accessor_kind⟩" + modifier: modifier "var" source="var" (external) name_node: identifier "count" source="count" (external) accessor_kind: accessor_kind "get" source="get" - type: identifier "Int" source="Int" (external) + return_type: identifier "Int" source="Int" (external) diff --git a/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output index d95dfd51bc74..6b9a2fad278a 100644 --- a/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output +++ b/unified/extractor/tests/corpus/swift/variables/property-with-willset-and-didset-observers.output @@ -110,12 +110,17 @@ top_level source="⟨body⟩" pattern: identifier "x" source="x" type: identifier "Int" source="Int" value: int_literal "0" source="0" - accessor_declaration source="⟨accessor_kind⟩ ⟨body⟩" + accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩ ⟨body⟩" modifier: modifier "var" source="var" (external) modifier "chained_declaration" source="" name_node: identifier "x" source="x" (external) accessor_kind: accessor_kind "willSet" source="willSet" + parameter: + parameter source="⟨pattern⟩" + modifier: modifier "generated" source="" + type: identifier "Int" source="Int" (external) + pattern: identifier "newValue" source="willSet" body: block source="{ ⟨stmt⟩ }" stmt: @@ -124,12 +129,17 @@ top_level source="⟨body⟩" argument: argument source="⟨value⟩" value: identifier "newValue" source="newValue" - accessor_declaration source="⟨accessor_kind⟩ ⟨body⟩" + accessor_declaration source="⟨accessor_kind⟩⟨parameter⟩ ⟨body⟩" modifier: modifier "var" source="var" (external) modifier "chained_declaration" source="" name_node: identifier "x" source="x" (external) accessor_kind: accessor_kind "didSet" source="didSet" + parameter: + parameter source="⟨pattern⟩" + modifier: modifier "generated" source="" + type: identifier "Int" source="Int" (external) + pattern: identifier "oldValue" source="didSet" body: block source="{ ⟨stmt⟩ }" stmt: diff --git a/unified/ql/consistency-queries/DataFlowConsistency.ql b/unified/ql/consistency-queries/DataFlowConsistency.ql index 8cbec3a23f8e..71cd02531030 100644 --- a/unified/ql/consistency-queries/DataFlowConsistency.ql +++ b/unified/ql/consistency-queries/DataFlowConsistency.ql @@ -5,6 +5,10 @@ private import codeql.dataflow.internal.DataFlowImplConsistency module ConsistencyInput implements InputSig { predicate argHasPostUpdateExclude(DataFlowInput::ArgumentNode n) { not exists(n.getBasicBlock()) // ignore unreachable data flow nodes + or + // The value passed into a setter call technically needs a post-update node + // because it's an argument to a call, but this rarely matters in practice. + n.(BuilderNode).isSetterValueArgument(_) } predicate reverseReadExclude(DataFlow::Node n) { diff --git a/unified/ql/lib/codeql/unified/internal/Ast.qll b/unified/ql/lib/codeql/unified/internal/Ast.qll index 17c06cf1fd66..5f410460536e 100644 --- a/unified/ql/lib/codeql/unified/internal/Ast.qll +++ b/unified/ql/lib/codeql/unified/internal/Ast.qll @@ -123,8 +123,10 @@ module Unified { /** Gets the node corresponding to the field `parameter`. */ final override F::Parameter getAParameter() { result = this.getParameter(_) } - /** Gets the node corresponding to the field `type`. */ - final F::Expr getType() { unified_accessor_declaration_type(this, result) } + /** Gets the node corresponding to the field `return_type`. */ + final override F::Expr getReturnType() { + unified_accessor_declaration_return_type(this, result) + } /** Gets a field or child node of this node. */ final override F::AstNode getAFieldOrChild() { @@ -133,7 +135,7 @@ module Unified { unified_accessor_declaration_modifier(this, _, result) or unified_accessor_declaration_def(this, _, result) or unified_accessor_declaration_parameter(this, _, result) or - unified_accessor_declaration_type(this, result) + unified_accessor_declaration_return_type(this, result) } } @@ -1624,7 +1626,7 @@ module Unified { or result = node.(AccessorDeclaration).getParameter(i) and name = "getParameter" or - result = node.(AccessorDeclaration).getType() and i = -1 and name = "getType" + result = node.(AccessorDeclaration).getReturnType() and i = -1 and name = "getReturnType" or result = node.(Argument).getModifier(i) and name = "getModifier" or diff --git a/unified/ql/lib/codeql/unified/internal/ExprPositions.qll b/unified/ql/lib/codeql/unified/internal/ExprPositions.qll index 3edc36cc1eb2..d93205b371a1 100644 --- a/unified/ql/lib/codeql/unified/internal/ExprPositions.qll +++ b/unified/ql/lib/codeql/unified/internal/ExprPositions.qll @@ -16,7 +16,7 @@ predicate isInTypeContext(Expr expr) { or expr = any(FunctionExpr n).getReturnType() or - expr = any(AccessorDeclaration n).getType() + expr = any(AccessorDeclaration n).getReturnType() or expr = any(Parameter n).getType() or diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index e4a179741207..9db28d702497 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -409,6 +409,7 @@ module Public { | decl instanceof VariableDeclaration or decl instanceof FunctionDeclaration or // treat functions as values + decl instanceof AccessorDeclaration or decl instanceof Parameter or decl instanceof ForEachStmt or decl instanceof PatternGuardExpr or @@ -446,6 +447,25 @@ module Public { } } + private AccessorDeclaration getAnAccessor(LocalVariable v) { + v.getABinding() = result.getNameNode() and + (result instanceof GetterDeclaration or result instanceof SetterDeclaration) + } + + /** A local variable defined by a getter/setter pair, which does not store a value on its own. */ + final class ComputedLocalVariable extends LocalVariable { + ComputedLocalVariable() { exists(getAnAccessor(this)) } + + /** Gets the getter or setter for this computed variable. */ + AccessorDeclaration getAnAccessor() { result = getAnAccessor(this) } + + /** Gets the getter for this computed variable. */ + GetterDeclaration getGetterDeclaration() { result = getAnAccessor(this) } + + /** Gets the setter for this computed variable. */ + SetterDeclaration getSetterDeclaration() { result = getAnAccessor(this) } + } + /** An access to a locally-declared name. */ class LocalNameAccess extends PotentialLocalNameAccess { LocalNameAccess() { not this instanceof UnqualifiedMemberAccess } diff --git a/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll b/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll index f6b6cc25f7f5..2b01d98d28dc 100644 --- a/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll +++ b/unified/ql/lib/codeql/unified/internal/NameBindingPlugin.qll @@ -81,6 +81,7 @@ predicate isInstanceMember(Member member) { ( member instanceof VariableDeclaration or member instanceof FunctionDeclaration or + member instanceof AccessorDeclaration or member instanceof ConstructorDeclaration ) and exists(ClassLikeDeclaration cls | cls.getAMember() = member | diff --git a/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll b/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll index 58fd36fc5512..199155913d8e 100644 --- a/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/StaticNameBinding.qll @@ -755,10 +755,25 @@ predicate unqualifiedMemberAccess( } module Public { + /** + * An expression that accesses a named member, either with an explicit base (`MemberAccessExpr`) + * or with an implicit base (`UnqualifiedMemberAccess`). + */ + abstract private class MemberAccessImpl extends Expr { + /** Gets the name of the member being accessed. */ + abstract string getMemberName(); + } + + final class MemberAccess = MemberAccessImpl; + + private class MemberAccessExprImpl extends MemberAccessImpl instanceof MemberAccessExpr { + override string getMemberName() { result = MemberAccessExpr.super.getMemberName() } + } + /** * A name node appearing in an unqualified position, referring to a member of an enclosing class. */ - class UnqualifiedMemberAccess extends Identifier { + class UnqualifiedMemberAccess extends Identifier, MemberAccessImpl { private boolean instanceAccess; private NameBinding target; private ClassLikeDeclaration accessingClass; @@ -783,7 +798,7 @@ module Public { } /** Gets the simple name of this identifier, that is, the name of the member being accessed. */ - string getName() { result = this.getValue() } + override string getMemberName() { result = this.getValue() } } } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll index b539ca7e4240..04672886c699 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/CallGraph.qll @@ -1,9 +1,27 @@ private import unified private import AllDataFlow private import codeql.unified.internal.typeinference.TypeInference as T +private import codeql.unified.internal.NameBinding as N +private import codeql.unified.internal.ExprPositions DataFlowCallable viableCallable(DataFlowCall c) { result.asSourceCallable() = T::resolveCallTarget(c.asExplicitCall()) or + result.asSourceCallable() = resolveAccessor(c.asGetterCall()).(GetterDeclaration) + or + result.asSourceCallable() = resolveAccessor(c.asSetterCall()).(SetterDeclaration) + or result.asSummarizedCallable().matchesCall(c.asExplicitCall()) } + +private AccessorDeclaration resolveAccessor(Expr access) { + result.getNameNode() = N::getStaticBindingTargetFromRef(access) + or + not exists(N::getStaticBindingTargetFromRef(access)) and + exists(MemberAccessExpr m, N::NamespaceNode namespace | + access = m and + // TODO: Make type inference own accessor-resolution. It also needs it internally. + namespace.isInstanceMemberNamespace(T::inferType(m.getBase()).asClass()) and + namespace.getMember(m.getMemberName()).isMember(result) + ) +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/CaptureSsa.qll b/unified/ql/lib/codeql/unified/internal/dataflow/CaptureSsa.qll index cd74ad53eafd..9a53e78af93b 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/CaptureSsa.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/CaptureSsa.qll @@ -21,7 +21,7 @@ module CaptureSsaInput implements InputSig { Callable basicBlockGetEnclosingCallable(BasicBlock bb) { result = bb.getEnclosingCallable() } class CapturedVariable extends LocalVariable { - CapturedVariable() { this.isCaptured() } + CapturedVariable() { this.isCaptured() and not this instanceof ComputedLocalVariable } Callable getCallable() { result = super.getDeclaringCallable() } } @@ -35,27 +35,14 @@ module CaptureSsaInput implements InputSig { CapturedParameter() { none() } } - private class TExpr = TCallableNode or TLocalVariableRefNode; - private predicate isTopLevelCallable(BuilderNode callable) { callable.isCallable(any(TopLevel t)) } - class Expr extends TExpr { + class Expr extends Stage2Node { Expr() { not isTopLevelCallable(this) } - string toString() { result = this.(BuilderNode).toString() } - - Location getLocation() { result = this.(BuilderNode).getLocation() } - - predicate hasCfgNode(BasicBlock bb, int i) { - this.(LocalVariableRefNode).hasCfgNode(bb, i) - or - exists(DataFlowCallable callable | - this = TCallableNode(callable) and - bb.getNode(i).injects(callable.asSourceCallable()) - ) - } + predicate hasCfgNode(BasicBlock bb, int i) { this.hasControlFlowPosition(bb, i) } } final private class FinalLocalVariableRefNode = LocalVariableRefNode; @@ -84,7 +71,7 @@ module CaptureSsaInput implements InputSig { node.(BuilderNode).isCallable(callable) or exists(Stage2Node prev | callableHasLocalAlias(callable, prev) | - step(prev, any(Step s | s.value()), node) + step(prev, any(Step s | s.value() or s.callGraph()), node) or localSsaStep(prev, node, _) ) @@ -103,6 +90,21 @@ module CaptureSsaInput implements InputSig { ) } + private predicate isEscape(BuilderNode node) { + node.isResultValue(any(Argument arg).getValue()) + or + node.isSetterValueArgument(_) + or + node.isImplicitArgument(_, _, _) + or + exists(U::Expr e | + isReturnedExpr(_, e) and + node.isResultValue(e) + ) + or + step(node, any(Step s | s.store(_)), _) + } + class ClosureExpr extends Expr instanceof TCallableNode { // TODO: In order to support classes with captures, this needs to cover constructor calls to such classes as well. private Callable callable; @@ -112,8 +114,8 @@ module CaptureSsaInput implements InputSig { predicate hasBody(Callable body) { callable = body } predicate hasAliasedAccess(Expr f) { - // TODO: Restrict to places where the function is called or escapes into a call where it might get called. - callableHasLocalAlias(callable, f) and not f.(LocalVariableRefNode).getRefKind().isWrite() + callableHasLocalAlias(callable, f) and + isEscape(f) } } } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll index b6ceba637ef5..95a20c70e4c4 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowCall.qll @@ -1,11 +1,34 @@ private import unified private import AllDataFlow private import codeql.unified.internal.ExprPositions +private import codeql.unified.internal.NameBinding as N private newtype TDataFlowCall = TExplicitCall(CallExpr call) { not isInBindingContext(call, _) // ignore constructor patterns } or + TGetterCall(Expr expr) { + hasResultValue(expr) and + ( + N::getStaticBindingTargetFromRef(expr) = any(GetterDeclaration g).getNameNode() + or + expr instanceof MemberAccess + ) + } or + TSetterCall(Expr expr) { + ( + isInAssignmentContext(expr, _) + or + // Write back a value that was mutated in-place, such as `x.p` in `x.p.f = y` + isMutatedByCallOrAssign(expr, _) and + mayWriteBackViaSetterCall(expr) + ) and + ( + N::getStaticBindingTargetFromRef(expr) = any(SetterDeclaration g).getNameNode() + or + expr instanceof MemberAccess + ) + } or TSummaryCall(SummarizedCallable callable, SummaryNode callee) { FlowSummaryOutput::summaryCallbackRange(callable, callee) } @@ -20,6 +43,12 @@ class DataFlowCall extends TDataFlowCall { /** Gets the `CallExpr` wrapped by this dataflow call, if any. */ CallExpr asExplicitCall() { this = TExplicitCall(result) } + /** Gets the member/variable access for which this represents the call to a getter. */ + Expr asGetterCall() { this = TGetterCall(result) } + + /** Gets the member/variable access for which this represents the call to a setter. */ + Expr asSetterCall() { this = TSetterCall(result) } + /** Holds if this is a synthesized call to `callee` in `callable`. */ predicate isSummaryCall(SummarizedCallable callable, SummaryNode callee) { this = TSummaryCall(callable, callee) @@ -29,6 +58,10 @@ class DataFlowCall extends TDataFlowCall { string toString() { result = this.asExplicitCall().toString() or + result = "[getter call] " + this.asGetterCall().toString() + or + result = "[setter call] " + this.asSetterCall().toString() + or exists(SummarizedCallable callable, SummaryNode callee | this.isSummaryCall(callable, callee) and result = "[summary] call to " + callee.toString() + " in " + callable.toString() @@ -39,6 +72,10 @@ class DataFlowCall extends TDataFlowCall { Location getLocation() { result = this.asExplicitCall().getLocation() or + result = this.asGetterCall().getLocation() + or + result = this.asSetterCall().getLocation() + or this.isSummaryCall(_, _) and result instanceof EmptyLocation } @@ -46,6 +83,10 @@ class DataFlowCall extends TDataFlowCall { DataFlowCallable getEnclosingCallable() { result.asSourceCallable() = this.asExplicitCall().getEnclosingCallable() or + result.asSourceCallable() = this.asGetterCall().getEnclosingCallable() + or + result.asSourceCallable() = this.asSetterCall().getEnclosingCallable() + or this.isSummaryCall(result.asSummarizedCallable(), _) } } diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll index a4f45f3277d5..d804e790b7a7 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowGraph.qll @@ -92,12 +92,23 @@ predicate step(BuilderNode node1, Step step, BuilderNode node2) { or exists(UnqualifiedMemberAccess access | access.isInstanceAccess() | node1.isLocalVariableRead(access, access.getImplicitQualifierVariable()) and - step.readName(access.getName()) and + step.readName(access.getMemberName()) and node2.isResultValue(access) or (node1.isIncomingValue(access) or node1.isPostUpdate(access)) and - step.storeName(access.getName()) and + step.storeName(access.getMemberName()) and node2.isLocalVariablePostUpdate(access, access.getImplicitQualifierVariable()) + or + // Pass the receiver along to the getter/setter invoked by this expression + exists(DataFlowCall call | access = [call.asGetterCall(), call.asSetterCall()] | + node1.isLocalVariableRead(access, access.getImplicitQualifierVariable()) and + step.value() and + node2.isReceiverArgumentEx(call) + or + node1.isReceiverPostUpdateEx(call) and + step.value() and + node2.isLocalVariablePostUpdate(access, access.getImplicitQualifierVariable()) + ) ) or exists(StringInterpolationExpr expr | @@ -124,6 +135,17 @@ predicate step(BuilderNode node1, Step step, BuilderNode node2) { (node1.isIncomingValue(expr) or node1.isPostUpdate(expr)) and step.storeName(expr.getMemberName()) and node2.isPostUpdate(expr.getBase()) + or + // Pass the receiver along to the getter/setter invoked by this expression + exists(DataFlowCall call | expr = [call.asGetterCall(), call.asSetterCall()] | + node1.isResultValue(expr.getBase()) and + step.value() and + node2.isReceiverArgumentEx(call) + or + node1.isReceiverPostUpdateEx(call) and + step.value() and + node2.isPostUpdate(expr.getBase()) + ) ) or // Calls and constructor-patterns targeting an enum-case constructor. @@ -139,6 +161,20 @@ predicate step(BuilderNode node1, Step step, BuilderNode node2) { node2.isIncomingValue(arg.getValue()) ) or + // For local getters/setters, pass the callable into the callee argument. + // Capture SSA depends on this. + exists(ComputedLocalVariable local, AccessorDeclaration accessor, DataFlowCall call | + accessor = local.getAnAccessor() and + node1.isCallable(accessor) and + step.callGraph() and + ( + call.asGetterCall() = local.getAnAccess() and accessor instanceof GetterDeclaration + or + call.asSetterCall() = local.getAnAccess() and accessor instanceof SetterDeclaration + ) and + node2.isImplicitArgument(call, any(ArgumentPosition pos | pos.isCallee()), false) + ) + or exists(SwitchExpr expr | node1.isResultValue(expr.getValue()) and step.value() and diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll index b84f051047e6..d376be2cb84a 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowInstantiation.qll @@ -41,6 +41,12 @@ module DataFlowInput implements InputSig { FlowSummaryOutput::summaryOutNode(callee, getSummaryNode(n), kind) and call.isSummaryCall(_, callee) ) + or + exists(Expr access | + call.asGetterCall() = access and + n.isResultValue(access) and + kind.isStandardReturnValue() + ) } class OutNode extends Node { @@ -101,6 +107,9 @@ module DataFlowInput implements InputSig { or n.(BuilderNode).isImplicitArgument(call, pos, false) or + n.(BuilderNode).isSetterValueArgument(call) and + pos.asPositional() = 0 + or exists(FlowSummaryOutput::SummaryNode callee | FlowSummaryOutput::summaryArgumentNode(callee, getSummaryNode(n), pos) and call.isSummaryCall(_, callee) @@ -260,6 +269,8 @@ module DataFlowInput implements InputSig { predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() } // TODO predicate allowParameterReturnInSelf(ParameterNode p) { + p.isReceiverParameter(_) + or captureSsaAllowParameterReturnInSelf(p) or exists(DataFlowCallable c, ParameterPosition pos | diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll index b4cf63992ece..337ec1a5cc21 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowNode.qll @@ -27,9 +27,10 @@ private predicate hasIncomingValueAtCfgNode(Expr expr, ControlFlowNode cfgNode) ) } -private predicate hasPostUpdate(Expr expr, ControlFlowNode cfgNode) { +/** Holds if `expr` has a post-update at `cfgNode` resulting from a surrounding call or assignment. */ +private predicate hasPostUpdateAfterCallOrAssign(Expr expr, ControlFlowNode cfgNode) { exists(MemberAccessExpr member | - (hasIncomingValueAtCfgNode(member, cfgNode) or hasPostUpdate(member, cfgNode)) and + (hasIncomingValueAtCfgNode(member, cfgNode) or hasPostUpdateAfterCallOrAssign(member, cfgNode)) and expr = member.getBase() ) or @@ -40,11 +41,36 @@ private predicate hasPostUpdate(Expr expr, ControlFlowNode cfgNode) { ) or exists(Expr target | - hasPostUpdate(target, cfgNode) and + hasPostUpdateAfterCallOrAssign(target, cfgNode) and exprStep(expr, target, _) ) } +/** + * Holds if the value of `expr` is mutated at `cfgNode` by a surrounding call or assignment, + * and thus needs to be written back if `expr` refers to a computed value. + */ +predicate isMutatedByCallOrAssign(Expr expr, ControlFlowNode cfgNode) { + hasPostUpdateAfterCallOrAssign(expr, cfgNode) +} + +/** + * Holds if `expr` has a post-update at `cfgNode`. + */ +private predicate hasPostUpdate(Expr expr, ControlFlowNode cfgNode) { + hasPostUpdateAfterCallOrAssign(expr, cfgNode) + or + not hasPostUpdateAfterCallOrAssign(expr, _) and + exists(MemberAccessExpr member | + // If the member access is not updated by call or assignment, it might still be a call to a getter, + // and thus needs a post-update. Generate a post-update for this case. + // In case it's a call to a getter, while also being post-updated from a call or assignment, we accept + // the imprecision and use the post-update position from the call or assignment. + expr = member.getBase() and + cfgNode.isAfter(member) + ) +} + /** * Holds if `repr` performs an access to `var` of the given `kind` at `cfgNode`. * @@ -69,6 +95,11 @@ predicate performsVariableAccess( (hasIncomingValueAtCfgNode(access, cfgNode) or hasPostUpdate(access, cfgNode)) and kind.isPostUpdate() or + // Generate a post-update for the receiver in case this is a call to a getter that mutates 'self'. + not (hasIncomingValueAtCfgNode(access, _) or hasPostUpdate(access, _)) and + cfgNode.isAfter(access) and + kind.isPostUpdate() + or exists(CallExpr call | access = call.getCallee() and cfgNode.isAfter(call) and @@ -155,6 +186,22 @@ class BuilderNode extends TDataFlowNodeStage1 { /** Holds if this represents the updated state of the value returned by `expr` after it has been mutated by the surrounding assignment or call. */ predicate isPostUpdate(Expr expr) { this = TExprPostUpdateNode(expr) } + /** + * Holds if this node represents the new value passed to the setter invoked by `call`. + * + * For a direct assignment, such as `x.p = y`, this is the incoming value of `x.p`. + * When the value of a computed property is mutated in place, such as `x.p.f = y`, + * this is the post-update node of `x.p`, as the mutated value is written back via the setter. + */ + predicate isSetterValueArgument(DataFlowCall call) { + exists(Expr access | access = call.asSetterCall() | + this.isIncomingValue(access) + or + not isInAssignmentContext(access, _) and + this.isPostUpdate(access) + ) + } + /** Holds if this node is the canonical representative for the return value of `callable`. */ predicate isReturnNode(Callable callable) { this.isReturnNodeEx(toDataFlowCallable(callable)) } @@ -186,13 +233,21 @@ class BuilderNode extends TDataFlowNodeStage1 { } /** Holds if this node represents the receiver argument passed to `call`. */ - predicate isReceiverArgument(CallExpr call) { - this.isImplicitArgument(toDataFlowCall(call), any(ArgumentPosition p | p.isReceiver()), false) + predicate isReceiverArgumentEx(DataFlowCall call) { + this.isImplicitArgument(call, any(ArgumentPosition p | p.isReceiver()), false) } + /** Holds if this node represents the receiver argument passed to `call`. */ + predicate isReceiverArgument(CallExpr call) { this.isReceiverArgumentEx(toDataFlowCall(call)) } + /** Holds if this node represents the updated state of the receiver of `call` after the call returns. */ predicate isReceiverPostUpdate(CallExpr call) { - this.isImplicitArgument(toDataFlowCall(call), any(ArgumentPosition p | p.isReceiver()), true) + this.isReceiverPostUpdateEx(toDataFlowCall(call)) + } + + /** Holds if this node represents the updated state of the receiver of `call` after the call returns. */ + predicate isReceiverPostUpdateEx(DataFlowCall call) { + this.isImplicitArgument(call, any(ArgumentPosition p | p.isReceiver()), true) } /** Holds if this is the canonical representative for the given `callable`. */ @@ -312,6 +367,64 @@ class BuilderNode extends TDataFlowNodeStage1 { result = call.getLocation() ) } + + /** + * Holds if this data flow node is associated with the `i`'th index in the given basic block. + * + * Note that some data flow nodes may have an index appearing before the first or after the + * last `ControlFlowNode` node in the basic block. Multiple data flow nodes may share the same control flow position. + * + * Also note that some data flow nodes have no associated control flow position, either because they are + * in unreachable code, or belong to a synthesized callable that has no control flow graph. + */ + predicate hasControlFlowPosition(BasicBlock bb, int i) { + exists(ControlFlowNode cfgNode | cfgNode = bb.getNode(i) | + exists(Expr expr | + this.isResultValue(expr) and cfgNode.asExpr() = expr + or + this.isIncomingValue(expr) and hasIncomingValueAtCfgNode(expr, cfgNode) + or + this.isPostUpdate(expr) and hasPostUpdate(expr, cfgNode) + ) + or + exists(AstNode repr, LocalVariable var, VariableRefKind kind | + this.isLocalVariableRef(repr, var, kind) and + performsVariableAccess(repr, var, kind, cfgNode) + ) + or + exists(DataFlowCallable callable | + this.isImplicitParameter(callable, _) and + cfgNode.(ControlFlow::EntryNode).getEnclosingCallable() = callable.asSourceCallable() + or + this.isCallableEx(callable) and + cfgNode.injects(callable.asSourceCallable()) + ) + or + exists(DataFlowCall call, Expr access, boolean isPost | + this.isImplicitArgument(call, _, isPost) + | + (access = call.asExplicitCall() or access = call.asGetterCall()) and + ( + isPost = false and cfgNode.injects(access) + or + isPost = true and cfgNode.isAfter(access) + ) + or + access = call.asSetterCall() and + ( + exists(Assignment assignment | isInAssignmentContext(access, assignment) | + isPost = false and cfgNode.injects(assignment) + or + isPost = true and cfgNode.isAfter(assignment) + ) + or + // Setter invoked to write back a mutated value + not isInAssignmentContext(access, _) and + isMutatedByCallOrAssign(access, cfgNode) + ) + ) + ) + } } /** A node in stage 2, which includes stage 1 and local SSA nodes. */ @@ -335,6 +448,16 @@ class Stage2Node extends TDataFlowNodeStage2 { result = node.getLocation() ) } + + predicate hasControlFlowPosition(BasicBlock bb, int i) { + this.(BuilderNode).hasControlFlowPosition(bb, i) + or + exists(LocalSsaDataFlowOutput::SsaNode node | + this = TLocalSsaNode(node) and + bb = node.getBasicBlock() and + i = node.getIndex() // TODO: why is this marked as internal in the SSA library? + ) + } } class Node extends TDataFlowNode { @@ -483,44 +606,7 @@ class Node extends TDataFlowNode { * in unreachable code, or belong to a synthesized callable that has no control flow graph. */ predicate hasControlFlowPosition(BasicBlock bb, int i) { - exists(ControlFlowNode cfgNode | cfgNode = bb.getNode(i) | - exists(Expr expr | - this.isResultValue(expr) and cfgNode.asExpr() = expr - or - this.isIncomingValue(expr) and hasIncomingValueAtCfgNode(expr, cfgNode) - or - this.isPostUpdate(expr) and hasPostUpdate(expr, cfgNode) - ) - or - exists(AstNode repr, LocalVariable var, VariableRefKind kind | - this.isLocalVariableRef(repr, var, kind) and - performsVariableAccess(repr, var, kind, cfgNode) - ) - or - exists(DataFlowCallable callable | - this.(BuilderNode).isImplicitParameter(callable, _) and - cfgNode.(ControlFlow::EntryNode).getEnclosingCallable() = callable.asSourceCallable() - or - this.isCallableEx(callable) and - cfgNode.injects(callable.asSourceCallable()) - ) - or - exists(DataFlowCall call, CallExpr sourceCall, boolean isPost | - call.asExplicitCall() = sourceCall and - this.(BuilderNode).isImplicitArgument(call, _, isPost) and - ( - isPost = false and cfgNode.injects(sourceCall) - or - isPost = true and cfgNode.isAfter(sourceCall) - ) - ) - ) - or - exists(LocalSsaDataFlowOutput::SsaNode node | - this = TLocalSsaNode(node) and - bb = node.getBasicBlock() and - i = node.getIndex() // TODO: why is this marked as internal in the SSA library? - ) + this.(Stage2Node).hasControlFlowPosition(bb, i) or exists(CaptureSsaOutput::SynthesizedCaptureNode node | this = TCaptureSsaNode(node) and diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll index 05a26ad2d9b4..9b48861293e2 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPlugin.qll @@ -26,6 +26,15 @@ class DataFlowPlugin extends Unit { * This affects the steps generated by the language-agnostic code as well as steps from the plugin itself. */ predicate bypassContentSet(ContentSet contents) { none() } + + /** + * Holds if mutating the value of `expr` in-place, such as `expr.f = y` or `expr.mutate()`, + * may cause the mutated value to be written back via a setter call. + * + * `expr` is an expression in getter position, such as a member access. + */ + bindingset[expr] + predicate mayWriteBackViaSetterCall(Expr expr) { none() } } /** @@ -40,3 +49,12 @@ predicate bypassContentSet(ContentSet contents) { any(DataFlowPlugin p).bypassCo predicate exprStep(Expr expr1, Expr expr2, boolean mustFlow) { any(DataFlowPlugin p).exprStep(expr1, expr2, mustFlow) } + +/** + * Holds if mutating the value of `expr` in-place, such as `expr.f = y` or `expr.mutate()`, + * may cause the mutated value to be written back via a setter call. + */ +bindingset[expr] +predicate mayWriteBackViaSetterCall(Expr expr) { + any(DataFlowPlugin p).mayWriteBackViaSetterCall(expr) +} diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll index 95247afd8e43..7147a0a7101c 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/DataFlowPluginSwift.qll @@ -11,6 +11,10 @@ private class SwiftDataFlowPlugin extends DataFlowPlugin { contents.asSingleton().asNamedMember() = "some.0" } + // Swift writes back mutated values of computed properties via their setter + bindingset[expr] + override predicate mayWriteBackViaSetterCall(Expr expr) { any() } + override predicate exprStep(Expr expr1, Expr expr2, boolean mustFlow) { mustFlow = true and exists(UnaryExpr expr | diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll index 2a92691a6347..55ae2926b438 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/LocalSsa.qll @@ -10,7 +10,7 @@ private import codeql.util.Void module LocalSsaInput implements InputSig { class SourceVariable extends LocalVariable { - SourceVariable() { not this.isCaptured() } + SourceVariable() { not this.isCaptured() and not this instanceof ComputedLocalVariable } } predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) { diff --git a/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll b/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll index 4f54fe36fd65..c4f3fc10e24d 100644 --- a/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll +++ b/unified/ql/lib/codeql/unified/internal/dataflow/Step.qll @@ -7,7 +7,8 @@ private newtype TStep = TJumpStep() or TTaintStep() or TReadStep(ContentSet contents) or - TStoreStep(ContentSet contents) + TStoreStep(ContentSet contents) or + TCallGraphStep() /** * A type of data flow step, used during construction of the data flow graph. @@ -47,6 +48,9 @@ class Step extends TStep { pragma[nomagic] predicate storeArrayElement() { this.store(ContentSet::arrayElement()) } + /** Holds if this step can only be used for call graph construction. */ + predicate callGraph() { this = TCallGraphStep() } + string toString() { this.value() and result = "value" or @@ -61,5 +65,7 @@ class Step extends TStep { or this.store(contents) and result = "store[" + contents + "]" ) + or + this.callGraph() and result = "call-graph" } } diff --git a/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll b/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll index 181e129fc955..0395b24b028a 100644 --- a/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll +++ b/unified/ql/lib/codeql/unified/internal/typeinference/Type.qll @@ -88,6 +88,9 @@ abstract private class TypeImpl extends TType { /** Gets the location of this type. */ abstract Location getLocation(); + + /** Gets the class whose instances are described by this type. */ + ClassLikeDeclaration asClass() { this = TClassLikeDeclarationType(result) } } /** diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 5c93d547be59..54ede4d9d45c 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -155,9 +155,9 @@ unified_accessor_declaration_parameter( unique int parameter: @unified_parameter ref ); -unified_accessor_declaration_type( +unified_accessor_declaration_return_type( unique int unified_accessor_declaration: @unified_accessor_declaration ref, - unique int type__: @unified_expr ref + unique int return_type: @unified_expr ref ); unified_accessor_declaration_def( diff --git a/unified/ql/test/library-tests/dataflow/accessors_instance.swift b/unified/ql/test/library-tests/dataflow/accessors_instance.swift new file mode 100644 index 000000000000..4771c48dc2a6 --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/accessors_instance.swift @@ -0,0 +1,154 @@ +func source(_ s: String) -> String { return s } + +@discardableResult +func sink(_ s: Any) -> String { return "" } + +func t1() { + class C { + var x: String { source("t1.1") } + } + sink(C().x) // $ hasValueFlow=t1.1 +} + +func t2() { + // Note: swift-format will try to remove the explicit 'get' below + // swift-format-ignore + class C { + var x: String { + get { source("t2.1") } + } + } + sink(C().x) // $ hasValueFlow=t2.1 +} + +func t3() { + class C { + var x: String { + get { source("t3.1") } + set {} + } + } + sink(C().x) // $ hasValueFlow=t3.1 +} + +func t4() { + class C { + var x: String { + get {} + set { sink(newValue) } // $ hasValueFlow=t4.1 + } + } + C().x = source("t4.1") +} + +func t4a() { + class C { + var x: String { + get {} + set(v) { sink(v) } // $ hasValueFlow=t4a.1 + } + } + C().x = source("t4a.1") +} + +func t5() { + class C { + var x: String { + get { source("t5.1") } + set { sink(newValue) } // $ hasValueFlow=t5.1 + } + } + let c = C() + c.x = c.x +} + +func t6() { + class C { + var x: String { source("t6.1") } + + func method() { + sink(self.x) // $ hasValueFlow=t6.1 + sink(x) // $ hasValueFlow=t6.1 + } + } +} + +func t7() { + class C { + var field: String = "safe" + var x: String { self.field } + } + var c = C() + sink(c.x) // no flow + c.field = source("t7.1") + sink(c.x) // $ hasValueFlow=t7.1 +} + +func t8() { + class C { + var field: String = "safe" + var otherField: String = "safe" + var x: String { + get {} + set { otherField = field + newValue } + } + } + var c = C() + c.field = source("t8.1") + c.x = "safe" // trigger the setter + sink(c.otherField) // $ hasTaintFlow=t8.1 +} + +func t9() { + class C { + var field: String = "safe" + var x: String { + field = source("t9.1") + return "" + } + } + let c = C() + let ignored = c.x + sink(c.field) // $ hasValueFlow=t9.1 +} + +func t10() { + class C { + var field: String = "safe" + var x: String { + field = source("t10.1") + return "" + } + + func method() { + let ignored = x + sink(field) // $ hasValueFlow=t10.1 + } + } +} + +func t11() { + class C { + var p: (String, String) { + get { ("safe", "safe") } + set { sink(newValue.0) } // $ hasValueFlow=t11.1 + } + } + let c = C() + c.p.0 = source("t11.1") +} + +func t12() { + struct S { + var field: String = "safe" + mutating func update() { field = source("t12.1") } + } + class C { + var s: S { + get { S() } + set { sink(newValue.field) } // $ MISSING: hasValueFlow=t12.1 + } + } + let c = C() + c.s.update() // MISSING: the call to 'update' is not resolved +} diff --git a/unified/ql/test/library-tests/dataflow/accessors_local.swift b/unified/ql/test/library-tests/dataflow/accessors_local.swift new file mode 100644 index 000000000000..51c5a62e2894 --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/accessors_local.swift @@ -0,0 +1,108 @@ +func source(_ s: String) -> String { return s } + +@discardableResult +func sink(_ s: Any) -> String { return "" } + +func t1() { + var x: String { source("t1.1") } + sink(x) // $ hasValueFlow=t1.1 +} + +func t2() { + // Note: swift-format will try to remove the explicit 'get' below + // swift-format-ignore + var x: String { + get { source("t2.1") } + } + sink(x) // $ hasValueFlow=t2.1 +} + +func t3() { + var x: String { + get { source("t3.1") } + set {} + } + sink(x) // $ hasValueFlow=t3.1 +} + +func t4() { + var x: String { + get {} + set { sink(newValue) } // $ hasValueFlow=t4.1 + } + x = source("t4.1") +} + +func t5() { + var x: String { + get { source("t5.1") } + set { sink(newValue) } // $ hasValueFlow=t5.1 + } + x = x +} + +func t6() { + var y: String = source("t6.1") + var x: String { y } + sink(x) // $ hasValueFlow=t6.1 +} + +func t7() { + var y: String = "safe" + var x: String { y } + sink(x) // no flow + y = source("t7.1") + sink(x) // $ hasValueFlow=t7.1 +} + +func t8() { + var x: String { source("t8.1") } + let closure = { + sink(x) // $ hasValueFlow=t8.1 + } + closure() +} + +func t9() { + var y: String = "safe" + var x: String { y } + let closure = { + sink(x) // $ hasValueFlow=t9.1 + } + y = source("t9.1") + closure() +} + +func t10() { + var y: String = "safe" + var x1: String { y } + var x2: String { x1 } + var x3: String { x2 } + let closure = { + sink(x3) // $ hasValueFlow=t9.1 + } + y = source("t9.1") + closure() +} + +func t11() { + var p: (String, String) { + get { ("safe", "safe") } + set { sink(newValue.0) } // $ hasValueFlow=t11.1 + } + p.0 = source("t11.1") +} + +func t12() { + struct S { + var field: String = "safe" + mutating func mutate() { field = source("t12.1") } + } + var p: S { + get { S() } + set { sink(newValue.field) } // $ MISSING: 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/accessors_static.swift b/unified/ql/test/library-tests/dataflow/accessors_static.swift new file mode 100644 index 000000000000..42a9299164b8 --- /dev/null +++ b/unified/ql/test/library-tests/dataflow/accessors_static.swift @@ -0,0 +1,67 @@ +func source(_ s: String) -> String { return s } + +@discardableResult +func sink(_ s: Any) -> String { return "" } + +func t1() { + class C { + static var x: String { source("t1.1") } + } + sink(C.x) // $ hasValueFlow=t1.1 +} + +func t2() { + // Note: swift-format will try to remove the explicit 'get' below + // swift-format-ignore + class C { + static var x: String { + get { source("t2.1") } + } + } + sink(C.x) // $ hasValueFlow=t2.1 +} + +func t3() { + class C { + static var x: String { + get { source("t3.1") } + set {} + } + } + sink(C.x) // $ hasValueFlow=t3.1 +} + +func t4() { + class C { + static var x: String { + get {} + set { sink(newValue) } // $ hasValueFlow=t4.1 + } + } + C.x = source("t4.1") +} + +func t5() { + class C { + static var x: String { + get { source("t5.1") } + set { sink(newValue) } // $ hasValueFlow=t5.1 + } + } + C.x = C.x +} + +func t6() { + class C { + static var x: String { source("t6.1") } + + func method() { + sink(Self.x) // $ hasValueFlow=t6.1 + } + + static func staticMethod() { + sink(Self.x) // $ hasValueFlow=t6.1 + sink(x) // $ hasValueFlow=t6.1 + } + } +} diff --git a/unified/ql/test/library-tests/dataflow/test.expected b/unified/ql/test/library-tests/dataflow/test.expected index 5ec726ff99be..5f5ceee231f9 100644 --- a/unified/ql/test/library-tests/dataflow/test.expected +++ b/unified/ql/test/library-tests/dataflow/test.expected @@ -1,5 +1,82 @@ models edges +| accessors_instance.swift:8:25:8:38 | source(...) | accessors_instance.swift:10:10:10:14 | ... .x | provenance | | +| accessors_instance.swift:18:19:18:32 | source(...) | accessors_instance.swift:21:10:21:14 | ... .x | provenance | | +| accessors_instance.swift:27:19:27:32 | source(...) | accessors_instance.swift:31:10:31:14 | ... .x | provenance | | +| accessors_instance.swift:38:13:38:15 | newValue | accessors_instance.swift:38:24:38:31 | newValue | provenance | | +| accessors_instance.swift:41:5:41:9 | ... .x | accessors_instance.swift:38:13:38:15 | newValue | provenance | | +| accessors_instance.swift:41:13:41:26 | source(...) | accessors_instance.swift:41:5:41:9 | ... .x | provenance | | +| accessors_instance.swift:48:17:48:17 | v | accessors_instance.swift:48:27:48:27 | v | provenance | | +| accessors_instance.swift:51:5:51:9 | ... .x | accessors_instance.swift:48:17:48:17 | v | provenance | | +| accessors_instance.swift:51:13:51:27 | source(...) | accessors_instance.swift:51:5:51:9 | ... .x | provenance | | +| accessors_instance.swift:57:19:57:32 | source(...) | accessors_instance.swift:62:11:62:13 | ... .x | provenance | | +| accessors_instance.swift:58:13:58:15 | newValue | accessors_instance.swift:58:24:58:31 | newValue | provenance | | +| accessors_instance.swift:62:5:62:7 | ... .x | accessors_instance.swift:58:13:58:15 | newValue | provenance | | +| accessors_instance.swift:62:11:62:13 | ... .x | accessors_instance.swift:62:5:62:7 | ... .x | provenance | | +| accessors_instance.swift:67:25:67:38 | source(...) | accessors_instance.swift:70:18:70:23 | ... .x | provenance | | +| accessors_instance.swift:67:25:67:38 | source(...) | accessors_instance.swift:71:18:71:18 | x | provenance | | +| accessors_instance.swift:79:25:79:28 | self [field] | accessors_instance.swift:79:25:79:34 | ... .field | provenance | | +| accessors_instance.swift:83:5:83:5 | [post] c [field] | accessors_instance.swift:84:10:84:10 | c [field] | provenance | | +| accessors_instance.swift:83:5:83:11 | ... .field | accessors_instance.swift:83:5:83:5 | [post] c [field] | provenance | | +| accessors_instance.swift:83:15:83:28 | source(...) | accessors_instance.swift:83:5:83:11 | ... .field | provenance | | +| accessors_instance.swift:84:10:84:10 | c [field] | accessors_instance.swift:79:25:79:28 | self [field] | provenance | | +| accessors_instance.swift:84:10:84:10 | c [field] | accessors_instance.swift:84:10:84:12 | ... .x | provenance | | +| accessors_instance.swift:93:32:93:36 | field | accessors_instance.swift:93:19:93:28 | otherField | provenance | | +| accessors_instance.swift:97:5:97:5 | [post] c [field] | accessors_instance.swift:98:5:98:5 | c [field] | provenance | | +| accessors_instance.swift:97:5:97:11 | ... .field | accessors_instance.swift:97:5:97:5 | [post] c [field] | provenance | | +| accessors_instance.swift:97:15:97:28 | source(...) | accessors_instance.swift:97:5:97:11 | ... .field | provenance | | +| accessors_instance.swift:98:5:98:5 | c [field] | accessors_instance.swift:93:32:93:36 | field | provenance | | +| accessors_instance.swift:98:5:98:5 | c [field] | accessors_instance.swift:99:10:99:10 | c [otherField] | provenance | | +| accessors_instance.swift:99:10:99:10 | c [otherField] | accessors_instance.swift:99:10:99:21 | ... .otherField | provenance | | +| accessors_instance.swift:106:13:106:17 | field | accessors_instance.swift:112:10:112:10 | c [field] | provenance | | +| accessors_instance.swift:106:21:106:34 | source(...) | accessors_instance.swift:106:13:106:17 | field | provenance | | +| accessors_instance.swift:112:10:112:10 | c [field] | accessors_instance.swift:112:10:112:16 | ... .field | provenance | | +| 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:13:134:15 | newValue [0] | accessors_instance.swift:134:24:134:31 | newValue [0] | 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:13:134:15 | 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_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 | | +| accessors_local.swift:31:9:31:11 | newValue | accessors_local.swift:31:20:31:27 | newValue | provenance | | +| accessors_local.swift:33:5:33:5 | x | accessors_local.swift:31:9:31:11 | newValue | provenance | | +| accessors_local.swift:33:9:33:22 | source(...) | accessors_local.swift:33:5:33:5 | x | provenance | | +| accessors_local.swift:38:15:38:28 | source(...) | accessors_local.swift:41:9:41:9 | x | provenance | | +| accessors_local.swift:39:9:39:11 | newValue | accessors_local.swift:39:20:39:27 | newValue | provenance | | +| accessors_local.swift:41:5:41:5 | x | accessors_local.swift:39:9:39:11 | newValue | provenance | | +| accessors_local.swift:41:9:41:9 | x | accessors_local.swift:41:5:41:5 | x | provenance | | +| accessors_local.swift:45:9:45:9 | y | accessors_local.swift:46:21:46:21 | y | provenance | | +| accessors_local.swift:45:9:45:9 | y | accessors_local.swift:47:10:47:10 | x | provenance | | +| accessors_local.swift:45:21:45:34 | source(...) | accessors_local.swift:45:9:45:9 | y | provenance | | +| accessors_local.swift:54:5:54:5 | y | accessors_local.swift:52:21:52:21 | y | provenance | | +| accessors_local.swift:54:5:54:5 | y | accessors_local.swift:55:10:55:10 | x | provenance | | +| accessors_local.swift:54:9:54:22 | source(...) | accessors_local.swift:54:5:54:5 | y | provenance | | +| accessors_local.swift:59:21:59:34 | source(...) | accessors_local.swift:61:14:61:14 | x | provenance | | +| accessors_local.swift:72:5:72:5 | y | accessors_local.swift:70:14:70:14 | x | provenance | | +| accessors_local.swift:72:9:72:22 | source(...) | accessors_local.swift:72:5:72:5 | y | provenance | | +| accessors_local.swift:84:5:84:5 | y | accessors_local.swift:82:14:82:15 | x3 | provenance | | +| accessors_local.swift:84:9:84:22 | source(...) | accessors_local.swift:84:5:84:5 | y | provenance | | +| accessors_local.swift:91:9:91:11 | newValue [0] | accessors_local.swift:91:20:91:27 | newValue [0] | provenance | | +| accessors_local.swift:91:20:91:27 | newValue [0] | accessors_local.swift:91:20:91:29 | ... .0 | provenance | | +| accessors_local.swift:93:5:93:5 | [post] p [0] | accessors_local.swift:91:9:91:11 | 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_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 | | +| accessors_static.swift:38:13:38:15 | newValue | accessors_static.swift:38:24:38:31 | newValue | provenance | | +| accessors_static.swift:41:5:41:7 | ... .x | accessors_static.swift:38:13:38:15 | newValue | provenance | | +| accessors_static.swift:41:11:41:24 | source(...) | accessors_static.swift:41:5:41:7 | ... .x | provenance | | +| accessors_static.swift:47:19:47:32 | source(...) | accessors_static.swift:51:11:51:13 | ... .x | provenance | | +| accessors_static.swift:48:13:48:15 | newValue | accessors_static.swift:48:24:48:31 | newValue | provenance | | +| accessors_static.swift:51:5:51:7 | ... .x | accessors_static.swift:48:13:48:15 | newValue | provenance | | +| accessors_static.swift:51:11:51:13 | ... .x | accessors_static.swift:51:5:51:7 | ... .x | provenance | | +| accessors_static.swift:56:32:56:45 | source(...) | accessors_static.swift:59:18:59:23 | ... .x | provenance | | +| accessors_static.swift:56:32:56:45 | source(...) | accessors_static.swift:63:18:63:23 | ... .x | provenance | | +| accessors_static.swift:56:32:56:45 | source(...) | accessors_static.swift:64:18:64:18 | x | provenance | | | calls.swift:7:19:7:19 | x | calls.swift:8:14:8:14 | x | provenance | | | calls.swift:10:12:10:25 | source(...) | calls.swift:7:19:7:19 | x | provenance | | | calls.swift:15:16:15:29 | source(...) | calls.swift:17:10:17:17 | target(...) | provenance | | @@ -38,19 +115,17 @@ edges | calls.swift:99:18:99:21 | self [field] | calls.swift:99:18:99:27 | ... .field | provenance | | | calls.swift:111:18:111:21 | self [field] | calls.swift:111:18:111:27 | ... .field | provenance | | | calls.swift:123:18:123:21 | self [field] | calls.swift:123:18:123:27 | ... .field | provenance | | -| capture.swift:10:9:10:9 | x | capture.swift:14:5:14:11 | closure [x] | provenance | | +| capture.swift:10:9:10:9 | x | capture.swift:12:14:12:14 | x | provenance | | | capture.swift:10:13:10:26 | source(...) | capture.swift:10:9:10:9 | x | provenance | | -| capture.swift:14:5:14:11 | closure [x] | capture.swift:12:14:12:14 | x | provenance | | | capture.swift:20:9:20:9 | x | capture.swift:24:10:24:10 | x | provenance | | | capture.swift:20:13:20:26 | source(...) | capture.swift:20:9:20:9 | x | provenance | | -| capture.swift:28:9:28:9 | x | capture.swift:37:5:37:11 | closure [x] | provenance | | +| capture.swift:28:9:28:9 | x | capture.swift:32:14:32:14 | x | provenance | | +| capture.swift:28:9:28:9 | x | capture.swift:33:14:33:14 | x | provenance | | +| capture.swift:28:9:28:9 | x | capture.swift:38:10:38:11 | y1 | provenance | | +| capture.swift:28:9:28:9 | x | capture.swift:39:10:39:11 | y2 | provenance | | | capture.swift:28:13:28:26 | source(...) | capture.swift:28:9:28:9 | x | provenance | | | capture.swift:32:14:32:14 | x | capture.swift:32:9:32:10 | y1 | provenance | | | capture.swift:33:14:33:14 | x | capture.swift:33:9:33:10 | y2 | provenance | | -| capture.swift:37:5:37:11 | closure [x] | capture.swift:32:14:32:14 | x | provenance | | -| capture.swift:37:5:37:11 | closure [x] | capture.swift:33:14:33:14 | x | provenance | | -| capture.swift:37:5:37:11 | closure [x] | capture.swift:38:10:38:11 | y1 | provenance | | -| capture.swift:37:5:37:11 | closure [x] | capture.swift:39:10:39:11 | y2 | provenance | | | capture.swift:43:9:43:9 | x | capture.swift:49:13:49:13 | x | provenance | | | capture.swift:43:13:43:26 | source(...) | capture.swift:43:9:43:9 | x | provenance | | | capture.swift:45:22:45:24 | arg | capture.swift:46:13:46:15 | arg | provenance | | @@ -65,70 +140,58 @@ edges | capture.swift:60:13:60:13 | x | capture.swift:56:22:56:24 | arg | provenance | | | capture.swift:60:13:60:13 | x | capture.swift:61:10:61:10 | y [0] | provenance | | | capture.swift:61:10:61:10 | y [0] | capture.swift:61:10:61:12 | ... .0 | provenance | | -| capture.swift:69:5:69:5 | x | capture.swift:70:5:70:11 | closure [x] | provenance | | +| capture.swift:69:5:69:5 | x | capture.swift:67:14:67:14 | x | provenance | | | capture.swift:69:9:69:22 | source(...) | capture.swift:69:5:69:5 | x | provenance | | -| capture.swift:70:5:70:11 | closure [x] | capture.swift:67:14:67:14 | x | provenance | | -| capture.swift:74:9:74:9 | x | capture.swift:78:5:78:9 | local [x] | provenance | | +| capture.swift:74:9:74:9 | x | capture.swift:76:14:76:14 | x | provenance | | | capture.swift:74:13:74:26 | source(...) | capture.swift:74:9:74:9 | x | provenance | | -| capture.swift:78:5:78:9 | local [x] | capture.swift:76:14:76:14 | x | provenance | | | capture.swift:84:9:84:9 | x | capture.swift:88:10:88:10 | x | provenance | | | capture.swift:84:13:84:26 | source(...) | capture.swift:84:9:84:9 | x | provenance | | -| capture.swift:93:9:93:9 | x | capture.swift:94:13:94:17 | local [x] | provenance | | +| capture.swift:93:9:93:9 | x | capture.swift:94:13:94:19 | local(...) | provenance | | +| capture.swift:93:9:93:9 | x | capture.swift:98:14:98:14 | x | provenance | | +| capture.swift:93:9:93:9 | x | capture.swift:99:16:99:16 | x | provenance | | | capture.swift:93:13:93:26 | source(...) | capture.swift:93:9:93:9 | x | provenance | | | capture.swift:94:9:94:9 | y | capture.swift:95:10:95:10 | y | provenance | | -| capture.swift:94:13:94:17 | local [x] | capture.swift:94:13:94:19 | local(...) | provenance | | -| capture.swift:94:13:94:17 | local [x] | capture.swift:98:14:98:14 | x | provenance | | -| capture.swift:94:13:94:17 | local [x] | capture.swift:99:16:99:16 | x | provenance | | | capture.swift:94:13:94:19 | local(...) | capture.swift:94:9:94:9 | y | provenance | | | capture.swift:104:9:104:9 | x | capture.swift:105:28:105:28 | x | provenance | | | capture.swift:104:9:104:9 | x | capture.swift:108:23:108:23 | x | provenance | | | capture.swift:104:13:104:27 | source(...) | capture.swift:104:9:104:9 | x | provenance | | -| capture.swift:105:23:105:24 | x1 | capture.swift:111:5:111:12 | closure1 [x1] | provenance | | +| capture.swift:105:23:105:24 | x1 | capture.swift:106:14:106:15 | x1 | provenance | | | capture.swift:105:28:105:28 | x | capture.swift:105:23:105:24 | x1 | provenance | | | capture.swift:108:23:108:23 | x | capture.swift:108:23:108:23 | x | provenance | | -| capture.swift:108:23:108:23 | x | capture.swift:112:5:112:12 | closure2 [x] | provenance | | -| capture.swift:111:5:111:12 | closure1 [x1] | capture.swift:106:14:106:15 | x1 | provenance | | -| capture.swift:112:5:112:12 | closure2 [x] | capture.swift:109:14:109:14 | x | provenance | | +| capture.swift:108:23:108:23 | x | capture.swift:109:14:109:14 | x | provenance | | | capture.swift:116:9:116:9 | x | capture.swift:117:26:117:26 | x | provenance | | | capture.swift:116:13:116:27 | source(...) | capture.swift:116:9:116:9 | x | provenance | | | capture.swift:117:22:117:22 | x | capture.swift:117:36:117:36 | x | provenance | | -| capture.swift:117:22:117:22 | x | capture.swift:121:5:121:11 | closure [x] | provenance | | +| capture.swift:117:22:117:22 | x | capture.swift:118:14:118:14 | x | provenance | | | capture.swift:117:26:117:26 | x | capture.swift:117:22:117:22 | x | provenance | | -| capture.swift:117:29:117:32 | blah | capture.swift:121:5:121:11 | closure [blah] | provenance | | +| capture.swift:117:29:117:32 | blah | capture.swift:119:14:119:17 | blah | provenance | | | capture.swift:117:36:117:36 | x | capture.swift:117:29:117:32 | blah | provenance | | -| capture.swift:121:5:121:11 | closure [blah] | capture.swift:119:14:119:17 | blah | provenance | | -| capture.swift:121:5:121:11 | closure [x] | capture.swift:118:14:118:14 | x | provenance | | -| capture.swift:147:5:147:5 | x | capture.swift:149:5:149:12 | closure2 [x] | provenance | | +| capture.swift:147:5:147:5 | x | capture.swift:143:14:143:14 | x | provenance | | | capture.swift:147:9:147:23 | source(...) | capture.swift:147:5:147:5 | x | provenance | | -| capture.swift:149:5:149:12 | closure2 [x] | capture.swift:143:14:143:14 | x | provenance | | -| capture.swift:156:9:156:9 | x | capture.swift:160:9:160:15 | closure [self, x] | provenance | | +| capture.swift:156:9:156:9 | x | capture.swift:158:18:158:21 | self [x] | provenance | | | capture.swift:156:13:156:25 | source(...) | capture.swift:156:9:156:9 | x | provenance | | | capture.swift:158:18:158:21 | self [x] | capture.swift:158:18:158:23 | ... .x | provenance | | -| capture.swift:160:9:160:15 | closure [self, x] | capture.swift:158:18:158:21 | self [x] | provenance | | | capture.swift:164:9:164:9 | x | capture.swift:165:26:165:29 | self [x] | provenance | | | capture.swift:164:13:164:25 | source(...) | capture.swift:164:9:164:9 | x | provenance | | | capture.swift:165:26:165:29 | self [x] | capture.swift:165:26:165:29 | self [x] | provenance | | -| capture.swift:165:26:165:29 | self [x] | capture.swift:169:9:169:15 | closure [self, x] | provenance | | +| capture.swift:165:26:165:29 | self [x] | capture.swift:166:18:166:21 | self [x] | provenance | | +| capture.swift:165:26:165:29 | self [x] | capture.swift:167:18:167:18 | x | provenance | | | capture.swift:166:18:166:21 | self [x] | capture.swift:166:18:166:23 | ... .x | provenance | | -| capture.swift:169:9:169:15 | closure [self, x] | capture.swift:166:18:166:21 | self [x] | provenance | | -| capture.swift:169:9:169:15 | closure [self, x] | capture.swift:167:18:167:18 | x | provenance | | | capture.swift:173:9:173:9 | x | capture.swift:174:31:174:34 | self [x] | provenance | | | capture.swift:173:13:173:25 | source(...) | capture.swift:173:9:173:9 | x | provenance | | | capture.swift:174:31:174:34 | self [x] | capture.swift:174:31:174:34 | self [x] | provenance | | -| capture.swift:174:31:174:34 | self [x] | capture.swift:179:9:179:15 | closure [self, x] | provenance | | +| capture.swift:174:31:174:34 | self [x] | capture.swift:175:23:175:26 | self [x] | provenance | | | capture.swift:175:19:175:26 | ... .some(...) [x] | capture.swift:175:23:175:26 | self [x] | provenance | | | capture.swift:175:23:175:26 | self [x] | capture.swift:175:19:175:26 | ... .some(...) [x] | provenance | | | capture.swift:175:23:175:26 | self [x] | capture.swift:176:18:176:21 | self [x] | provenance | | | capture.swift:175:23:175:26 | self [x] | capture.swift:177:18:177:18 | x | provenance | | | capture.swift:176:18:176:21 | self [x] | capture.swift:176:18:176:23 | ... .x | provenance | | -| capture.swift:179:9:179:15 | closure [self, x] | capture.swift:175:23:175:26 | self [x] | provenance | | | capture.swift:183:9:183:9 | x | capture.swift:184:34:184:37 | self [x] | provenance | | | capture.swift:183:13:183:25 | source(...) | capture.swift:183:9:183:9 | x | provenance | | | capture.swift:184:34:184:37 | self [x] | capture.swift:184:34:184:37 | self [x] | provenance | | -| capture.swift:184:34:184:37 | self [x] | capture.swift:188:9:188:15 | closure [self, x] | provenance | | +| capture.swift:184:34:184:37 | self [x] | capture.swift:185:18:185:21 | self [x] | provenance | | +| capture.swift:184:34:184:37 | self [x] | capture.swift:186:18:186:18 | x | provenance | | | capture.swift:185:18:185:21 | self [x] | capture.swift:185:18:185:23 | ... .x | provenance | | -| capture.swift:188:9:188:15 | closure [self, x] | capture.swift:185:18:185:21 | self [x] | provenance | | -| capture.swift:188:9:188:15 | closure [self, x] | capture.swift:186:18:186:18 | x | provenance | | | enums.swift:12:9:12:9 | e [case1.0] | enums.swift:14:12:14:12 | e [case1.0] | provenance | | | enums.swift:12:13:12:35 | ... .case1(...) [case1.0] | enums.swift:12:9:12:9 | e [case1.0] | provenance | | | enums.swift:12:21:12:34 | source(...) | enums.swift:12:13:12:35 | ... .case1(...) [case1.0] | provenance | | @@ -459,6 +522,112 @@ edges | test.swift:271:20:271:35 | source(...) | test.swift:271:9:271:9 | x | provenance | | | test.swift:273:30:273:45 | source(...) | test.swift:274:10:274:12 | f(...) | provenance | | nodes +| accessors_instance.swift:8:25:8:38 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:10:10:10:14 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:18:19:18:32 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:21:10:21:14 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:27:19:27:32 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:31:10:31:14 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:38:13:38:15 | newValue | semmle.label | newValue | +| accessors_instance.swift:38:24:38:31 | newValue | semmle.label | newValue | +| accessors_instance.swift:41:5:41:9 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:41:13:41:26 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:48:17:48:17 | v | semmle.label | v | +| accessors_instance.swift:48:27:48:27 | v | semmle.label | v | +| accessors_instance.swift:51:5:51:9 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:51:13:51:27 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:57:19:57:32 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:58:13:58:15 | newValue | semmle.label | newValue | +| accessors_instance.swift:58:24:58:31 | newValue | semmle.label | newValue | +| accessors_instance.swift:62:5:62:7 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:62:11:62:13 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:67:25:67:38 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:70:18:70:23 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:71:18:71:18 | x | semmle.label | x | +| accessors_instance.swift:79:25:79:28 | self [field] | semmle.label | self [field] | +| accessors_instance.swift:79:25:79:34 | ... .field | semmle.label | ... .field | +| accessors_instance.swift:83:5:83:5 | [post] c [field] | semmle.label | [post] c [field] | +| accessors_instance.swift:83:5:83:11 | ... .field | semmle.label | ... .field | +| accessors_instance.swift:83:15:83:28 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:84:10:84:10 | c [field] | semmle.label | c [field] | +| accessors_instance.swift:84:10:84:12 | ... .x | semmle.label | ... .x | +| accessors_instance.swift:93:19:93:28 | otherField | semmle.label | otherField | +| accessors_instance.swift:93:32:93:36 | field | semmle.label | field | +| accessors_instance.swift:97:5:97:5 | [post] c [field] | semmle.label | [post] c [field] | +| accessors_instance.swift:97:5:97:11 | ... .field | semmle.label | ... .field | +| accessors_instance.swift:97:15:97:28 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:98:5:98:5 | c [field] | semmle.label | c [field] | +| accessors_instance.swift:99:10:99:10 | c [otherField] | semmle.label | c [otherField] | +| accessors_instance.swift:99:10:99:21 | ... .otherField | semmle.label | ... .otherField | +| accessors_instance.swift:106:13:106:17 | field | semmle.label | field | +| accessors_instance.swift:106:21:106:34 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:112:10:112:10 | c [field] | semmle.label | c [field] | +| accessors_instance.swift:112:10:112:16 | ... .field | semmle.label | ... .field | +| accessors_instance.swift:119:13:119:17 | field | semmle.label | field | +| accessors_instance.swift:119:21:119:35 | source(...) | semmle.label | source(...) | +| accessors_instance.swift:125:18:125:22 | field | semmle.label | field | +| accessors_instance.swift:134:13:134:15 | newValue [0] | semmle.label | newValue [0] | +| 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_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(...) | +| accessors_local.swift:17:10:17:10 | x | semmle.label | x | +| accessors_local.swift:22:15:22:28 | source(...) | semmle.label | source(...) | +| accessors_local.swift:25:10:25:10 | x | semmle.label | x | +| accessors_local.swift:31:9:31:11 | newValue | semmle.label | newValue | +| accessors_local.swift:31:20:31:27 | newValue | semmle.label | newValue | +| accessors_local.swift:33:5:33:5 | x | semmle.label | x | +| accessors_local.swift:33:9:33:22 | source(...) | semmle.label | source(...) | +| accessors_local.swift:38:15:38:28 | source(...) | semmle.label | source(...) | +| accessors_local.swift:39:9:39:11 | newValue | semmle.label | newValue | +| accessors_local.swift:39:20:39:27 | newValue | semmle.label | newValue | +| accessors_local.swift:41:5:41:5 | x | semmle.label | x | +| accessors_local.swift:41:9:41:9 | x | semmle.label | x | +| accessors_local.swift:45:9:45:9 | y | semmle.label | y | +| accessors_local.swift:45:21:45:34 | source(...) | semmle.label | source(...) | +| accessors_local.swift:46:21:46:21 | y | semmle.label | y | +| accessors_local.swift:47:10:47:10 | x | semmle.label | x | +| accessors_local.swift:52:21:52:21 | y | semmle.label | y | +| accessors_local.swift:54:5:54:5 | y | semmle.label | y | +| accessors_local.swift:54:9:54:22 | source(...) | semmle.label | source(...) | +| accessors_local.swift:55:10:55:10 | x | semmle.label | x | +| accessors_local.swift:59:21:59:34 | source(...) | semmle.label | source(...) | +| accessors_local.swift:61:14:61:14 | x | semmle.label | x | +| accessors_local.swift:70:14:70:14 | x | semmle.label | x | +| accessors_local.swift:72:5:72:5 | y | semmle.label | y | +| accessors_local.swift:72:9:72:22 | source(...) | semmle.label | source(...) | +| accessors_local.swift:82:14:82:15 | x3 | semmle.label | x3 | +| accessors_local.swift:84:5:84:5 | y | semmle.label | y | +| accessors_local.swift:84:9:84:22 | source(...) | semmle.label | source(...) | +| accessors_local.swift:91:9:91:11 | newValue [0] | semmle.label | newValue [0] | +| accessors_local.swift:91:20:91:27 | newValue [0] | semmle.label | newValue [0] | +| accessors_local.swift:91:20:91:29 | ... .0 | semmle.label | ... .0 | +| 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_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(...) | +| accessors_static.swift:21:10:21:12 | ... .x | semmle.label | ... .x | +| accessors_static.swift:27:19:27:32 | source(...) | semmle.label | source(...) | +| accessors_static.swift:31:10:31:12 | ... .x | semmle.label | ... .x | +| accessors_static.swift:38:13:38:15 | newValue | semmle.label | newValue | +| accessors_static.swift:38:24:38:31 | newValue | semmle.label | newValue | +| accessors_static.swift:41:5:41:7 | ... .x | semmle.label | ... .x | +| accessors_static.swift:41:11:41:24 | source(...) | semmle.label | source(...) | +| accessors_static.swift:47:19:47:32 | source(...) | semmle.label | source(...) | +| accessors_static.swift:48:13:48:15 | newValue | semmle.label | newValue | +| accessors_static.swift:48:24:48:31 | newValue | semmle.label | newValue | +| accessors_static.swift:51:5:51:7 | ... .x | semmle.label | ... .x | +| accessors_static.swift:51:11:51:13 | ... .x | semmle.label | ... .x | +| accessors_static.swift:56:32:56:45 | source(...) | semmle.label | source(...) | +| accessors_static.swift:59:18:59:23 | ... .x | semmle.label | ... .x | +| accessors_static.swift:63:18:63:23 | ... .x | semmle.label | ... .x | +| accessors_static.swift:64:18:64:18 | x | semmle.label | x | | calls.swift:7:19:7:19 | x | semmle.label | x | | calls.swift:8:14:8:14 | x | semmle.label | x | | calls.swift:10:12:10:25 | source(...) | semmle.label | source(...) | @@ -510,7 +679,6 @@ nodes | capture.swift:10:9:10:9 | x | semmle.label | x | | capture.swift:10:13:10:26 | source(...) | semmle.label | source(...) | | capture.swift:12:14:12:14 | x | semmle.label | x | -| capture.swift:14:5:14:11 | closure [x] | semmle.label | closure [x] | | capture.swift:20:9:20:9 | x | semmle.label | x | | capture.swift:20:13:20:26 | source(...) | semmle.label | source(...) | | capture.swift:24:10:24:10 | x | semmle.label | x | @@ -520,7 +688,6 @@ nodes | capture.swift:32:14:32:14 | x | semmle.label | x | | capture.swift:33:9:33:10 | y2 | semmle.label | y2 | | capture.swift:33:14:33:14 | x | semmle.label | x | -| capture.swift:37:5:37:11 | closure [x] | semmle.label | closure [x] | | capture.swift:38:10:38:11 | y1 | semmle.label | y1 | | capture.swift:39:10:39:11 | y2 | semmle.label | y2 | | capture.swift:43:9:43:9 | x | semmle.label | x | @@ -542,18 +709,15 @@ nodes | capture.swift:67:14:67:14 | x | semmle.label | x | | capture.swift:69:5:69:5 | x | semmle.label | x | | capture.swift:69:9:69:22 | source(...) | semmle.label | source(...) | -| capture.swift:70:5:70:11 | closure [x] | semmle.label | closure [x] | | capture.swift:74:9:74:9 | x | semmle.label | x | | capture.swift:74:13:74:26 | source(...) | semmle.label | source(...) | | capture.swift:76:14:76:14 | x | semmle.label | x | -| capture.swift:78:5:78:9 | local [x] | semmle.label | local [x] | | capture.swift:84:9:84:9 | x | semmle.label | x | | capture.swift:84:13:84:26 | source(...) | semmle.label | source(...) | | capture.swift:88:10:88:10 | x | semmle.label | x | | capture.swift:93:9:93:9 | x | semmle.label | x | | capture.swift:93:13:93:26 | source(...) | semmle.label | source(...) | | capture.swift:94:9:94:9 | y | semmle.label | y | -| capture.swift:94:13:94:17 | local [x] | semmle.label | local [x] | | capture.swift:94:13:94:19 | local(...) | semmle.label | local(...) | | capture.swift:95:10:95:10 | y | semmle.label | y | | capture.swift:98:14:98:14 | x | semmle.label | x | @@ -566,8 +730,6 @@ nodes | capture.swift:108:23:108:23 | x | semmle.label | x | | capture.swift:108:23:108:23 | x | semmle.label | x | | capture.swift:109:14:109:14 | x | semmle.label | x | -| capture.swift:111:5:111:12 | closure1 [x1] | semmle.label | closure1 [x1] | -| capture.swift:112:5:112:12 | closure2 [x] | semmle.label | closure2 [x] | | capture.swift:116:9:116:9 | x | semmle.label | x | | capture.swift:116:13:116:27 | source(...) | semmle.label | source(...) | | capture.swift:117:22:117:22 | x | semmle.label | x | @@ -576,17 +738,13 @@ nodes | capture.swift:117:36:117:36 | x | semmle.label | x | | capture.swift:118:14:118:14 | x | semmle.label | x | | capture.swift:119:14:119:17 | blah | semmle.label | blah | -| capture.swift:121:5:121:11 | closure [blah] | semmle.label | closure [blah] | -| capture.swift:121:5:121:11 | closure [x] | semmle.label | closure [x] | | capture.swift:143:14:143:14 | x | semmle.label | x | | capture.swift:147:5:147:5 | x | semmle.label | x | | capture.swift:147:9:147:23 | source(...) | semmle.label | source(...) | -| capture.swift:149:5:149:12 | closure2 [x] | semmle.label | closure2 [x] | | capture.swift:156:9:156:9 | x | semmle.label | x | | capture.swift:156:13:156:25 | source(...) | semmle.label | source(...) | | capture.swift:158:18:158:21 | self [x] | semmle.label | self [x] | | capture.swift:158:18:158:23 | ... .x | semmle.label | ... .x | -| capture.swift:160:9:160:15 | closure [self, x] | semmle.label | closure [self, x] | | capture.swift:164:9:164:9 | x | semmle.label | x | | capture.swift:164:13:164:25 | source(...) | semmle.label | source(...) | | capture.swift:165:26:165:29 | self [x] | semmle.label | self [x] | @@ -594,7 +752,6 @@ nodes | capture.swift:166:18:166:21 | self [x] | semmle.label | self [x] | | capture.swift:166:18:166:23 | ... .x | semmle.label | ... .x | | capture.swift:167:18:167:18 | x | semmle.label | x | -| capture.swift:169:9:169:15 | closure [self, x] | semmle.label | closure [self, x] | | capture.swift:173:9:173:9 | x | semmle.label | x | | capture.swift:173:13:173:25 | source(...) | semmle.label | source(...) | | capture.swift:174:31:174:34 | self [x] | semmle.label | self [x] | @@ -605,7 +762,6 @@ nodes | capture.swift:176:18:176:21 | self [x] | semmle.label | self [x] | | capture.swift:176:18:176:23 | ... .x | semmle.label | ... .x | | capture.swift:177:18:177:18 | x | semmle.label | x | -| capture.swift:179:9:179:15 | closure [self, x] | semmle.label | closure [self, x] | | capture.swift:183:9:183:9 | x | semmle.label | x | | capture.swift:183:13:183:25 | source(...) | semmle.label | source(...) | | capture.swift:184:34:184:37 | self [x] | semmle.label | self [x] | @@ -613,7 +769,6 @@ nodes | capture.swift:185:18:185:21 | self [x] | semmle.label | self [x] | | capture.swift:185:18:185:23 | ... .x | semmle.label | ... .x | | capture.swift:186:18:186:18 | x | semmle.label | x | -| capture.swift:188:9:188:15 | closure [self, x] | semmle.label | closure [self, x] | | enums.swift:12:9:12:9 | e [case1.0] | semmle.label | e [case1.0] | | enums.swift:12:13:12:35 | ... .case1(...) [case1.0] | semmle.label | ... .case1(...) [case1.0] | | enums.swift:12:21:12:34 | source(...) | semmle.label | source(...) | @@ -1018,13 +1173,17 @@ nodes | test.swift:273:30:273:45 | source(...) | semmle.label | source(...) | | test.swift:274:10:274:12 | f(...) | semmle.label | f(...) | subpaths +| accessors_instance.swift:84:10:84:10 | c [field] | accessors_instance.swift:79:25:79:28 | self [field] | accessors_instance.swift:79:25:79:34 | ... .field | accessors_instance.swift:84:10:84:12 | ... .x | +| accessors_instance.swift:98:5:98:5 | c [field] | accessors_instance.swift:93:32:93:36 | field | accessors_instance.swift:93:19:93:28 | otherField | accessors_instance.swift:99:10:99:10 | c [otherField] | +| accessors_local.swift:45:9:45:9 | y | accessors_local.swift:46:21:46:21 | y | accessors_local.swift:46:21:46:21 | y | accessors_local.swift:47:10:47:10 | x | +| accessors_local.swift:54:5:54:5 | y | accessors_local.swift:52:21:52:21 | y | accessors_local.swift:52:21:52:21 | y | accessors_local.swift:55:10:55:10 | x | | calls.swift:31:17:31:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:31:10:31:31 | target(...) | | calls.swift:32:17:32:30 | source(...) | calls.swift:28:19:28:19 | x | calls.swift:29:16:29:24 | ... + ... | calls.swift:32:10:32:31 | target(...) | -| capture.swift:37:5:37:11 | closure [x] | capture.swift:32:14:32:14 | x | capture.swift:32:9:32:10 | y1 | capture.swift:38:10:38:11 | y1 | -| capture.swift:37:5:37:11 | closure [x] | capture.swift:33:14:33:14 | x | capture.swift:33:9:33:10 | y2 | capture.swift:39:10:39:11 | y2 | +| capture.swift:28:9:28:9 | x | capture.swift:32:14:32:14 | x | capture.swift:32:9:32:10 | y1 | capture.swift:38:10:38:11 | y1 | +| capture.swift:28:9:28:9 | x | capture.swift:33:14:33:14 | x | capture.swift:33:9:33:10 | y2 | capture.swift:39:10:39:11 | y2 | | capture.swift:49:13:49:13 | x | capture.swift:45:22:45:24 | arg | capture.swift:46:9:46:9 | y | capture.swift:50:10:50:10 | y | | capture.swift:60:13:60:13 | x | capture.swift:56:22:56:24 | arg | capture.swift:57:9:57:9 | [post] y [0] | capture.swift:61:10:61:10 | y [0] | -| capture.swift:94:13:94:17 | local [x] | capture.swift:99:16:99:16 | x | capture.swift:99:16:99:16 | x | capture.swift:94:13:94:19 | local(...) | +| capture.swift:93:9:93:9 | x | capture.swift:99:16:99:16 | x | capture.swift:99:16:99:16 | x | capture.swift:94:13:94:19 | local(...) | | flow-summaries.swift:54:35:54:48 | source(...) | flow-summaries.swift:54:53:54:53 | y | flow-summaries.swift:54:65:54:65 | y | flow-summaries.swift:54:10:54:67 | ... .throughCallback(...) | | flow-summaries.swift:55:35:55:48 | source(...) | flow-summaries.swift:55:53:55:53 | y | flow-summaries.swift:55:65:55:71 | ... + ... | flow-summaries.swift:55:10:55:73 | ... .throughCallback(...) | | test.swift:196:30:196:44 | source(...) | test.swift:189:22:189:22 | s | test.swift:189:58:189:58 | s | test.swift:196:16:196:45 | asyncIdentity(...) | @@ -1036,6 +1195,38 @@ subpaths | test.swift:214:43:214:57 | source(...) | test.swift:193:30:193:30 | s | test.swift:193:73:193:73 | s | test.swift:214:21:214:58 | asyncThrowingIdentity(...) | testFailures #select +| accessors_instance.swift:10:10:10:14 | ... .x | accessors_instance.swift:8:25:8:38 | source(...) | accessors_instance.swift:10:10:10:14 | ... .x | $@ | accessors_instance.swift:8:25:8:38 | source(...) | source(...) | +| accessors_instance.swift:21:10:21:14 | ... .x | accessors_instance.swift:18:19:18:32 | source(...) | accessors_instance.swift:21:10:21:14 | ... .x | $@ | accessors_instance.swift:18:19:18:32 | source(...) | source(...) | +| accessors_instance.swift:31:10:31:14 | ... .x | accessors_instance.swift:27:19:27:32 | source(...) | accessors_instance.swift:31:10:31:14 | ... .x | $@ | accessors_instance.swift:27:19:27:32 | source(...) | source(...) | +| accessors_instance.swift:38:24:38:31 | newValue | accessors_instance.swift:41:13:41:26 | source(...) | accessors_instance.swift:38:24:38:31 | newValue | $@ | accessors_instance.swift:41:13:41:26 | source(...) | source(...) | +| accessors_instance.swift:48:27:48:27 | v | accessors_instance.swift:51:13:51:27 | source(...) | accessors_instance.swift:48:27:48:27 | v | $@ | accessors_instance.swift:51:13:51:27 | source(...) | source(...) | +| accessors_instance.swift:58:24:58:31 | newValue | accessors_instance.swift:57:19:57:32 | source(...) | accessors_instance.swift:58:24:58:31 | newValue | $@ | accessors_instance.swift:57:19:57:32 | source(...) | source(...) | +| accessors_instance.swift:70:18:70:23 | ... .x | accessors_instance.swift:67:25:67:38 | source(...) | accessors_instance.swift:70:18:70:23 | ... .x | $@ | accessors_instance.swift:67:25:67:38 | source(...) | source(...) | +| accessors_instance.swift:71:18:71:18 | x | accessors_instance.swift:67:25:67:38 | source(...) | accessors_instance.swift:71:18:71:18 | x | $@ | accessors_instance.swift:67:25:67:38 | source(...) | source(...) | +| accessors_instance.swift:84:10:84:12 | ... .x | accessors_instance.swift:83:15:83:28 | source(...) | accessors_instance.swift:84:10:84:12 | ... .x | $@ | accessors_instance.swift:83:15:83:28 | source(...) | source(...) | +| 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_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(...) | +| accessors_local.swift:31:20:31:27 | newValue | accessors_local.swift:33:9:33:22 | source(...) | accessors_local.swift:31:20:31:27 | newValue | $@ | accessors_local.swift:33:9:33:22 | source(...) | source(...) | +| accessors_local.swift:39:20:39:27 | newValue | accessors_local.swift:38:15:38:28 | source(...) | accessors_local.swift:39:20:39:27 | newValue | $@ | accessors_local.swift:38:15:38:28 | source(...) | source(...) | +| accessors_local.swift:47:10:47:10 | x | accessors_local.swift:45:21:45:34 | source(...) | accessors_local.swift:47:10:47:10 | x | $@ | accessors_local.swift:45:21:45:34 | source(...) | source(...) | +| accessors_local.swift:55:10:55:10 | x | accessors_local.swift:54:9:54:22 | source(...) | accessors_local.swift:55:10:55:10 | x | $@ | accessors_local.swift:54:9:54:22 | source(...) | source(...) | +| accessors_local.swift:61:14:61:14 | x | accessors_local.swift:59:21:59:34 | source(...) | accessors_local.swift:61:14:61:14 | x | $@ | accessors_local.swift:59:21:59:34 | source(...) | source(...) | +| 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_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(...) | +| accessors_static.swift:38:24:38:31 | newValue | accessors_static.swift:41:11:41:24 | source(...) | accessors_static.swift:38:24:38:31 | newValue | $@ | accessors_static.swift:41:11:41:24 | source(...) | source(...) | +| accessors_static.swift:48:24:48:31 | newValue | accessors_static.swift:47:19:47:32 | source(...) | accessors_static.swift:48:24:48:31 | newValue | $@ | accessors_static.swift:47:19:47:32 | source(...) | source(...) | +| accessors_static.swift:59:18:59:23 | ... .x | accessors_static.swift:56:32:56:45 | source(...) | accessors_static.swift:59:18:59:23 | ... .x | $@ | accessors_static.swift:56:32:56:45 | source(...) | source(...) | +| accessors_static.swift:63:18:63:23 | ... .x | accessors_static.swift:56:32:56:45 | source(...) | accessors_static.swift:63:18:63:23 | ... .x | $@ | accessors_static.swift:56:32:56:45 | source(...) | source(...) | +| accessors_static.swift:64:18:64:18 | x | accessors_static.swift:56:32:56:45 | source(...) | accessors_static.swift:64:18:64:18 | x | $@ | accessors_static.swift:56:32:56:45 | source(...) | source(...) | | calls.swift:8:14:8:14 | x | calls.swift:10:12:10:25 | source(...) | calls.swift:8:14:8:14 | x | $@ | calls.swift:10:12:10:25 | source(...) | source(...) | | calls.swift:17:10:17:17 | target(...) | calls.swift:15:16:15:29 | source(...) | calls.swift:17:10:17:17 | target(...) | $@ | calls.swift:15:16:15:29 | source(...) | source(...) | | calls.swift:22:14:22:14 | x | calls.swift:24:15:24:28 | source(...) | calls.swift:22:14:22:14 | x | $@ | calls.swift:24:15:24:28 | source(...) | source(...) |