From 0e9d17b59cf9f70e187e27a2d286897504ea59dd Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:20 +0200 Subject: [PATCH] unified/swift: add top-level normalization and fallback scaffold --- .../extractor/src/languages/swift/swift.rs | 14 ++++++++++- .../extractor/tests/corpus/swift/closures.txt | 10 ++++++++ .../tests/corpus/swift/collections.txt | 21 ++++++++++++---- .../tests/corpus/swift/control-flow.txt | 16 +++++++++++++ .../extractor/tests/corpus/swift/desugar.txt | 4 ++++ .../tests/corpus/swift/functions.txt | 18 ++++++++++++++ .../extractor/tests/corpus/swift/literals.txt | 16 +++++++++++++ .../extractor/tests/corpus/swift/loops.txt | 12 ++++++++++ .../tests/corpus/swift/operators.txt | 24 +++++++++++++++++++ .../corpus/swift/optionals-and-errors.txt | 16 +++++++++++++ .../extractor/tests/corpus/swift/types.txt | 24 +++++++++++++++++++ .../tests/corpus/swift/variables.txt | 16 +++++++++++++ 12 files changed, 185 insertions(+), 6 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index d7ad7d87129..bc2f15ebd49 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,8 +1,20 @@ use codeql_extractor::extractor::simple; -use yeast::{build::BuildCtx, rule, DesugaringConfig, PhaseKind}; +use yeast::{rule, DesugaringConfig, PhaseKind}; fn translation_rules() -> Vec { vec![ + // ---- Top-level ---- + // Capture all top-level statements, including unnamed tokens like `nil`. + rule!( + (source_file statement: _* @children) + => + (top_level + body: (block stmt: {..children}) + ) + ), + // Declarations may be wrapped in local/global wrapper nodes. + rule!((global_declaration _ @inner) => {inner}), + rule!((local_declaration _ @inner) => {inner}), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 0afea480a19..32004a0973d 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -50,6 +50,8 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { (x: Int) -> Int in x * 2 }" === Closure with shorthand parameters @@ -82,6 +84,8 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { $0 + $1 }" === Trailing closure @@ -114,6 +118,8 @@ source_file top_level body: + block + stmt: unsupported_node "xs.map { $0 * 2 }" === Closure with capture list @@ -163,6 +169,8 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { [weak self] in self?.doThing() }" === Multi-statement closure @@ -236,3 +244,5 @@ source_file top_level body: + block + stmt: unsupported_node "let f = { (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index afafc1e69ef..69437de0111 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -28,6 +28,8 @@ source_file top_level body: + block + stmt: unsupported_node "let xs = [1, 2, 3]" === Empty array literal with type @@ -68,6 +70,8 @@ source_file top_level body: + block + stmt: unsupported_node "let xs: [Int] = []" === Dictionary literal @@ -106,6 +110,8 @@ source_file top_level body: + block + stmt: unsupported_node "let d = [\"a\": 1, \"b\": 2]" === Set literal @@ -155,6 +161,8 @@ source_file top_level body: + block + stmt: unsupported_node "let s: Set = [1, 2, 3]" === Tuple literal @@ -191,6 +199,8 @@ source_file top_level body: + block + stmt: unsupported_node "let t = (1, \"two\", 3.0)" === Subscript access @@ -232,9 +242,8 @@ source_file top_level body: - unsupported_node "// TODO: tree-sitter-swift parses `xs[0]` as a call_expression (same shape" - unsupported_node "// as `xs(0)`), so the mapping currently produces a call_expr. Update the" - unsupported_node "// parser / add a separate subscript_expr node and remap when fixed." + block + stmt: unsupported_node "let first = xs[0]" === Dictionary subscript @@ -276,8 +285,8 @@ source_file top_level body: - unsupported_node "// TODO: same parser issue as the array subscript case above —" - unsupported_node "// `d[\"key\"]` is parsed as `call_expression(d, (\"key\"))`." + block + stmt: unsupported_node "let v = d[\"key\"]" === Tuple member access @@ -309,3 +318,5 @@ source_file top_level body: + block + stmt: unsupported_node "let n = t.0" diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 600e1126cbf..f621a2ca665 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -35,6 +35,8 @@ source_file top_level body: + block + stmt: unsupported_node "if x > 0 {\n print(x)\n}" === If-else @@ -90,6 +92,8 @@ source_file top_level body: + block + stmt: unsupported_node "if x > 0 {\n print(x)\n} else {\n print(-x)\n}" === If-else-if chain @@ -165,6 +169,8 @@ source_file top_level body: + block + stmt: unsupported_node "if x > 0 {\n print(1)\n} else if x < 0 {\n print(2)\n} else {\n print(3)\n}" === If-let optional binding @@ -207,6 +213,8 @@ source_file top_level body: + block + stmt: unsupported_node "if let value = optional {\n print(value)\n}" === Guard let @@ -240,6 +248,8 @@ source_file top_level body: + block + stmt: unsupported_node "guard let value = optional else { return }" === Ternary expression @@ -277,6 +287,8 @@ source_file top_level body: + block + stmt: unsupported_node "let y = x > 0 ? 1 : -1" === Switch statement @@ -357,6 +369,8 @@ source_file top_level body: + block + stmt: unsupported_node "switch x {\ncase 1:\n print(\"one\")\ncase 2, 3:\n print(\"two or three\")\ndefault:\n print(\"other\")\n}" === Switch with binding pattern @@ -445,3 +459,5 @@ source_file top_level body: + block + stmt: unsupported_node "switch shape {\ncase .circle(let r):\n print(r)\ncase .square(let s):\n print(s)\n}" diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt index 9f9ffeb070a..c4c486e995b 100644 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ b/unified/extractor/tests/corpus/swift/desugar.txt @@ -17,6 +17,8 @@ source_file top_level body: + block + stmt: unsupported_node "1 + 2" === Another additive expression is desugared @@ -37,3 +39,5 @@ source_file top_level body: + block + stmt: unsupported_node "foo + bar" diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index 0a8210a4cf7..5a5832c5450 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -31,6 +31,8 @@ source_file top_level body: + block + stmt: unsupported_node "func greet() {\n print(\"hello\")\n}" === Function with parameters and return type @@ -93,6 +95,8 @@ source_file top_level body: + block + stmt: unsupported_node "func add(_ a: Int, _ b: Int) -> Int {\n return a + b\n}" === Function with named parameters @@ -138,6 +142,8 @@ source_file top_level body: + block + stmt: unsupported_node "func greet(person name: String) {\n print(name)\n}" === Function with default parameter value @@ -185,6 +191,8 @@ source_file top_level body: + block + stmt: unsupported_node "func greet(name: String = \"world\") {\n print(name)\n}" === Variadic function @@ -249,6 +257,8 @@ source_file top_level body: + block + stmt: unsupported_node "func sum(_ values: Int...) -> Int {\n return values.reduce(0, +)\n}" === Function call @@ -276,6 +286,8 @@ source_file top_level body: + block + stmt: unsupported_node "foo(1, 2)" === Function call with labelled arguments @@ -306,6 +318,8 @@ source_file top_level body: + block + stmt: unsupported_node "greet(person: \"Bob\")" === Method call @@ -336,6 +350,8 @@ source_file top_level body: + block + stmt: unsupported_node "list.append(1)" === Generic function @@ -387,3 +403,5 @@ source_file top_level body: + block + stmt: unsupported_node "func identity(_ x: T) -> T {\n return x\n}" diff --git a/unified/extractor/tests/corpus/swift/literals.txt b/unified/extractor/tests/corpus/swift/literals.txt index 5044831a869..53c60b79796 100644 --- a/unified/extractor/tests/corpus/swift/literals.txt +++ b/unified/extractor/tests/corpus/swift/literals.txt @@ -13,6 +13,8 @@ source_file top_level body: + block + stmt: unsupported_node "42" === Negative integer literal @@ -32,6 +34,8 @@ source_file top_level body: + block + stmt: unsupported_node "-7" === Floating-point literal @@ -48,6 +52,8 @@ source_file top_level body: + block + stmt: unsupported_node "3.14" === Boolean literals @@ -67,6 +73,10 @@ source_file top_level body: + block + stmt: + unsupported_node "true" + unsupported_node "false" === Nil literal @@ -83,6 +93,8 @@ source_file top_level body: + block + stmt: unsupported_node "nil" === String literal @@ -101,6 +113,8 @@ source_file top_level body: + block + stmt: unsupported_node "\"hello\"" === String with interpolation @@ -122,3 +136,5 @@ source_file top_level body: + block + stmt: unsupported_node "\"hello \\(name)\"" diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt index 8b9f3410d35..0ce418219fa 100644 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ b/unified/extractor/tests/corpus/swift/loops.txt @@ -37,6 +37,8 @@ source_file top_level body: + block + stmt: unsupported_node "for x in [1, 2, 3] {\n print(x)\n}" === For-in over range @@ -76,6 +78,8 @@ source_file top_level body: + block + stmt: unsupported_node "for i in 0..<10 {\n print(i)\n}" === For-in with where clause @@ -119,6 +123,8 @@ source_file top_level body: + block + stmt: unsupported_node "for x in xs where x > 0 {\n print(x)\n}" === While loop @@ -154,6 +160,8 @@ source_file top_level body: + block + stmt: unsupported_node "while x > 0 {\n x -= 1\n}" === Repeat-while loop @@ -189,6 +197,8 @@ source_file top_level body: + block + stmt: unsupported_node "repeat {\n x -= 1\n} while x > 0" === Break and continue @@ -252,3 +262,5 @@ source_file top_level body: + block + stmt: unsupported_node "for x in xs {\n if x < 0 { continue }\n if x > 100 { break }\n print(x)\n}" diff --git a/unified/extractor/tests/corpus/swift/operators.txt b/unified/extractor/tests/corpus/swift/operators.txt index f1a4a5fcdb2..30726ad873f 100644 --- a/unified/extractor/tests/corpus/swift/operators.txt +++ b/unified/extractor/tests/corpus/swift/operators.txt @@ -17,6 +17,8 @@ source_file top_level body: + block + stmt: unsupported_node "a + b" === Subtraction @@ -37,6 +39,8 @@ source_file top_level body: + block + stmt: unsupported_node "a - b" === Multiplication @@ -57,6 +61,8 @@ source_file top_level body: + block + stmt: unsupported_node "a * b" === Division @@ -77,6 +83,8 @@ source_file top_level body: + block + stmt: unsupported_node "a / b" === Operator precedence: addition and multiplication @@ -101,6 +109,8 @@ source_file top_level body: + block + stmt: unsupported_node "a + b * c" === Parenthesised expression @@ -129,6 +139,8 @@ source_file top_level body: + block + stmt: unsupported_node "(a + b) * c" === Comparison @@ -149,6 +161,8 @@ source_file top_level body: + block + stmt: unsupported_node "a < b" === Equality @@ -169,6 +183,8 @@ source_file top_level body: + block + stmt: unsupported_node "a == b" === Logical and @@ -189,6 +205,8 @@ source_file top_level body: + block + stmt: unsupported_node "a && b" === Logical or @@ -209,6 +227,8 @@ source_file top_level body: + block + stmt: unsupported_node "a || b" === Logical not @@ -228,6 +248,8 @@ source_file top_level body: + block + stmt: unsupported_node "!a" === Range operator @@ -248,3 +270,5 @@ source_file top_level body: + block + stmt: unsupported_node "1...10" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 572e9181a68..e4d0e30f688 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -34,6 +34,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x: Int? = nil" === Optional chaining @@ -74,6 +76,8 @@ source_file top_level body: + block + stmt: unsupported_node "let n = obj?.foo?.bar" === Force unwrap @@ -103,6 +107,8 @@ source_file top_level body: + block + stmt: unsupported_node "let n = opt!" === Nil-coalescing @@ -132,6 +138,8 @@ source_file top_level body: + block + stmt: unsupported_node "let n = opt ?? 0" === Throwing function @@ -167,6 +175,8 @@ source_file top_level body: + block + stmt: unsupported_node "func read() throws -> String {\n return \"\"\n}" === Do-catch @@ -216,6 +226,8 @@ source_file top_level body: + block + stmt: unsupported_node "do {\n try foo()\n} catch {\n print(error)\n}" === Try? expression @@ -252,6 +264,8 @@ source_file top_level body: + block + stmt: unsupported_node "let result = try? foo()" === Try! expression @@ -288,3 +302,5 @@ source_file top_level body: + block + stmt: unsupported_node "let result = try! foo()" diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt index 0bebaa1238f..4eab7971642 100644 --- a/unified/extractor/tests/corpus/swift/types.txt +++ b/unified/extractor/tests/corpus/swift/types.txt @@ -18,6 +18,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Foo {}" === Class with stored properties @@ -79,6 +81,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Point {\n var x: Int\n var y: Int\n}" === Class with initializer @@ -152,6 +156,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Point {\n var x: Int\n init(x: Int) {\n self.x = x\n }\n}" === Class with method @@ -200,6 +206,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Counter {\n var n = 0\n func bump() {\n n += 1\n }\n}" === Class inheritance @@ -228,6 +236,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Dog: Animal {}" === Struct @@ -289,6 +299,8 @@ source_file top_level body: + block + stmt: unsupported_node "struct Point {\n let x: Int\n let y: Int\n}" === Enum with cases @@ -332,6 +344,8 @@ source_file top_level body: + block + stmt: unsupported_node "enum Direction {\n case north\n case south\n case east\n case west\n}" === Enum with associated values @@ -389,6 +403,8 @@ source_file top_level body: + block + stmt: unsupported_node "enum Shape {\n case circle(radius: Double)\n case square(side: Double)\n}" === Protocol declaration @@ -414,6 +430,8 @@ source_file top_level body: + block + stmt: unsupported_node "protocol Drawable {\n func draw()\n}" === Extension @@ -463,6 +481,8 @@ source_file top_level body: + block + stmt: unsupported_node "extension Int {\n func squared() -> Int { return self * self }\n}" === Computed property @@ -555,6 +575,8 @@ source_file top_level body: + block + stmt: unsupported_node "class Rect {\n var w: Double\n var h: Double\n var area: Double {\n return w * h\n }\n}" === Property with getter and setter @@ -639,3 +661,5 @@ source_file top_level body: + block + stmt: unsupported_node "class Box {\n private var _v = 0\n var v: Int {\n get { return _v }\n set { _v = newValue }\n }\n}" diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index 1911ddd02b1..ea3b898f98b 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -23,6 +23,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x = 1" === Var binding @@ -49,6 +51,8 @@ source_file top_level body: + block + stmt: unsupported_node "var x = 1" === Let with type annotation @@ -84,6 +88,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x: Int = 1" === Var without initialiser @@ -118,6 +124,8 @@ source_file top_level body: + block + stmt: unsupported_node "var x: Int" === Tuple destructuring binding @@ -154,6 +162,8 @@ source_file top_level body: + block + stmt: unsupported_node "let (a, b) = pair" === Multiple bindings on one line @@ -185,6 +195,8 @@ source_file top_level body: + block + stmt: unsupported_node "let x = 1, y = 2" === Assignment @@ -207,6 +219,8 @@ source_file top_level body: + block + stmt: unsupported_node "x = 1" === Compound assignment @@ -229,3 +243,5 @@ source_file top_level body: + block + stmt: unsupported_node "x += 1"