From cea1106a50759fb2576842e723805b5b4956fe27 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:45:33 +0200 Subject: [PATCH 01/20] unified: Fix extraction of type parameter bounds and constraints --- .../extractor/src/languages/swift/swift.rs | 50 ++++++++++ ...ic-class-parameters-and-constraints.output | 94 +++++++++++++++++++ ...ric-class-parameters-and-constraints.swift | 2 + 3 files changed, 146 insertions(+) create mode 100644 unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output create mode 100644 unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.swift diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 000601b7215c..407f36bea2ba 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1065,19 +1065,51 @@ fn translation_rules() -> Vec> { // A nominal type's `inheritanceClause` (`: Base, Proto`) becomes a list // of `base_type`s, one per inherited type. Each declaration keyword // gets its own rule; the bodies are identical but for the keyword. + rule!( + (genericParameter + attributes: _* @attrs + specifier: _? @@spec + name: @@name + inheritedType: _? @bound) + => + (type_parameter + modifier: {attrs} + modifier: (modifier #{spec})? + name: (identifier #{name}) + bound: {bound}) + ), + rule!( + (genericRequirement + requirement: (conformanceRequirement leftType: @ty rightType: @bound)) + => + (bound_type_constraint type: {ty} bound: {bound}) + ), + rule!( + (genericRequirement + requirement: (sameTypeRequirement leftType: @left rightType: @right)) + => + (equality_type_constraint left: {left} right: {right}) + ), // Class declaration with body containing members rule!( (classDecl classKeyword: @kind modifiers: _* @mods name: @name + genericParameterClause: (genericParameterClause + parameters: _* @params + genericWhereClause: (genericWhereClause requirements: _* @parameter_constraints)?)? inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)? + genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)? memberBlock: (memberBlock members: _* @members)) => (class_like_declaration modifier: (modifier #{kind}) modifier: {mods} name: (identifier #{name}) + type_parameter: {params} + type_constraint: {parameter_constraints} + type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} member: {members}) ), @@ -1087,13 +1119,20 @@ fn translation_rules() -> Vec> { enumKeyword: @kind modifiers: _* @mods name: @name + genericParameterClause: (genericParameterClause + parameters: _* @params + genericWhereClause: (genericWhereClause requirements: _* @parameter_constraints)?)? inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)? + genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)? memberBlock: (memberBlock members: _* @members)) => (class_like_declaration modifier: (modifier #{kind}) modifier: {mods} name: (identifier #{name}) + type_parameter: {params} + type_constraint: {parameter_constraints} + type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} member: {members}) ), @@ -1103,13 +1142,20 @@ fn translation_rules() -> Vec> { structKeyword: @kind modifiers: _* @mods name: @name + genericParameterClause: (genericParameterClause + parameters: _* @params + genericWhereClause: (genericWhereClause requirements: _* @parameter_constraints)?)? inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)? + genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)? memberBlock: (memberBlock members: _* @members)) => (class_like_declaration modifier: (modifier #{kind}) modifier: {mods} name: (identifier #{name}) + type_parameter: {params} + type_constraint: {parameter_constraints} + type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} member: {members}) ), @@ -1119,13 +1165,17 @@ fn translation_rules() -> Vec> { protocolKeyword: @kind modifiers: _* @mods name: @name + genericParameterClause: (genericParameterClause parameters: _* @params)? inheritanceClause: (inheritanceClause inheritedTypes: (inheritedType type: @bases)*)? + genericWhereClause: (genericWhereClause requirements: _* @declaration_constraints)? memberBlock: (memberBlock members: _* @members)) => (class_like_declaration modifier: (modifier #{kind}) modifier: {mods} name: (identifier #{name}) + type_parameter: {params} + type_constraint: {declaration_constraints} base_type: {bases.into_iter().map(|ty| tree!((base_type type: {ty})))} member: {members}) ), diff --git a/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output b/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output new file mode 100644 index 000000000000..26b17599ae00 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.output @@ -0,0 +1,94 @@ +class Box where U: Equatable, U == T { +} + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + classDecl + attributes: + name: identifier "Box" + genericParameterClause: + genericParameterClause + parameters: + genericParameter + colon: : + attributes: + name: identifier "T" + trailingComma: , + inheritedType: + identifierType + name: identifier "Equatable" + genericParameter + attributes: + name: identifier "U" + leftAngle: < + rightAngle: > + genericWhereClause: + genericWhereClause + requirements: + genericRequirement + trailingComma: , + requirement: + conformanceRequirement + colon: : + leftType: + identifierType + name: identifier "U" + rightType: + identifierType + name: identifier "Equatable" + genericRequirement + requirement: + sameTypeRequirement + equal: binaryOperator "==" + leftType: + identifierType + name: identifier "U" + rightType: + identifierType + name: identifier "T" + whereKeyword: where + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + modifiers: + classKeyword: class + +--- + +top_level + body: + block + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Box" + type_parameter: + type_parameter + name: identifier "T" + bound: + named_type_expr + name: identifier "Equatable" + type_parameter + name: identifier "U" + type_constraint: + bound_type_constraint + type: + named_type_expr + name: identifier "U" + bound: + named_type_expr + name: identifier "Equatable" + equality_type_constraint + left: + named_type_expr + name: identifier "U" + right: + named_type_expr + name: identifier "T" diff --git a/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.swift b/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.swift new file mode 100644 index 000000000000..80c882308a51 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/types/generic-class-parameters-and-constraints.swift @@ -0,0 +1,2 @@ +class Box where U: Equatable, U == T { +} From e0e3406dcd9b6042c82972d7920e2e4fdf3d1981 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:46:42 +0200 Subject: [PATCH 02/20] unified: Support . in name test annotations --- unified/ql/test/library-tests/variables/variables.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/test/library-tests/variables/variables.ql b/unified/ql/test/library-tests/variables/variables.ql index cf653afebd9d..26d5913075e7 100644 --- a/unified/ql/test/library-tests/variables/variables.ql +++ b/unified/ql/test/library-tests/variables/variables.ql @@ -13,7 +13,7 @@ predicate plainCommentAt(string filepath, int line, string text) { predicate keyValueCommentAt(string filepath, int line, string key, string value) { exists(string text, string regexp, string match | plainCommentAt(filepath, line, text) and - regexp = "(\\w+)=(\\w+)" and + regexp = "(\\w+)=([\\w.]+)" and match = text.regexpFind(regexp, _, _) and key = match.regexpCapture(regexp, 1) and value = match.regexpCapture(regexp, 2) From eef277aafbc4cdab0aca5d67e1b5fb166e04be77 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:47:00 +0200 Subject: [PATCH 03/20] unified: Add more local name binding test cases --- .../library-tests/variables/class_scope.swift | 73 +++++++++++++++++++ .../test/library-tests/variables/test.swift | 2 +- .../library-tests/variables/top_level.swift | 17 +++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 unified/ql/test/library-tests/variables/class_scope.swift create mode 100644 unified/ql/test/library-tests/variables/top_level.swift diff --git a/unified/ql/test/library-tests/variables/class_scope.swift b/unified/ql/test/library-tests/variables/class_scope.swift new file mode 100644 index 000000000000..a4a85d23f084 --- /dev/null +++ b/unified/ql/test/library-tests/variables/class_scope.swift @@ -0,0 +1,73 @@ +class A { + static func static_before() { + print(staticVar) // $ access=staticVar + B(); // $ access=A.B + let b: B = nil // $ access=A.B + let c: C = nil // $ access=A.C + } + func instance_before() { + print(instanceVar) // $ access=instanceVar + B(); // $ access=A.B + let b: B = nil // $ access=A.B + let c: C = nil // $ access=A.C + } + + private static let staticVar = 123 + private let instanceVar = 456 + + class B {} // name=A.B + typealias C = B // $ access=A.B // name=A.C + + static func static_after() { + print(staticVar) // $ access=staticVar + B(); // $ access=A.B + let b: B = nil // $ access=A.B + let c: C = nil // $ access=A.C + + } + func instance_after() { + print(instanceVar) // $ access=instanceVar + B(); // $ access=A.B + let b: B = nil // $ access=A.B + let c: C = nil // $ access=A.C + } +} + +class Base {} // name=top.Base + +// Base types and type parameter bounds can't see members in the class body +class C : Base { // $ access=top.Base + class Base {} // name=C.Base +} + +class D { // $ access=top.Base + class Base {} // name=D.Base +} + +class E where T : Base { // $ access=top.Base access=T + class Base {} // name=E.Base +} + +// Base types and type parameter bounds can see type parameters +class F : + D { // $ access=TypeParamF access=D +} + +class G> { // $ access=TypeParamG access=D +} + +class H where + TypeParamH : Base { // $ access=TypeParamH access=top.Base +} + +// Type parameter bounds can see other type parameters, even if declared later +class I< + T1 : D, // $ access=D access=I.T2 + T2> { // name=I.T2 +} + +// Members can see type parameters. +class J { + let x: TypeParamI; // $ access=TypeParamI +} diff --git a/unified/ql/test/library-tests/variables/test.swift b/unified/ql/test/library-tests/variables/test.swift index e4280dc5b408..b5d0469e8a44 100644 --- a/unified/ql/test/library-tests/variables/test.swift +++ b/unified/ql/test/library-tests/variables/test.swift @@ -343,7 +343,7 @@ enum E38 { } // Switch with a multi-pattern case that binds 'x' in each pattern -func t38(value: E38) { +func t38(value: E38) { // $ access=E38 switch value { // $ access=value case .a(let x), // $ access=x1 // name=x1 .b(let x): // $ access=x1 diff --git a/unified/ql/test/library-tests/variables/top_level.swift b/unified/ql/test/library-tests/variables/top_level.swift new file mode 100644 index 000000000000..a7ea16ef10b8 --- /dev/null +++ b/unified/ql/test/library-tests/variables/top_level.swift @@ -0,0 +1,17 @@ +func use_before() { + print(x) // $ access=top.x + B(); // $ access=top.B + let b: B = nil // $ access=top.B + let c: C = nil // $ access=top.C +} + +let x = 123 // name=top.x +class B {} // name=top.B +typealias C = B // $ access=top.B // name=top.C + +func use_after() { + print(x) // $ access=top.x + B(); // $ access=top.B + let b: B = nil // $ access=top.B + let c: C = nil // $ access=top.C +} From 3b51c4ce97d44606ace08c5222d1c26bb6bc42b9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:59:40 +0200 Subject: [PATCH 04/20] unified: add MISSING annotations --- .../library-tests/variables/class_scope.swift | 50 +++++++++---------- .../test/library-tests/variables/test.swift | 2 +- .../library-tests/variables/top_level.swift | 16 +++--- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/unified/ql/test/library-tests/variables/class_scope.swift b/unified/ql/test/library-tests/variables/class_scope.swift index a4a85d23f084..275c484fb8ac 100644 --- a/unified/ql/test/library-tests/variables/class_scope.swift +++ b/unified/ql/test/library-tests/variables/class_scope.swift @@ -1,73 +1,73 @@ class A { static func static_before() { - print(staticVar) // $ access=staticVar - B(); // $ access=A.B - let b: B = nil // $ access=A.B - let c: C = nil // $ access=A.C + print(staticVar) // $ MISSING: access=staticVar + B(); // $ MISSING: access=A.B + let b: B = nil // $ MISSING: access=A.B + let c: C = nil // $ MISSING: access=A.C } func instance_before() { - print(instanceVar) // $ access=instanceVar - B(); // $ access=A.B - let b: B = nil // $ access=A.B - let c: C = nil // $ access=A.C + print(instanceVar) // $ MISSING: access=instanceVar + B(); // $ MISSING: access=A.B + let b: B = nil // $ MISSING: access=A.B + let c: C = nil // $ MISSING: access=A.C } private static let staticVar = 123 private let instanceVar = 456 class B {} // name=A.B - typealias C = B // $ access=A.B // name=A.C + typealias C = B // $ MISSING: access=A.B // name=A.C static func static_after() { - print(staticVar) // $ access=staticVar - B(); // $ access=A.B - let b: B = nil // $ access=A.B - let c: C = nil // $ access=A.C + print(staticVar) // $ MISSING: access=staticVar + B(); // $ MISSING: access=A.B + let b: B = nil // $ MISSING: access=A.B + let c: C = nil // $ MISSING: access=A.C } func instance_after() { - print(instanceVar) // $ access=instanceVar - B(); // $ access=A.B - let b: B = nil // $ access=A.B - let c: C = nil // $ access=A.C + print(instanceVar) // $ MISSING: access=instanceVar + B(); // $ MISSING: access=A.B + let b: B = nil // $ MISSING: access=A.B + let c: C = nil // $ MISSING: access=A.C } } class Base {} // name=top.Base // Base types and type parameter bounds can't see members in the class body -class C : Base { // $ access=top.Base +class C : Base { // $ MISSING: access=top.Base class Base {} // name=C.Base } -class D { // $ access=top.Base +class D { // $ MISSING: access=top.Base class Base {} // name=D.Base } -class E where T : Base { // $ access=top.Base access=T +class E where T : Base { // $ MISSING: access=T access=top.Base class Base {} // name=E.Base } // Base types and type parameter bounds can see type parameters class F : - D { // $ access=TypeParamF access=D + D { // $ MISSING: access=D access=TypeParamF } class G> { // $ access=TypeParamG access=D + D> { // $ MISSING: access=D access=TypeParamG } class H where - TypeParamH : Base { // $ access=TypeParamH access=top.Base + TypeParamH : Base { // $ MISSING: access=TypeParamH access=top.Base } // Type parameter bounds can see other type parameters, even if declared later class I< - T1 : D, // $ access=D access=I.T2 + T1 : D, // $ MISSING: access=D access=I.T2 T2> { // name=I.T2 } // Members can see type parameters. class J { - let x: TypeParamI; // $ access=TypeParamI + let x: TypeParamI; // $ MISSING: access=TypeParamI } diff --git a/unified/ql/test/library-tests/variables/test.swift b/unified/ql/test/library-tests/variables/test.swift index b5d0469e8a44..37ba0f5282eb 100644 --- a/unified/ql/test/library-tests/variables/test.swift +++ b/unified/ql/test/library-tests/variables/test.swift @@ -343,7 +343,7 @@ enum E38 { } // Switch with a multi-pattern case that binds 'x' in each pattern -func t38(value: E38) { // $ access=E38 +func t38(value: E38) { // $ MISSING: access=E38 switch value { // $ access=value case .a(let x), // $ access=x1 // name=x1 .b(let x): // $ access=x1 diff --git a/unified/ql/test/library-tests/variables/top_level.swift b/unified/ql/test/library-tests/variables/top_level.swift index a7ea16ef10b8..9489b950d4f3 100644 --- a/unified/ql/test/library-tests/variables/top_level.swift +++ b/unified/ql/test/library-tests/variables/top_level.swift @@ -1,17 +1,17 @@ func use_before() { - print(x) // $ access=top.x - B(); // $ access=top.B - let b: B = nil // $ access=top.B - let c: C = nil // $ access=top.C + print(x) // $ MISSING: access=top.x + B(); // $ MISSING: access=top.B + let b: B = nil // $ MISSING: access=top.B + let c: C = nil // $ MISSING: access=top.C } let x = 123 // name=top.x class B {} // name=top.B -typealias C = B // $ access=top.B // name=top.C +typealias C = B // $ MISSING: access=top.B // name=top.C func use_after() { print(x) // $ access=top.x - B(); // $ access=top.B - let b: B = nil // $ access=top.B - let c: C = nil // $ access=top.C + B(); // $ MISSING: access=top.B + let b: B = nil // $ MISSING: access=top.B + let c: C = nil // $ MISSING: access=top.C } From 7d1b1e47511609656f89903e7d705a37a7c13348 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:47:25 +0200 Subject: [PATCH 05/20] unified: Track more kinds of names as local variables --- .../lib/codeql/unified/internal/AstExtra.qll | 5 ++ .../lib/codeql/unified/internal/Variables.qll | 60 ++++++++++++++++--- .../library-tests/variables/class_scope.swift | 50 ++++++++-------- .../test/library-tests/variables/test.swift | 2 +- .../library-tests/variables/top_level.swift | 16 ++--- 5 files changed, 91 insertions(+), 42 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/AstExtra.qll b/unified/ql/lib/codeql/unified/internal/AstExtra.qll index e14043dc46d2..378712b86cb9 100644 --- a/unified/ql/lib/codeql/unified/internal/AstExtra.qll +++ b/unified/ql/lib/codeql/unified/internal/AstExtra.qll @@ -52,4 +52,9 @@ module Public { result = this.getValue().regexpCapture("(?s)/\\*(.*)\\*/", 1) } } + + /** A `Stmt` at the top-level. */ + final class TopLevelStmt extends Stmt { + TopLevelStmt() { this = any(TopLevel t).getBody().getAStmt() } + } } diff --git a/unified/ql/lib/codeql/unified/internal/Variables.qll b/unified/ql/lib/codeql/unified/internal/Variables.qll index 49cad56d05db..7a05ce409e6f 100644 --- a/unified/ql/lib/codeql/unified/internal/Variables.qll +++ b/unified/ql/lib/codeql/unified/internal/Variables.qll @@ -130,6 +130,8 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig { // $ MISSING: access=top.Base +class D { // $ MISSING: access=top.Base SPURIOUS: access=D.Base class Base {} // name=D.Base } -class E where T : Base { // $ MISSING: access=T access=top.Base +class E where T : Base { // $ access=T MISSING: access=top.Base SPURIOUS: access=E.Base class Base {} // name=E.Base } // Base types and type parameter bounds can see type parameters class F : - D { // $ MISSING: access=D access=TypeParamF + D { // $ access=D access=TypeParamF } class G> { // $ MISSING: access=D access=TypeParamG + D> { // $ access=D access=TypeParamG } class H where - TypeParamH : Base { // $ MISSING: access=TypeParamH access=top.Base + TypeParamH : Base { // $ access=TypeParamH access=top.Base } // Type parameter bounds can see other type parameters, even if declared later class I< - T1 : D, // $ MISSING: access=D access=I.T2 + T1 : D, // $ access=D access=I.T2 T2> { // name=I.T2 } // Members can see type parameters. class J { - let x: TypeParamI; // $ MISSING: access=TypeParamI + let x: TypeParamI; // $ access=TypeParamI } diff --git a/unified/ql/test/library-tests/variables/test.swift b/unified/ql/test/library-tests/variables/test.swift index 37ba0f5282eb..b5d0469e8a44 100644 --- a/unified/ql/test/library-tests/variables/test.swift +++ b/unified/ql/test/library-tests/variables/test.swift @@ -343,7 +343,7 @@ enum E38 { } // Switch with a multi-pattern case that binds 'x' in each pattern -func t38(value: E38) { // $ MISSING: access=E38 +func t38(value: E38) { // $ access=E38 switch value { // $ access=value case .a(let x), // $ access=x1 // name=x1 .b(let x): // $ access=x1 diff --git a/unified/ql/test/library-tests/variables/top_level.swift b/unified/ql/test/library-tests/variables/top_level.swift index 9489b950d4f3..a7ea16ef10b8 100644 --- a/unified/ql/test/library-tests/variables/top_level.swift +++ b/unified/ql/test/library-tests/variables/top_level.swift @@ -1,17 +1,17 @@ func use_before() { - print(x) // $ MISSING: access=top.x - B(); // $ MISSING: access=top.B - let b: B = nil // $ MISSING: access=top.B - let c: C = nil // $ MISSING: access=top.C + print(x) // $ access=top.x + B(); // $ access=top.B + let b: B = nil // $ access=top.B + let c: C = nil // $ access=top.C } let x = 123 // name=top.x class B {} // name=top.B -typealias C = B // $ MISSING: access=top.B // name=top.C +typealias C = B // $ access=top.B // name=top.C func use_after() { print(x) // $ access=top.x - B(); // $ MISSING: access=top.B - let b: B = nil // $ MISSING: access=top.B - let c: C = nil // $ MISSING: access=top.C + B(); // $ access=top.B + let b: B = nil // $ access=top.B + let c: C = nil // $ access=top.C } From 5d851e4d7ac2f35979431a50203e07cb6f34a955 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:47:25 +0200 Subject: [PATCH 06/20] unified: More precise scoping inside classes --- .../ql/lib/codeql/unified/internal/Variables.qll | 15 +++++++++++++++ .../library-tests/variables/class_scope.swift | 6 +++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/Variables.qll b/unified/ql/lib/codeql/unified/internal/Variables.qll index 7a05ce409e6f..7151b17ad3c3 100644 --- a/unified/ql/lib/codeql/unified/internal/Variables.qll +++ b/unified/ql/lib/codeql/unified/internal/Variables.qll @@ -90,6 +90,20 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig { // $ MISSING: access=top.Base SPURIOUS: access=D.Base +class D { // $ access=top.Base class Base {} // name=D.Base } -class E where T : Base { // $ access=T MISSING: access=top.Base SPURIOUS: access=E.Base +class E where T : Base { // $ access=T access=top.Base class Base {} // name=E.Base } From 3e25c6aaf3e483c3431fdb8e426b31306cf8e565 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:28:10 +0200 Subject: [PATCH 07/20] unified: Rename Variables.qll -> LocalNameBinding.qll --- .../unified/internal/{Variables.qll => LocalNameBinding.qll} | 0 unified/ql/lib/unified.qll | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename unified/ql/lib/codeql/unified/internal/{Variables.qll => LocalNameBinding.qll} (100%) diff --git a/unified/ql/lib/codeql/unified/internal/Variables.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll similarity index 100% rename from unified/ql/lib/codeql/unified/internal/Variables.qll rename to unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll diff --git a/unified/ql/lib/unified.qll b/unified/ql/lib/unified.qll index fad4b3ef9e67..67d6ce7558ea 100644 --- a/unified/ql/lib/unified.qll +++ b/unified/ql/lib/unified.qll @@ -6,4 +6,4 @@ import codeql.Locations import codeql.files.FileSystem import codeql.unified.internal.Ast::UnifiedFinal import codeql.unified.internal.AstExtra::Public -import codeql.unified.internal.Variables::Public +import codeql.unified.internal.LocalNameBinding::Public From 6df74d7c3e6c0e2d2420bbda6dcc45ef61d50b93 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:30:12 +0200 Subject: [PATCH 08/20] unified: Rename 'variables' test --- .../{variables => local-name-binding}/class_scope.swift | 0 .../variables.expected => local-name-binding/test.expected} | 0 .../{variables/variables.ql => local-name-binding/test.ql} | 0 .../library-tests/{variables => local-name-binding}/test.swift | 0 .../{variables => local-name-binding}/top_level.swift | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename unified/ql/test/library-tests/{variables => local-name-binding}/class_scope.swift (100%) rename unified/ql/test/library-tests/{variables/variables.expected => local-name-binding/test.expected} (100%) rename unified/ql/test/library-tests/{variables/variables.ql => local-name-binding/test.ql} (100%) rename unified/ql/test/library-tests/{variables => local-name-binding}/test.swift (100%) rename unified/ql/test/library-tests/{variables => local-name-binding}/top_level.swift (100%) diff --git a/unified/ql/test/library-tests/variables/class_scope.swift b/unified/ql/test/library-tests/local-name-binding/class_scope.swift similarity index 100% rename from unified/ql/test/library-tests/variables/class_scope.swift rename to unified/ql/test/library-tests/local-name-binding/class_scope.swift diff --git a/unified/ql/test/library-tests/variables/variables.expected b/unified/ql/test/library-tests/local-name-binding/test.expected similarity index 100% rename from unified/ql/test/library-tests/variables/variables.expected rename to unified/ql/test/library-tests/local-name-binding/test.expected diff --git a/unified/ql/test/library-tests/variables/variables.ql b/unified/ql/test/library-tests/local-name-binding/test.ql similarity index 100% rename from unified/ql/test/library-tests/variables/variables.ql rename to unified/ql/test/library-tests/local-name-binding/test.ql diff --git a/unified/ql/test/library-tests/variables/test.swift b/unified/ql/test/library-tests/local-name-binding/test.swift similarity index 100% rename from unified/ql/test/library-tests/variables/test.swift rename to unified/ql/test/library-tests/local-name-binding/test.swift diff --git a/unified/ql/test/library-tests/variables/top_level.swift b/unified/ql/test/library-tests/local-name-binding/top_level.swift similarity index 100% rename from unified/ql/test/library-tests/variables/top_level.swift rename to unified/ql/test/library-tests/local-name-binding/top_level.swift From 686a07d7a5dd47ec5bcdbf2ef562bc236560933e Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 11:24:45 +0200 Subject: [PATCH 09/20] unified: Rename 'Variable' API to use LocalName terminology These can also refer to type names and module names, so 'Variable' is not an appropriate term. Actual variables will be introduced at a later stage. --- .../unified/internal/LocalNameBinding.qll | 54 +++++++++++++------ .../library-tests/local-name-binding/test.ql | 13 ++--- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 7151b17ad3c3..3f84fe4fcdcb 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -311,26 +311,46 @@ module LocalNameBindingOutput = LocalNameBinding -private Variable getVariableAt(string name, string filepath, int line) { +private LocalName getVariableAt(string name, string filepath, int line) { VariableAccessTest::declAt(result, filepath, line) and result.getName() = name } -query predicate ambiguousVariable(Variable v, string name, string filepath, int line) { +query predicate ambiguousVariable(LocalName v, string name, string filepath, int line) { v = getVariableAt(name, filepath, line) and strictcount(getVariableAt(name, filepath, line)) >= 2 } From a5faaefa53d8142982f8806763531aa882af1938 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 13:32:34 +0200 Subject: [PATCH 10/20] unified: Two refactorings - Use PotentialLocalNameAccess to define accessCand(), not the other way around - Use the Identifier as the definingNode, not wrappers like NameExpr --- .../unified/internal/LocalNameBinding.qll | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 3f84fe4fcdcb..8ab88b12352b 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -251,6 +251,11 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig; @@ -329,7 +325,7 @@ module Public { } /** - * An AST node that is a possibly reference to a local name, but could also refer to a member + * An identifier node that is a possibly reference to a local name, but could also refer to a member * visible through imports or inheritance. * * For example, the type annotation `C` below is a potential access to `class C`, but could @@ -341,16 +337,19 @@ module Public { * } * ``` */ -class PotentialLocalNameAccess extends LocalNameBindingOutput::LocalAccess { - LocalName getLocalName() { result = super.getLocal() } - - Identifier getIdentifier() { - result = this.(NameExpr).getIdentifier() +class PotentialLocalNameAccess extends Identifier { + PotentialLocalNameAccess() { + this = any(NameExpr e).getIdentifier() + or + this = any(NamePattern e).getIdentifier() or - result = this.(NamePattern).getIdentifier() + this = any(NamedTypeExpr e | not exists(e.getQualifier())).getName() or - result = this + this = any(FunctionDeclaration f).getName() + // TODO: include other declaration kinds here } - string getName() { result = this.getIdentifier().getValue() } + LocalName getLocalName() { result = this.(LocalNameBindingOutput::LocalAccess).getLocal() } + + string getName() { result = this.getValue() } } From 179ae25ab83948842f9703867f0876d37743c4e0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 13:39:29 +0200 Subject: [PATCH 11/20] unified: Also include declaration site access --- .../ql/lib/codeql/unified/internal/LocalNameBinding.qll | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 8ab88b12352b..6fb4f2fa2ab5 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -184,7 +184,7 @@ private module LocalNameBindingInput implements LocalNameBindingInputSig Date: Wed, 5 Aug 2026 14:16:23 +0200 Subject: [PATCH 12/20] unified: Replace getDefiningNode() with isDeclarationSite() --- .../ql/lib/codeql/unified/internal/LocalNameBinding.qll | 3 --- unified/ql/test/library-tests/local-name-binding/test.ql | 7 ++++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll index 6fb4f2fa2ab5..5e60e005df39 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -316,9 +316,6 @@ module Public { /** Gets the location of this local name's first declaration */ Location getLocation() { result = super.getLocation() } - /** Gets the AST node defining this local name. */ - AstNode getDefiningNode() { result = super.getDefiningNode() } - /** Gets the name of this local, as a string. */ string getName() { result = super.getName() } } diff --git a/unified/ql/test/library-tests/local-name-binding/test.ql b/unified/ql/test/library-tests/local-name-binding/test.ql index 61fb9520e7df..0fcae36dd31e 100644 --- a/unified/ql/test/library-tests/local-name-binding/test.ql +++ b/unified/ql/test/library-tests/local-name-binding/test.ql @@ -37,10 +37,15 @@ module VariableAccessTest implements TestSig { ) } + private PotentialLocalNameAccess getUniqueDeclarationSite(LocalName name) { + result = + unique(PotentialLocalNameAccess ac | ac.isDeclarationSite() and ac.getLocalName() = name) + } + predicate hasActualResult(Location location, string element, string tag, string value) { exists(PotentialLocalNameAccess va, LocalName v | v = va.getLocalName() and - not va = v.getDefiningNode() and + not va = getUniqueDeclarationSite(v) and // no need to annotate declaration site, if there is only one location = va.getLocation() and element = va.toString() and decl(v, value) and From 012b1ae9d7ce11e4a9b2fcfc591f0235822ec6f4 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 14:17:16 +0200 Subject: [PATCH 13/20] unified: Minor doc comment update --- 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 5e60e005df39..c978eccff0ac 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -1,5 +1,5 @@ /** - * Provides classes for reasoning about lexically scoped variables and references to these. + * Provides classes for reasoning about lexically scoped names and references to these. */ private import unified From a3a54c358b9048e5ba2cc9b621ea53d98846e2f5 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 14:29:21 +0200 Subject: [PATCH 14/20] unified: fix typo --- 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 c978eccff0ac..8c78a187b766 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -322,7 +322,7 @@ module Public { } /** - * An identifier node that is a possibly reference to a local name, but could also refer to a member + * An identifier node that is possibly a reference to a local name, but could also refer to a member * visible through imports or inheritance. * * For example, the type annotation `C` below is a potential access to `class C`, but could From 6e89c7b8f1d2435904d791c51d5a85f02d056d34 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 15:11:33 +0200 Subject: [PATCH 15/20] unified: Add mappings for type parameters on functions and type aliases --- .../extractor/src/languages/swift/swift.rs | 6 ++ .../swift/functions/generic-function.output | 3 + .../swift/functions/generic-type-alias.output | 77 +++++++++++++++++++ .../swift/functions/generic-type-alias.swift | 1 + 4 files changed, 87 insertions(+) create mode 100644 unified/extractor/tests/corpus/swift/functions/generic-type-alias.output create mode 100644 unified/extractor/tests/corpus/swift/functions/generic-type-alias.swift diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 407f36bea2ba..1a355aaffb85 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -568,6 +568,7 @@ fn translation_rules() -> Vec> { rule!( (functionDecl name: @name + genericParameterClause: (genericParameterClause parameters: _* @type_params)? signature: (functionSignature parameterClause: (functionParameterClause parameters: _* @params) returnClause: (returnClause type: @ret)?) @@ -575,6 +576,7 @@ fn translation_rules() -> Vec> { => (function_declaration name: (identifier #{name}) + type_parameter: {type_params} parameter: {params} return_type: {ret} body: (block stmt: {body})) @@ -582,12 +584,14 @@ fn translation_rules() -> Vec> { rule!( (functionDecl name: @name + genericParameterClause: (genericParameterClause parameters: _* @type_params)? signature: (functionSignature parameterClause: (functionParameterClause parameters: _* @params) returnClause: (returnClause type: @ret)?)) => (function_declaration name: (identifier #{name}) + type_parameter: {type_params} parameter: {params} return_type: {ret} body: (block)) @@ -1230,11 +1234,13 @@ fn translation_rules() -> Vec> { (typeAliasDecl modifiers: _* @mods name: @@name + genericParameterClause: (genericParameterClause parameters: _* @type_params)? initializer: (typeInitializerClause value: @val)) => (type_alias_declaration modifier: {mods} name: (identifier #{name}) + type_parameter: {type_params} r#type: {val}) ), // Associated type declaration (with optional bound) diff --git a/unified/extractor/tests/corpus/swift/functions/generic-function.output b/unified/extractor/tests/corpus/swift/functions/generic-function.output index d09aac2744a2..dc7831410e23 100644 --- a/unified/extractor/tests/corpus/swift/functions/generic-function.output +++ b/unified/extractor/tests/corpus/swift/functions/generic-function.output @@ -65,6 +65,9 @@ top_level stmt: function_declaration name: identifier "identity" + type_parameter: + type_parameter + name: identifier "T" parameter: parameter external_name: identifier "_" diff --git a/unified/extractor/tests/corpus/swift/functions/generic-type-alias.output b/unified/extractor/tests/corpus/swift/functions/generic-type-alias.output new file mode 100644 index 000000000000..36737bd95bda --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/generic-type-alias.output @@ -0,0 +1,77 @@ +typealias Box = Dictionary + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + typeAliasDecl + attributes: + name: identifier "Box" + genericParameterClause: + genericParameterClause + parameters: + genericParameter + colon: : + attributes: + name: identifier "T" + trailingComma: , + inheritedType: + identifierType + name: identifier "Equatable" + genericParameter + attributes: + name: identifier "U" + leftAngle: < + rightAngle: > + modifiers: + initializer: + typeInitializerClause + equal: = + value: + identifierType + name: identifier "Dictionary" + genericArgumentClause: + genericArgumentClause + arguments: + genericArgument + trailingComma: , + argument: + identifierType + name: identifier "T" + genericArgument + argument: + identifierType + name: identifier "U" + leftAngle: < + rightAngle: > + typealiasKeyword: typealias + +--- + +top_level + body: + block + stmt: + type_alias_declaration + name: identifier "Box" + type_parameter: + type_parameter + name: identifier "T" + bound: + named_type_expr + name: identifier "Equatable" + type_parameter + name: identifier "U" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Dictionary" + type_argument: + named_type_expr + name: identifier "T" + named_type_expr + name: identifier "U" diff --git a/unified/extractor/tests/corpus/swift/functions/generic-type-alias.swift b/unified/extractor/tests/corpus/swift/functions/generic-type-alias.swift new file mode 100644 index 000000000000..831f552bab1a --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/generic-type-alias.swift @@ -0,0 +1 @@ +typealias Box = Dictionary From 5a302d5e64b8922cd5b44eb32fb1df4c4b5e721a Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 15:14:41 +0200 Subject: [PATCH 16/20] unified: Add name binding tests --- .../library-tests/local-name-binding/class_scope.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/unified/ql/test/library-tests/local-name-binding/class_scope.swift b/unified/ql/test/library-tests/local-name-binding/class_scope.swift index d02ce69c8b53..e206aa0e5515 100644 --- a/unified/ql/test/library-tests/local-name-binding/class_scope.swift +++ b/unified/ql/test/library-tests/local-name-binding/class_scope.swift @@ -71,3 +71,11 @@ class I< class J { let x: TypeParamI; // $ access=TypeParamI } + +typealias Alias = + D; // $ access=D access=TypeParamAlias + +func foo(x: FooT) // $ access=FooT + -> D { // $ access=D access=FooT + let x: FooT = nil // $ access=FooT +} From 74e46ba70eeb634b7c6f205d80d2c5690faa4766 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 15:34:11 +0200 Subject: [PATCH 17/20] unified: Add mappings for constructor calls with generic arguments --- .../extractor/src/languages/swift/swift.rs | 14 +++ .../generic-specialization-expression.output | 9 +- ...onstructor-call-with-type-arguments.output | 106 ++++++++++++++++++ ...constructor-call-with-type-arguments.swift | 3 + 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output create mode 100644 unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.swift diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 1a355aaffb85..ccc1cc7f725d 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -185,6 +185,20 @@ fn translation_rules() -> Vec> { // the target AST has no expression-level discard (only `ignore_pattern`, // which is a pattern), so it becomes a `name_expr` over the `_` token. rule!((discardAssignmentExpr wildcard: @@w) => (name_expr identifier: (identifier #{w}))), + // A generic specialization in expression position (`C`, + // `Array`) is represented by swift-syntax as a + // `genericSpecializationExpr`. When used as a call target + // (`C()`), this should become a `call_expr` whose callee is a + // `generic_type_expr`, so we map it directly to that shape. + rule!( + (genericSpecializationExpr + expression: (declReferenceExpr baseName: @name) + genericArgumentClause: (genericArgumentClause arguments: (genericArgument argument: @args)*)) + => + (generic_type_expr + base: (named_type_expr name: (identifier #{name})) + type_argument: {args}) + ), // ---- Operators ---- // The parser front-end folds operator chains into nested // `infixOperatorExpr`s by precedence (see swift-syntax-rs), so diff --git a/unified/extractor/tests/corpus/swift/expressions/generic-specialization-expression.output b/unified/extractor/tests/corpus/swift/expressions/generic-specialization-expression.output index 4f3bd0214411..94cdf978b9d2 100644 --- a/unified/extractor/tests/corpus/swift/expressions/generic-specialization-expression.output +++ b/unified/extractor/tests/corpus/swift/expressions/generic-specialization-expression.output @@ -53,4 +53,11 @@ top_level identifier: identifier "numbers" value: call_expr - callee: unsupported_node "Array" + callee: + generic_type_expr + base: + named_type_expr + name: identifier "Array" + type_argument: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output b/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output new file mode 100644 index 000000000000..37afdfa0d639 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.output @@ -0,0 +1,106 @@ +class Foo {} +class C {} +let x = C() + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + classDecl + attributes: + name: identifier "Foo" + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + modifiers: + classKeyword: class + codeBlockItem + item: + classDecl + attributes: + name: identifier "C" + genericParameterClause: + genericParameterClause + parameters: + genericParameter + attributes: + name: identifier "T" + leftAngle: < + rightAngle: > + memberBlock: + memberBlock + leftBrace: { + rightBrace: } + members: + modifiers: + classKeyword: class + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + functionCallExpr + leftParen: ( + rightParen: ) + arguments: + additionalTrailingClosures: + calledExpression: + genericSpecializationExpr + expression: + declReferenceExpr + baseName: identifier "C" + genericArgumentClause: + genericArgumentClause + arguments: + genericArgument + argument: + identifierType + name: identifier "Foo" + leftAngle: < + rightAngle: > + pattern: + identifierPattern + identifier: identifier "x" + +--- + +top_level + body: + block + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Foo" + class_like_declaration + modifier: modifier "class" + name: identifier "C" + type_parameter: + type_parameter + name: identifier "T" + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: + call_expr + callee: + generic_type_expr + base: + named_type_expr + name: identifier "C" + type_argument: + named_type_expr + name: identifier "Foo" diff --git a/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.swift b/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.swift new file mode 100644 index 000000000000..c5a122cb3806 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/functions/constructor-call-with-type-arguments.swift @@ -0,0 +1,3 @@ +class Foo {} +class C {} +let x = C() From aaa2c1c22f9de8652173e2698ea037cb04b32788 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 5 Aug 2026 15:35:04 +0200 Subject: [PATCH 18/20] unified: Add test with generic constructor call --- .../ql/test/library-tests/local-name-binding/class_scope.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/unified/ql/test/library-tests/local-name-binding/class_scope.swift b/unified/ql/test/library-tests/local-name-binding/class_scope.swift index e206aa0e5515..5e251b8e6a0d 100644 --- a/unified/ql/test/library-tests/local-name-binding/class_scope.swift +++ b/unified/ql/test/library-tests/local-name-binding/class_scope.swift @@ -78,4 +78,5 @@ typealias Alias = func foo(x: FooT) // $ access=FooT -> D { // $ access=D access=FooT let x: FooT = nil // $ access=FooT + return D() // $ access=D access=FooT } From 9a31d09b33312604bac3939b2b6576ead5f2f525 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 6 Aug 2026 08:29:51 +0200 Subject: [PATCH 19/20] unified: Update debugScopeGraph.ql --- unified/ql/lib/codeql/unified/internal/dev/debugScopeGraph.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/lib/codeql/unified/internal/dev/debugScopeGraph.ql b/unified/ql/lib/codeql/unified/internal/dev/debugScopeGraph.ql index 2f9376b1732e..a77f8912d6fa 100644 --- a/unified/ql/lib/codeql/unified/internal/dev/debugScopeGraph.ql +++ b/unified/ql/lib/codeql/unified/internal/dev/debugScopeGraph.ql @@ -6,7 +6,7 @@ */ private import unified -private import codeql.unified.internal.Variables +private import codeql.unified.internal.LocalNameBinding /** * Holds if `node` should be shown in the graph. From 08359ecc226d5123663d93ef5a5dbe3d1e3983d4 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 6 Aug 2026 11:38:33 +0200 Subject: [PATCH 20/20] unified: Standardsize toString doc --- 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 8c78a187b766..0df235d521fd 100644 --- a/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll +++ b/unified/ql/lib/codeql/unified/internal/LocalNameBinding.qll @@ -310,7 +310,7 @@ module Public { * A representative for a lexically scoped entity, such as a local variable, type name, or module name. */ class LocalName instanceof LocalNameBindingOutput::Local { - /** Gets the name of this local, as a string. */ + /** Gets a textual representation of this local entity. */ string toString() { result = super.toString() } /** Gets the location of this local name's first declaration */