From a21384f9a4a5761c76c5a47838a8cf9e60adadd1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 13:54:38 +0100 Subject: [PATCH 1/8] unified: Auto format local name binding test --- .../local-name-binding/test.swift | 372 +++++++++--------- 1 file changed, 196 insertions(+), 176 deletions(-) diff --git a/unified/ql/test/library-tests/local-name-binding/test.swift b/unified/ql/test/library-tests/local-name-binding/test.swift index 6de3cab47e3a..029fdfae0603 100644 --- a/unified/ql/test/library-tests/local-name-binding/test.swift +++ b/unified/ql/test/library-tests/local-name-binding/test.swift @@ -1,339 +1,359 @@ func t1() -> Int { - let x = 42 // name=x1 - return x // $ access=x1 + let x = 42 // name=x1 + return x // $ access=x1 } func t2() -> Int { - let x = 42 // name=x1 + let x = 42 // name=x1 - if case let x = x + 10 { // $ access=x1 // name=x2 - print(x) // $ access=x2 + if case let x = x + 10 { // $ access=x1 // name=x2 + print(x) // $ access=x2 } - print(x) // $ access=x1 + print(x) // $ access=x1 } func t3() -> Int { - guard let x = foo() else { // name=x1 + guard let x = foo() else { // name=x1 return 0 } - print(x) // $ access=x1 + print(x) // $ access=x1 } // Function parameters -func t4(x: Int) -> Int { // name=x1 - return x // $ access=x1 +func t4(x: Int) -> Int { // name=x1 + return x // $ access=x1 } // Multiple parameters -func t5(x: Int, // name=x1 - y: Int) -> Int { // name=y1 - let z = x + // $ access=x1 // name=z1 - y // $ access=y1 - return z // $ access=z1 +func t5( + x: Int, // name=x1 + y: Int // name=y1 +) -> Int { + let z = // name=z1 + x // $ access=x1 + + y // $ access=y1 + return z // $ access=z1 } // Parameter shadowed by local variable -func t6(x: Int) -> Int { // name=x1 - let x = x * 2 // $ access=x1 // name=x2 - return x // $ access=x2 +func t6(x: Int) -> Int { // name=x1 + let x = x * 2 // $ access=x1 // name=x2 + return x // $ access=x2 } // Nested blocks func t7() { - let x = 1; // name=x1 + let x = 1 // name=x1 do { - print(x) // $ access=x1 - let x = 2 // name=x2 - print(x) // $ access=x2 + print(x) // $ access=x1 + let x = 2 // name=x2 + print(x) // $ access=x2 } - print(x) // $ access=x1 + print(x) // $ access=x1 } // For-in loop func t8() { - let array = [1, 2, 3] // name=array1 - for x in array { // $ access=array1 // name=x1 - print(x) // $ access=x1 + let array = [1, 2, 3] // name=array1 + for x in array { // $ access=array1 // name=x1 + print(x) // $ access=x1 } } // For-in loop with shadowing func t9() { - let x = 0 // name=x1 - let array = [1, 2, 3] // name=array1 - for x in array { // $ access=array1 // name=x2 - print(x) // $ access=x2 + let x = 0 // name=x1 + let array = [1, 2, 3] // name=array1 + for x in array { // $ access=array1 // name=x2 + print(x) // $ access=x2 } - print(x) // $ access=x1 + print(x) // $ access=x1 } // Switch statement with case bindings -func t10(value: Int) { // name=value1 - switch value { // $ access=value1 - case let x: // name=x1 - print(x) // $ access=x1 +func t10(value: Int) { // name=value1 + switch value { // $ access=value1 + case let x: // name=x1 + print(x) // $ access=x1 default: break } } // Switch with multiple cases -func t11(value: Int) { // name=value1 - switch value { // $ access=value1 - case let x where x > 0: // $ access=x1 // name=x1 - print(x) // $ access=x1 - case let x: // name=x2 - print(x) // $ access=x2 +func t11(value: Int) { // name=value1 + switch value { // $ access=value1 + case let x where x > 0: // $ access=x1 // name=x1 + print(x) // $ access=x1 + case let x: // name=x2 + print(x) // $ access=x2 } } // Tuple unpacking func t12() { - let tuple = (1, 2) // name=tuple1 - let (x, // name=x1 - y) = tuple // $ access=tuple1 // name=y1 - print(x) // $ access=x1 - print(y) // $ access=y1 + let tuple = (1, 2) // name=tuple1 + let ( + x, // name=x1 + y // name=y1 + ) = tuple // $ access=tuple1 + print(x) // $ access=x1 + print(y) // $ access=y1 } // Tuple unpacking with underscore func t13() { - let tuple = (1, 2, 3) // name=tuple1 - let (x, // name=x1 - _, - y) = tuple // $ access=tuple1 // name=y1 - print(x) // $ access=x1 - print(y) // $ access=y1 + let tuple = (1, 2, 3) // name=tuple1 + let ( + x, // name=x1 + _, + y // name=y1 + ) = tuple // $ access=tuple1 + print(x) // $ access=x1 + print(y) // $ access=y1 } // Optional binding (if let) -func t14(optional: Int?) { // name=optional1 - if let x = optional { // $ access=optional1 // name=x1 - print(x) // $ access=x1 +func t14(optional: Int?) { // name=optional1 + if let x = optional { // $ access=optional1 // name=x1 + print(x) // $ access=x1 } } // Multiple optional bindings -func t15(opt1: Int?, // name=opt11 - opt2: String?) { // name=opt21 - if let x = opt1, // $ access=opt11 // name=x1 - let y = opt2 { // $ access=opt21 // name=y1 - print(x) // $ access=x1 - print(y) // $ access=y1 +func t15( + opt1: Int?, // name=opt11 + opt2: String? // name=opt21 +) { + if let x = opt1, // $ access=opt11 // name=x1 + let y = opt2 // $ access=opt21 // name=y1 + { + print(x) // $ access=x1 + print(y) // $ access=y1 } } // Do-catch blocks func t16() throws { do { - let x = try foo() // name=x1 - print(x) // $ access=x1 - } catch let error { // name=error1 - print(error) // $ access=error1 + let x = try foo() // name=x1 + print(x) // $ access=x1 + } catch let error { // name=error1 + print(error) // $ access=error1 } } // Closure captures func t17() { - let x = 1 // $ captured=x1 // name=x1 - let closure = { // name=closure1 - print(x) // $ access=x1 + let x = 1 // $ captured=x1 // name=x1 + let closure = { // name=closure1 + print(x) // $ access=x1 } - closure() // $ access=closure1 + closure() // $ access=closure1 } // Closure with parameter shadowing func t18() { - let x = 1 // name=x1 + let x = 1 // name=x1 let closure = - { (x: Int) -> Void in // name=x2 - print(x) // $ access=x2 + { (x: Int) -> Void in // name=x2 + print(x) // $ access=x2 } - closure(2) // $ access=closure - print(x) // $ access=x1 + closure(2) // $ access=closure + print(x) // $ access=x1 } // While loop func t19() { - var x = 0 // name=x1 - while x < 10 { // $ access=x1 - x = x + 1 // $ access=x1 - print(x) // $ access=x1 + var x = 0 // name=x1 + while x < 10 { // $ access=x1 + x = x + 1 // $ access=x1 + print(x) // $ access=x1 } } // Repeat-while loop func t20() { - var x = 0 // name=x1 + var x = 0 // name=x1 repeat { - x = x + 1 // $ access=x1 - print(x) // $ access=x1 - } while x < 10 // $ access=x1 + x = x + 1 // $ access=x1 + print(x) // $ access=x1 + } while x < 10 // $ access=x1 } // Property shadowing (var) func t21() { - var x = 1 // name=x1 - var x = 2 // name=x2 - print(x) // $ access=x2 + var x = 1 // name=x1 + var x = 2 // name=x2 + print(x) // $ access=x2 } // Nested functions func t22() { - let x = 1 // name=x1 - func inner() { // name=inner1 - let x = 2 // name=x2 - print(x) // $ access=x2 + let x = 1 // name=x1 + func inner() { // name=inner1 + let x = 2 // name=x2 + print(x) // $ access=x2 } - inner() // $ access=inner1 - print(x) // $ access=x1 - let inner = 2 // name=inner2 - print(inner) // $ access=inner2 + inner() // $ access=inner1 + print(x) // $ access=x1 + let inner = 2 // name=inner2 + print(inner) // $ access=inner2 } // Three levels of shadowing func t23() { - let x = 1 // name=x1 + let x = 1 // name=x1 { - let x = 2 // name=x2 + let x = 2 // name=x2 { - let x = 3 // name=x3 - print(x) // $ access=x3 + let x = 3 // name=x3 + print(x) // $ access=x3 } - print(x) // $ access=x2 + print(x) // $ access=x2 } - print(x) // $ access=x1 + print(x) // $ access=x1 } // If-let followed by regular if -func t24(optional: Int?) { // name=optional1 - if let x = optional { // $ access=optional1 // name=x1 - print(x) // $ access=x1 +func t24(optional: Int?) { // name=optional1 + if let x = optional { // $ access=optional1 // name=x1 + print(x) // $ access=x1 } if true { - let x = 5 // name=x2 - print(x) // $ access=x2 + let x = 5 // name=x2 + print(x) // $ access=x2 } } // Switch with variable shadowed within body of case -func t25(value: Int) { // name=value1 - switch value { // $ access=value1 - case let x: // name=x1 - print(x) // $ access=x1 - let x = 10 // name=x2 - print(x) // $ access=x2 +func t25(value: Int) { // name=value1 + switch value { // $ access=value1 + case let x: // name=x1 + print(x) // $ access=x1 + let x = 10 // name=x2 + print(x) // $ access=x2 } } func t26() -> Int { - let x = 42 // name=x1 - guard let x = foo() else { // name=x2 - return x // $ access=x1 + let x = 42 // name=x1 + guard let x = foo() else { // name=x2 + return x // $ access=x1 } - print(x) // $ access=x2 + print(x) // $ access=x2 } // if with multiple conditions, mixing boolean and optional binding -func t27(opt: Int?) { // name=opt1 - if opt != nil, // $ access=opt1 - let x = opt { // $ access=opt1 - print(x) // $ access=x +func t27(opt: Int?) { // name=opt1 + if opt != nil, // $ access=opt1 + let x = opt // $ access=opt1 + { + print(x) // $ access=x } } // if with multiple let bindings and a boolean condition func t28(a: Int?, b: Int?) { - if let x = a, // $ access=a - let y = b, // $ access=b - x < y { // $ access=x access=y - print(x) // $ access=x - print(y) // $ access=y + if let x = a, // $ access=a + let y = b, // $ access=b + x < y // $ access=x access=y + { + print(x) // $ access=x + print(y) // $ access=y } } // if with multiple conditions where a later binding shadows an outer variable -func t29(opt: Int?) { // name=opt1 - let x = 0 // name=x1 - if opt != nil, // $ access=opt1 - let x = opt { // $ access=opt1 - print(x) // $ access=x +func t29(opt: Int?) { // name=opt1 + let x = 0 // name=x1 + if opt != nil, // $ access=opt1 + let x = opt // $ access=opt1 + { + print(x) // $ access=x } - print(x) // $ access=x1 + print(x) // $ access=x1 } // guard with multiple conditions, mixing boolean and optional binding -func t30(opt: Int?) { // name=opt1 - guard opt != nil, // $ access=opt1 - let x = opt else { // $ access=opt1 +func t30(opt: Int?) { // name=opt1 + guard opt != nil, // $ access=opt1 + let x = opt // $ access=opt1 + else { return } - print(x) // $ access=x + print(x) // $ access=x } // guard with multiple let bindings and a boolean condition func t31(a: Int?, b: Int?) { - guard let x = a, // $ access=a - let y = b, // $ access=b - x < y else { // $ access=x access=y + guard let x = a, // $ access=a + let y = b, // $ access=b + x < y // $ access=x access=y + else { return } - print(x) // $ access=x - print(y) // $ access=y + print(x) // $ access=x + print(y) // $ access=y } // guard with multiple conditions where bound variable is used in later condition -func t32(opt: Int?) { // name=opt1 - guard let x = opt, // $ access=opt1 - x > 0 else { // $ access=x +func t32(opt: Int?) { // name=opt1 + guard let x = opt, // $ access=opt1 + x > 0 // $ access=x + else { return } - print(x) // $ access=x + print(x) // $ access=x } func t33() { - let x = 1 // name=x1 - guard x > 0, // $ access=x1 - let x = x, // $ access=x1 // name=x2 - x > 0 else { // $ access=x2 + let x = 1 // name=x1 + guard x > 0, // $ access=x1 + let x = x, // $ access=x1 // name=x2 + x > 0 // $ access=x2 + else { return } - print(x) // $ access=x2 + print(x) // $ access=x2 } func t34() { - let x = 1 // name=x1 - if x > 0, // $ access=x1 - let x = x, // $ access=x1 // name=x2 - x > 0 { // $ access=x2 - print(x) // $ access=x2 + let x = 1 // name=x1 + if x > 0, // $ access=x1 + let x = x, // $ access=x1 // name=x2 + x > 0 // $ access=x2 + { + print(x) // $ access=x2 } } // While-let optional binding -func t35(optional: Int?) { // name=optional1 - while let x = optional { // $ access=optional1 // name=x1 - print(x) // $ access=x1 +func t35(optional: Int?) { // name=optional1 + while let x = optional { // $ access=optional1 // name=x1 + print(x) // $ access=x1 } } // While with a sequence of variable bindings in the condition func t36(a: Int?, b: Int?) { - while let x = a, // $ access=a - let y = b, // $ access=b - x < y { // $ access=x access=y - print(x) // $ access=x - print(y) // $ access=y + while let x = a, // $ access=a + let y = b, // $ access=b + x < y // $ access=x access=y + { + print(x) // $ access=x + print(y) // $ access=y } } // While-let with a sequence of shadowing variable declarations func t37() { - let x = 1 // name=x1 - while let x = x, // $ access=x1 // name=x2 - let x = x, // $ access=x2 // name=x3 - x > 0 { // $ access=x3 - print(x) // $ access=x3 + let x = 1 // name=x1 + while let x = x, // $ access=x1 // name=x2 + let x = x, // $ access=x2 // name=x3 + x > 0 // $ access=x3 + { + print(x) // $ access=x3 } } @@ -343,22 +363,22 @@ enum E38 { } // Switch with a multi-pattern case that binds 'x' in each pattern -func t38(value: E38) { // $ access=E38 - switch value { // $ access=value - case .a(let x), // $ access=x1 // name=x1 - .b(let x): // $ access=x1 - print(x) // $ access=x1 - - case .a(let y) where y < 1, // $ access=y1 // name=y1 - .b(let y): // $ access=y1 - print(y) // $ access=y1 +func t38(value: E38) { // $ access=E38 + switch value { // $ access=value + case .a(let x), // $ access=x1 // name=x1 + .b(let x): // $ access=x1 + print(x) // $ access=x1 + + case .a(let y) where y < 1, // $ access=y1 // name=y1 + .b(let y): // $ access=y1 + print(y) // $ access=y1 } } // A non-binding pattern in a guard refers to an existing variable func t39(value: Int) { - let x = 1 // name=x1 - guard case x = value else { // $ access=value access=x1 + let x = 1 // name=x1 + guard case x = value else { // $ access=value access=x1 return } } From 8ef15ee3eb3b60abade40ca67cd156fb3baa9eb0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 13:55:49 +0100 Subject: [PATCH 2/8] unified: Add test where function and its parameter have the same name --- .../local-name-binding/test.swift | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/unified/ql/test/library-tests/local-name-binding/test.swift b/unified/ql/test/library-tests/local-name-binding/test.swift index 029fdfae0603..eb9be82c2a99 100644 --- a/unified/ql/test/library-tests/local-name-binding/test.swift +++ b/unified/ql/test/library-tests/local-name-binding/test.swift @@ -382,3 +382,26 @@ func t39(value: Int) { return } } + +func t40() { + // This function and its parameter have the same name + func foo( // $ SPURIOUS: access=foo2 // name=foo1 + _ foo: String // $ SPURIOUS: access=foo2 captured=foo2 // name=foo2 + ) { + foo // $ access=foo2 + } + foo() // $ MISSING: access=foo1 +} + +func t41() { + // This function and its parameter have the same name + // The parameter default value must resolve to the outer name (the function) + // swift-format-ignore + func foo( // $ SPURIOUS: access=foo2 // name=foo1 + _ foo: Any = // $ SPURIOUS: access=foo2 captured=foo2 // name=foo2 + foo // $ MISSING: access=foo1 SPURIOUS: access=foo2 + ) { + foo // $ access=foo2 + } + foo() // $ MISSING: access=foo1 +} From 962949c9816d55b07ac1709228b5a87e8a57630d Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 14:05:10 +0100 Subject: [PATCH 3/8] unified: Fix a TODO No semantic --- unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 7f88d1c496bf..f4580b485596 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -204,7 +204,7 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig Date: Fri, 30 Oct 2026 14:18:28 +0100 Subject: [PATCH 4/8] unified: Let bindingContext dictate resolution for name bindings --- .../codeql/namebinding/LocalNameBinding.qll | 10 ++++++---- .../codeql/unified/internal/LocalNameBinding.qll | 15 +++++++++++++-- .../library-tests/local-name-binding/test.swift | 12 ++++++------ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/shared/namebinding/codeql/namebinding/LocalNameBinding.qll b/shared/namebinding/codeql/namebinding/LocalNameBinding.qll index 2dd12bbc8abc..d1759f2366ec 100644 --- a/shared/namebinding/codeql/namebinding/LocalNameBinding.qll +++ b/shared/namebinding/codeql/namebinding/LocalNameBinding.qll @@ -449,6 +449,12 @@ module LocalNameBinding /** Gets a textual representation of this local entity. */ string toString() { result = this.getName() } + + /** Holds if this variable has the given name and scope. */ + pragma[nomagic] + predicate hasNameAndScope(string name, AstNode scope) { + this = TExplicitLocal(_, name, scope) or this = TImplicitLocal(name, scope) + } } final class Local = LocalImpl; @@ -484,10 +490,6 @@ module LocalNameBinding override string getName() { result = name } override Location getLocation() { result = scope.getLocation() } - - /** Holds if this variable has the given name and scope. */ - pragma[nomagic] - predicate hasNameAndScope(string name_, AstNode scope_) { name = name_ and scope = scope_ } } /** A local access. */ diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index f4580b485596..ea06e84b33b3 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -347,6 +347,17 @@ predicate bindingContext = LocalNameBindingInput::bindingContext/3; module LocalNameBindingOutput = LocalNameBinding; +private LocalNameBindingOutput::Local resolveId(Identifier id) { + // For names in binding position, always resolve to the scope set by the bindingContext predicate + exists(AstNode scope | + bindingContext(id, scope, _) and + result.hasNameAndScope(id.getValue(), scope) + ) + or + not bindingContext(id, _, _) and + result = id.(LocalNameBindingOutput::LocalAccess).getLocal() +} + module Public { /** * A representative for a lexically scoped entity, such as a local variable, type name, or module name. @@ -379,7 +390,7 @@ module Public { string getName() { result = this.getValue() } /** Gets the representative for the local name introduced by this declaration. */ - LocalName getLocalName() { result = this.(LocalNameBindingOutput::LocalAccess).getLocal() } + LocalName getLocalName() { result = resolveId(this) } } final class LocalVariable = LocalVariableImpl; @@ -497,7 +508,7 @@ final class PotentialLocalNameAccess = PotentialLocalNameAccessImpl; */ abstract private class PotentialLocalNameAccessImpl extends AstNode { /** Gets the representative for the local name being accessed. */ - LocalName getLocalName() { result = this.(LocalNameBindingOutput::LocalAccess).getLocal() } + LocalName getLocalName() { result = resolveId(this) } /** Gets the name being accessed. */ abstract string getName(); diff --git a/unified/ql/test/library-tests/local-name-binding/test.swift b/unified/ql/test/library-tests/local-name-binding/test.swift index eb9be82c2a99..db6ed0fe2eff 100644 --- a/unified/ql/test/library-tests/local-name-binding/test.swift +++ b/unified/ql/test/library-tests/local-name-binding/test.swift @@ -385,23 +385,23 @@ func t39(value: Int) { func t40() { // This function and its parameter have the same name - func foo( // $ SPURIOUS: access=foo2 // name=foo1 - _ foo: String // $ SPURIOUS: access=foo2 captured=foo2 // name=foo2 + func foo( // name=foo1 + _ foo: String // name=foo2 ) { foo // $ access=foo2 } - foo() // $ MISSING: access=foo1 + foo() // $ access=foo1 } func t41() { // This function and its parameter have the same name // The parameter default value must resolve to the outer name (the function) // swift-format-ignore - func foo( // $ SPURIOUS: access=foo2 // name=foo1 - _ foo: Any = // $ SPURIOUS: access=foo2 captured=foo2 // name=foo2 + func foo( // name=foo1 + _ foo: Any = // name=foo2 foo // $ MISSING: access=foo1 SPURIOUS: access=foo2 ) { foo // $ access=foo2 } - foo() // $ MISSING: access=foo1 + foo() // $ access=foo1 } From 3d21281b2b34711a2777f706b894c5c3b4526fc2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 14:19:33 +0100 Subject: [PATCH 5/8] unified: Exclude name bindings from resolution --- unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index ea06e84b33b3..08c3aa845328 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -330,7 +330,9 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig Date: Fri, 30 Oct 2026 14:43:35 +0100 Subject: [PATCH 6/8] unified: Ensure parameter defaults resolve correctly --- .../codeql/unified/internal/LocalNameBinding.qll | 13 +++++++++++-- .../library-tests/local-name-binding/test.swift | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 08c3aa845328..e0ca29ec0868 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -203,8 +203,17 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig Date: Fri, 30 Oct 2026 14:35:15 +0100 Subject: [PATCH 7/8] unified: Simplify getChild This turned out not to be needed for the fix I was working on, but it seemed like a worthwhile simplification to land anyway. --- .../unified/internal/LocalNameBinding.qll | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index e0ca29ec0868..ce2299832c58 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -76,28 +76,21 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig Date: Wed, 11 Nov 2026 11:10:14 +0100 Subject: [PATCH 8/8] unified: Fix resolution of 'super' to 'self' resolveId only accepted Identifier, but SuperExpr is also a PotentialLocalNameAccess. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .../lib/codeql/unified/internal/LocalNameBinding.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index ce2299832c58..21e0a92521de 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -351,15 +351,15 @@ predicate bindingContext = LocalNameBindingInput::bindingContext/3; module LocalNameBindingOutput = LocalNameBinding; -private LocalNameBindingOutput::Local resolveId(Identifier id) { +private LocalNameBindingOutput::Local resolveId(AstNode n) { // For names in binding position, always resolve to the scope set by the bindingContext predicate exists(AstNode scope | - bindingContext(id, scope, _) and - result.hasNameAndScope(id.getValue(), scope) + bindingContext(n, scope, _) and + result.hasNameAndScope(n.(Identifier).getValue(), scope) ) or - not bindingContext(id, _, _) and - result = id.(LocalNameBindingOutput::LocalAccess).getLocal() + not bindingContext(n, _, _) and + result = n.(LocalNameBindingOutput::LocalAccess).getLocal() } module Public {