Skip to content

Commit 036ed04

Browse files
committed
unified: Support OrPattern bindings
1 parent 891e244 commit 036ed04

2 files changed

Lines changed: 47 additions & 5 deletions

File tree

unified/ql/lib/codeql/unified/internal/Variables.qll

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,39 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig<Locatio
213213
)
214214
}
215215

216-
predicate declInScope(AstNode definingNode, string name, AstNode scope) {
217-
bindingContext(definingNode, scope) and
218-
(
219-
definingNode.(NamePattern).getIdentifier().getValue() = name
216+
/**
217+
* Gets the nearest enclosing `OrPattern` to which variable bindings in `p` should be lifted.
218+
*
219+
* To ensure that `case .foo(let x), .bar(let x)` result in a single definition for
220+
* the variable `x`, the `OrPattern` becomes the `definingNode` for `x`.
221+
*
222+
* At the moment no further checks are needed since the Swift compiler enforces that
223+
* variable names bound in any branch are bound in all branches.
224+
*/
225+
private OrPattern getEnclosingOrPattern(Pattern p) {
226+
p = result.getPattern(_)
227+
or
228+
exists(Pattern parent | result = getEnclosingOrPattern(parent) |
229+
p = parent.(ConstructorPattern).getElement(_).getPattern()
220230
or
221-
definingNode.(Identifier).getValue() = name
231+
p = parent.(TuplePattern).getElement(_).getPattern()
232+
)
233+
}
234+
235+
predicate declInScope(AstNode definingNode, string name, AstNode scope) {
236+
exists(AstNode pattern |
237+
bindingContext(pattern, scope) and
238+
(
239+
pattern.(NamePattern).getIdentifier().getValue() = name
240+
or
241+
pattern.(Identifier).getValue() = name
242+
) and
243+
(
244+
definingNode = getEnclosingOrPattern(pattern)
245+
or
246+
not exists(getEnclosingOrPattern(pattern)) and
247+
definingNode = pattern
248+
)
222249
)
223250
}
224251

unified/ql/test/library-tests/variables/test.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,18 @@ func t37() {
334334
print(x) // $ access=x3
335335
}
336336
}
337+
338+
enum E38 {
339+
case a(Int)
340+
case b(Int)
341+
}
342+
343+
// Switch with a multi-pattern case that binds 'x' in each pattern
344+
// Note: the testing framework does not make it possible to name the 'x' variable in this case.
345+
func t38(value: E38) {
346+
switch value { // $ access=value
347+
case .a(let x), // $ access=x
348+
.b(let x): // $ access=x
349+
print(x) // $ access=x
350+
}
351+
}

0 commit comments

Comments
 (0)