From 9c16ee84e5519a5929a843c9b929034092e46a90 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 13:54:38 +0100 Subject: [PATCH 1/7] 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 f234ed8e37bd498cf5451ed42bdf88de09b233a0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 13:55:49 +0100 Subject: [PATCH 2/7] 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 92cb7d1e8d9f6c2e4b1bd8e4c4d5565e3a934739 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 14:05:10 +0100 Subject: [PATCH 3/7] 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 9db28d702497..f56f42306999 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/7] 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 f56f42306999..9d438e81c666 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; @@ -495,7 +506,7 @@ module Public { */ class PotentialLocalNameAccess extends IdentifierExpr { /** 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. */ string getName() { result = this.getValue() } 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 f210a1b63ce5b0ae115651b4f122946f67522ae4 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 30 Oct 2026 14:19:33 +0100 Subject: [PATCH 5/7] 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 9d438e81c666..fa66e9c7aa08 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/7] 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 fa66e9c7aa08..cd2a30349396 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/7] 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 cd2a30349396..1d863da67564 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