Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions swift/actions/run-ql-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ runs:
--check-repeated-labels \
--check-redefined-labels \
--check-use-before-definition \
--consistency-queries "${{ github.workspace }}/swift/ql/consistency-queries" \
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
${{ inputs.flags }} \
swift/ql/test
Expand Down
9 changes: 5 additions & 4 deletions swift/ql/.generated.list

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion swift/ql/.gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions swift/ql/consistency-queries/PrintAstConsistency.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import codeql.swift.printast.Consistency
4 changes: 4 additions & 0 deletions swift/ql/consistency-queries/qlpack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: codeql/swift-consistency-queries
groups: [swift, test, consistency-queries]
dependencies:
codeql/swift-all: ${workspace}
Original file line number Diff line number Diff line change
Expand Up @@ -111,31 +111,21 @@ module Stmts {

override predicate propagatesAbnormal(ControlFlowElement node) { none() }

private predicate isBodyOfTapExpr() { any(TapExpr tap).getBody() = ast }

// Note: If the brace statement is the body of a `TapExpr`, the first element is the variable
// declaration (see https://github.com/apple/swift/blob/main/include/swift/AST/Expr.h#L848)
// that's initialized by the `TapExpr`. In `TapExprTree` we've already visited this declaration,
// along with its initializer. So we skip the first element here.
private AstNode getFirstElement() {
if this.isBodyOfTapExpr() then result = ast.getElement(1) else result = ast.getFirstElement()
}

override predicate first(ControlFlowElement first) {
this.firstInner(first)
or
not exists(this.getFirstElement()) and first.asAstNode() = ast
not exists(ast.getFirstElement()) and first.asAstNode() = ast
}

override predicate last(ControlFlowElement last, Completion c) {
this.lastInner(last, c)
or
not exists(this.getFirstElement()) and
not exists(ast.getFirstElement()) and
last.asAstNode() = ast and
c instanceof SimpleCompletion
}

predicate firstInner(ControlFlowElement first) { astFirst(this.getFirstElement(), first) }
predicate firstInner(ControlFlowElement first) { astFirst(ast.getFirstElement(), first) }

/** Gets the body of the i'th `defer` statement. */
private BraceStmt getDeferStmtBody(int i) {
Expand Down Expand Up @@ -969,6 +959,9 @@ module Decls {
result.asAstNode() = ast.getPattern(j).getFullyUnresolved()
)
or
// PropertyWrapperBackingVarBinding is a special case as it shares the init
// with the original variable binding (see also PrintAstNode.qll)
ast != any(VarDecl d).getPropertyWrapperBackingVarBinding() and
Copy link
Contributor

Choose a reason for hiding this comment

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

As we discussed in our sync. See https://codeql.github.com/docs/ql-language-reference/formulas/#equality.

Suggested change
ast != any(VarDecl d).getPropertyWrapperBackingVarBinding() and
not ast = any(VarDecl d).getPropertyWrapperBackingVarBinding() and

exists(int j |
i = 2 * j + 1 and
result.asAstNode() = ast.getInit(j).getFullyConverted()
Expand Down Expand Up @@ -1397,20 +1390,12 @@ module Exprs {
override TapExpr ast;

final override ControlFlowElement getChildElement(int i) {
// We first visit the local variable declaration.
// First we visit the expression that gives the local variable (the first element in the body) its initial value.
i = 0 and
result.asAstNode() = ast.getVar()
or
// Then we visit the expression that gives the local variable its initial value.
i = 1 and
result.asAstNode() = ast.getSubExpr().getFullyConverted()
or
// And finally, we visit the body that potentially mutates the local variable.
// Note that the CFG for the body will skip the first element in the
// body because it's guaranteed to be the variable declaration
// that we've already visited at i = 0. See the explanation
// in `BraceStmtTree` for why this is necessary.
i = 2 and
// Then, we visit the body that potentially mutates the local variable.
i = 1 and
result.asAstNode() = ast.getBody()
}
}
Expand Down
7 changes: 1 addition & 6 deletions swift/ql/lib/codeql/swift/generated/ParentChild.qll

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions swift/ql/lib/codeql/swift/generated/Raw.qll

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions swift/ql/lib/codeql/swift/printast/Consistency.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
private import codeql.swift.printast.PrintAstNode

query predicate doubleChildren(
PrintAstNode parent, int index, string label1, PrintAstNode child1, string label2,
PrintAstNode child2
) {
child1 != child2 and
parent.hasChild(child1, index, label1) and
parent.hasChild(child2, index, label2)
}

query predicate doubleIndexes(
PrintAstNode parent, int index1, string label1, int index2, string label2, PrintAstNode child
) {
index1 != index2 and
parent.hasChild(child, index1, label1) and
parent.hasChild(child, index2, label2)
}

query predicate doubleParents(
PrintAstNode parent1, string label1, PrintAstNode parent2, string label2, PrintAstNode child
) {
parent1 != parent2 and
parent1.hasChild(child, _, label1) and
parent2.hasChild(child, _, label2)
}

private predicate isChildOf(PrintAstNode parent, PrintAstNode child) {
parent.hasChild(child, _, _)
}

query predicate parentChildLoops(PrintAstNode parent, PrintAstNode child) {
isChildOf(parent, child) and isChildOf*(child, parent)
}
17 changes: 17 additions & 0 deletions swift/ql/lib/codeql/swift/printast/PrintAstNode.qll
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,20 @@ class PrintFunction extends PrintLocatable {
key = "InterfaceType" and result = ast.getInterfaceType().toString()
}
}

/**
* A specialization for `PatternBindingDecl`s that are backing a property wrapper, required as the
* `getInit` in these cases points to the same `Expr` that the `PatternBindingDecl` of the original
* variable. We therefore need to skip it to avoid double parents.
*/
class PrintPropertyWrapperPatternBindingDecl extends PrintLocatable {
override PatternBindingDecl ast;

PrintPropertyWrapperPatternBindingDecl() {
ast = any(VarDecl d).getPropertyWrapperBackingVarBinding()
}

override predicate hasChild(PrintAstNode child, int index, string label) {
PrintLocatable.super.hasChild(child, index, label) and label != "getInit(0)"
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| open_existential.swift:14:5:14:19 | OpenExistentialExpr | hasType: | yes | getSubExpr: | open_existential.swift:14:5:14:19 | call to foo() | getExistential: | open_existential.swift:14:5:14:13 | call to createP() | getOpaqueExpr: | open_existential.swift:14:5:14:13 | OpaqueValueExpr |

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| open_existential.swift:14:5:14:19 | OpenExistentialExpr | () |

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
protocol P {
func foo() -> ()
}

class C : P {
func foo() {}
}

func createP() -> P {
return C()
}

func test() {
createP().foo()
}
Empty file.
8 changes: 0 additions & 8 deletions swift/ql/test/library-tests/ast/no_double_children.ql

This file was deleted.

Empty file.
8 changes: 0 additions & 8 deletions swift/ql/test/library-tests/ast/no_double_indexes.ql

This file was deleted.

Empty file.
8 changes: 0 additions & 8 deletions swift/ql/test/library-tests/ast/no_double_parents.ql

This file was deleted.

Empty file.
7 changes: 0 additions & 7 deletions swift/ql/test/library-tests/ast/no_parent_child_loops.ql

This file was deleted.

Loading