| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 80c4b44 commit 97b5a9d
10 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,7 +9,6 @@ members = [ | |||
| 9 | 9 | "shared/yeast-schema", | |
| 10 | 10 | "ruby/extractor", | |
| 11 | 11 | "unified/extractor", | |
| 12 | - "unified/extractor/tree-sitter-swift", | ||
| 13 | 12 | "unified/swift-syntax-rs", | |
| 14 | 13 | "rust/extractor", | |
| 15 | 14 | "rust/extractor/macros", | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,23 +1,32 @@ | |||
| 1 | 1 | # Agent instructions | |
| 2 | 2 | ||
| 3 | - This is a CodeQL extractor based on tree-sitter. | ||
| 3 | + This is a CodeQL extractor that maps a language's parse tree onto a shared AST | ||
| 4 | + using the `yeast` desugaring engine. Swift, the only language so far, is parsed | ||
| 5 | + by Apple's swift-syntax rather than by tree-sitter. | ||
| 4 | 6 | ||
| 5 | 7 | ## Building | |
| 6 | 8 | - To build the extractor, run `scripts/create-extractor-pack.sh` | |
| 7 | 9 | ||
| 8 | 10 | ## Swift Parser | |
| 9 | - - The Swift parser is defined by `extractor/tree-sitter-swift/grammar.js` and can be edited if needed. | ||
| 11 | + - Swift source is parsed by `swift-syntax-parse`, a small Swift/Rust binary in | ||
| 12 | + `swift-syntax-rs` that wraps Apple's swift-syntax and emits the parse tree as | ||
| 13 | + JSON. There is no grammar in this repository to edit. | ||
| 10 | 14 | ||
| 11 | - - After editing the grammar, always run `scripts/regenerate-grammar.sh`. | ||
| 15 | + - `extractor/src/languages/swift/adapter.rs` converts that JSON into a yeast AST. | ||
| 12 | 16 | ||
| 13 | - - The raw parse tree is described by `extractor/tree-sitter-swift/node-types.yml` and should be reviewed after grammar changes. | ||
| 17 | + - The raw parse tree's shape is described by `extractor/swift_node_types.yml`, | ||
| 18 | + which is maintained by hand. | ||
| 14 | 19 | ||
| 15 | 20 | ## AST Mapping | |
| 16 | 21 | - The target AST shape is described by `extractor/ast_types.yml`. | |
| 17 | 22 | ||
| 18 | 23 | - The mapping from the parse tree to the target AST is found in `extractor/src/languages/swift/swift.rs` | |
| 19 | 24 | ||
| 20 | - - To run tests for the parser and mapping, run `cargo test` in the `extractor` directory. | ||
| 25 | + - To run tests for the parser and mapping, run `cargo test` in the `extractor` | ||
| 26 | + directory. The tests need the `swift-syntax-parse` binary: point | ||
| 27 | + `CODEQL_EXTRACTOR_UNIFIED_SWIFT_SYNTAX_PARSE` at it, or put it on `PATH`. | ||
| 28 | + Corpus tests skip themselves when it cannot be found, so check for skips | ||
| 29 | + before concluding a change is clean. | ||
| 21 | 30 | ||
| 22 | 31 | - Extractor test cases are located at `extractor/tests/corpus/swift/*/*.swift`. | |
| 23 | 32 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,6 +20,5 @@ codeql_rust_binary( | |||
| 20 | 20 | ) + [ | |
| 21 | 21 | "//shared/tree-sitter-extractor", | |
| 22 | 22 | "//shared/yeast", | |
| 23 | - "//unified/extractor/tree-sitter-swift", | ||
| 24 | 23 | ], | |
| 25 | 24 | ) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,9 +7,6 @@ edition = "2024" | |||
| 7 | 7 | ||
| 8 | 8 | # When updating these dependencies, run `misc/bazel/3rdparty/update_cargo_deps.sh` | |
| 9 | 9 | [dependencies] | |
| 10 | - tree-sitter = ">= 0.23.0" | ||
| 11 | - tree-sitter-embedded-template = "0.25.0" | ||
| 12 | - tree-sitter-swift = { path = "tree-sitter-swift" } | ||
| 13 | 10 | clap = { version = "4.5", features = ["derive"] } | |
| 14 | 11 | tracing = "0.1" | |
| 15 | 12 | tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -56,9 +56,6 @@ supertypes: | |||
| 56 | 56 | # A statement is anything that can appear in a block. | |
| 57 | 57 | # This type contains all of 'expr' and has partial overlap with 'member'. | |
| 58 | 58 | # For example, type_alias_declaration can appear either as a stmt or member. | |
| 59 | - # constructor_declaration and destructor_declaration appear here because | ||
| 60 | - # tree-sitter-swift's error recovery for #if/#endif in class bodies can place | ||
| 61 | - # init/deinit declarations at the wrong (statement) level. | ||
| 62 | 59 | stmt: | |
| 63 | 60 | - expr | |
| 64 | 61 | - variable_declaration | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,9 +17,8 @@ | |||
| 17 | 17 | //! * Collection nodes are already elided to JSON arrays upstream, so a | |
| 18 | 18 | //! list-valued field maps directly to that field holding several children. | |
| 19 | 19 | //! | |
| 20 | - //! Note: this preserves swift-syntax's own kind/field names. Aligning those | ||
| 21 | - //! names with the tree-sitter-swift schema (so the rewrite rules in | ||
| 22 | - //! [`super::swift`] fire) is done incrementally in the rules. | ||
| 20 | + //! Note: this preserves swift-syntax's own kind/field names; the rewrite rules | ||
| 21 | + //! in [`super::swift`] match those names directly. | ||
| 23 | 22 | ||
| 24 | 23 | use std::collections::BTreeMap; | |
| 25 | 24 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,8 +77,8 @@ fn chained_modifier(ctx: &mut yeast::build::BuildCtx<'_, SwiftContext>) -> Optio | |||
| 77 | 77 | ||
| 78 | 78 | /// Combine a list of boolean sub-conditions into a single expression by | |
| 79 | 79 | /// left-folding with the infix `&&` operator. Used by control-flow | |
| 80 | - /// rules (`if`, `guard`, `while`, `repeat-while`) whose tree-sitter | ||
| 81 | - /// nodes carry one or more comma-separated conditions that the target | ||
| 80 | + /// rules (`if`, `guard`, `while`, `repeat-while`), which carry one or | ||
| 81 | + /// more comma-separated conditions that the target | ||
| 82 | 82 | /// AST represents as a single `condition:` field. Panics on an empty | |
| 83 | 83 | /// input because every caller's grammar guarantees at least one | |
| 84 | 84 | /// condition. | |
@@ -144,7 +144,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 144 | 144 | // ---- Literals ---- | |
| 145 | 145 | // swift-syntax does not distinguish the lexical integer/string forms | |
| 146 | 146 | // (hex/binary/octal, single- vs multi-line, raw): each is a single | |
| 147 | - // `*LiteralExpr` kind, so the tree-sitter variants collapse to one rule. | ||
| 147 | + // `*LiteralExpr` kind, so one rule per literal type suffices. | ||
| 148 | 148 | rule!((integerLiteralExpr) => (int_literal)), | |
| 149 | 149 | rule!((floatLiteralExpr) => (float_literal)), | |
| 150 | 150 | rule!((booleanLiteralExpr) => (boolean_literal)), | |
@@ -169,8 +169,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 169 | 169 | rule!((declReferenceExpr baseName: @name) => (name_expr identifier: (identifier #{name}))), | |
| 170 | 170 | // A discard `_` used as an expression — e.g. the target of a discarding | |
| 171 | 171 | // assignment `_ = x`. swift-syntax models it as a `discardAssignmentExpr`; | |
| 172 | - // the tree-sitter path treated the bare `_` as a name, so map it to a | ||
| 173 | - // `name_expr` too. | ||
| 172 | + // the target AST has no expression-level discard (only `ignore_pattern`, | ||
| 173 | + // which is a pattern), so it becomes a `name_expr` over the `_` token. | ||
| 174 | 174 | rule!((discardAssignmentExpr wildcard: @@w) => (name_expr identifier: (identifier #{w}))), | |
| 175 | 175 | // ---- Operators ---- | |
| 176 | 176 | // The parser front-end folds operator chains into nested | |
@@ -244,10 +244,9 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 244 | 244 | accessor_kind: (accessor_kind "get") | |
| 245 | 245 | body: (block stmt: {body})) | |
| 246 | 246 | ), | |
| 247 | - // A property with an explicit accessor block. The two shapes differ only | ||
| 248 | - // by the presence of an initializer (tree-sitter split them into distinct | ||
| 249 | - // `willset_didset_block` vs computed-accessor node types; swift-syntax | ||
| 250 | - // makes both plain `accessorDecl`s): | ||
| 247 | + // A property with an explicit accessor block. swift-syntax makes both | ||
| 248 | + // shapes plain `accessorDecl`s, so they are told apart by the presence | ||
| 249 | + // of an initializer: | ||
| 251 | 250 | // | |
| 252 | 251 | // * With an initializer (`var x: T = e { willSet {…} didSet {…} }`) it is | |
| 253 | 252 | // a *stored* property with observers: emit the backing | |
@@ -398,8 +397,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 398 | 397 | // payload parameters; an element with a raw value (`case a = 1`) or a | |
| 399 | 398 | // plain element (`case north`) becomes a `variable_declaration`. All | |
| 400 | 399 | // carry the shared case modifiers / chained tag from `ctx` (set by the | |
| 401 | - // `enumCaseDecl` rule below) and are tagged `enum_case` (after any | ||
| 402 | - // `chained_declaration` tag, matching the tree-sitter modifier order). | ||
| 400 | + // `enumCaseDecl` rule below) and are tagged `enum_case`, after any | ||
| 401 | + // `chained_declaration` tag. | ||
| 403 | 402 | rule!( | |
| 404 | 403 | (enumCaseElement name: @name parameterClause: (enumCaseParameterClause parameters: _* @params)) | |
| 405 | 404 | => | |
@@ -432,8 +431,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 432 | 431 | // Enum cases. A single `case` declaration may carry modifiers | |
| 433 | 432 | // (e.g. `indirect`) and list several comma-separated elements; each | |
| 434 | 433 | // becomes its own declaration carrying those shared modifiers, and | |
| 435 | - // non-first ones are tagged `chained_declaration` (mirroring the | ||
| 436 | - // tree-sitter `enum_entry` rule). The modifiers are published into `ctx` | ||
| 434 | + // non-first ones are tagged `chained_declaration`. The modifiers are | ||
| 435 | + // published into `ctx` | ||
| 437 | 436 | // for the element rules above, which build the actual declaration. | |
| 438 | 437 | rule!( | |
| 439 | 438 | (enumCaseDecl modifiers: _* @mods elements: _* @@cases) | |
@@ -530,7 +529,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 530 | 529 | // A function declaration (parameters/return type/body optional). The | |
| 531 | 530 | // parameters and return type nest under `signature`; the body is a | |
| 532 | 531 | // `codeBlock`. A bodyless function (a protocol requirement) still emits | |
| 533 | - // an empty `block`, matching the tree-sitter path. | ||
| 532 | + // an empty `block`. | ||
| 534 | 533 | rule!( | |
| 535 | 534 | (functionDecl | |
| 536 | 535 | name: @name | |
@@ -723,8 +722,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 723 | 722 | condition: {and_chain(&mut ctx, cond)} | |
| 724 | 723 | else: {else_stmts}) | |
| 725 | 724 | ), | |
| 726 | - // Ternary (`c ? a : b`) desugars to an `if_expr`, as in the tree-sitter | ||
| 727 | - // path. | ||
| 725 | + // Ternary (`c ? a : b`) desugars to an `if_expr`. | ||
| 728 | 726 | rule!( | |
| 729 | 727 | (ternaryExpr condition: @cond thenExpression: @then_val elseExpression: @else_val) | |
| 730 | 728 | => | |
@@ -769,8 +767,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 769 | 767 | (pattern_guard_expr pattern: {pat} value: {val}) | |
| 770 | 768 | ), | |
| 771 | 769 | // Optional binding (`if let x = foo`, or shorthand `if let x`) desugars | |
| 772 | - // to a `pattern_guard_expr` matching `Optional.some(x)`, exactly as the | ||
| 773 | - // tree-sitter path does. The initialized form is matched first. | ||
| 770 | + // to a `pattern_guard_expr` matching `Optional.some(x)`. The initialized | ||
| 771 | + // form is matched first. | ||
| 774 | 772 | rule!( | |
| 775 | 773 | (optionalBindingCondition | |
| 776 | 774 | pattern: (identifierPattern identifier: @name) | |
@@ -845,10 +843,12 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 845 | 843 | ), | |
| 846 | 844 | rule!((arrayElement expression: @e) => expr { e }), | |
| 847 | 845 | // A dictionary literal (`["a": 1]`) is kept as an opaque `map_literal` | |
| 848 | - // leaf (its source span), matching the tree-sitter path. | ||
| 846 | + // leaf (its source span). | ||
| 849 | 847 | rule!((dictionaryExpr) => (map_literal)), | |
| 850 | - // A subscript access (`xs[0]`) is modelled as a call, exactly as the | ||
| 851 | - // tree-sitter grammar does (it parses `xs[0]` like `xs(0)`). | ||
| 848 | + // A subscript access (`xs[0]`) is modelled as a call. swift-syntax does | ||
| 849 | + // report a distinct `subscriptCallExpr`, so giving | ||
| 850 | + // subscripts their own shape needs only a `subscript_expr` node in | ||
| 851 | + // ast_types.yml and a remap here. | ||
| 852 | 852 | rule!( | |
| 853 | 853 | (subscriptCallExpr calledExpression: @callee arguments: _* @args) | |
| 854 | 854 | => | |
@@ -898,9 +898,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 898 | 898 | rule!((isExpr expression: @val type: @ty) => (type_test_expr expr: {val} operator: (infix_operator "is") type: {ty})), | |
| 899 | 899 | // Await expression → unary_expr with operator "await" | |
| 900 | 900 | rule!((awaitExpr expression: @val) => (unary_expr operator: (prefix_operator "await") operand: {val})), | |
| 901 | - // Force-unwrap (`x!`) → postfix unary_expr. swift-syntax has a dedicated | ||
| 902 | - // `forceUnwrapExpr` node (the tree-sitter path used the generic postfix | ||
| 903 | - // operator rule instead). | ||
| 901 | + // Force-unwrap (`x!`) → postfix unary_expr, via swift-syntax's dedicated | ||
| 902 | + // `forceUnwrapExpr` node. | ||
| 904 | 903 | rule!((forceUnwrapExpr expression: @e) => (unary_expr operator: (postfix_operator "!") operand: {e})), | |
| 905 | 904 | // ---- Imports ---- | |
| 906 | 905 | // An import declaration. The dotted path (a list of | |
@@ -961,8 +960,8 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 961 | 960 | // A named type (`Int`). `identifierType.name` is the type-name token. | |
| 962 | 961 | rule!((identifierType name: @@n) => (named_type_expr name: (identifier #{n}))), | |
| 963 | 962 | // A qualified type (`Outer.Inner`, `NSString.CompareOptions`). swift-syntax | |
| 964 | - // nests these as `memberType` nodes; like the old tree-sitter `user_type` | ||
| 965 | - // rule, we keep the whole dotted path as the opaque `named_type_expr` name. | ||
| 963 | + // nests these as `memberType` nodes; we keep the whole dotted path as the | ||
| 964 | + // opaque `named_type_expr` name. | ||
| 966 | 965 | rule!((memberType) @ty => (named_type_expr name: (identifier #{ty}))), | |
| 967 | 966 | // Sugared types desugar to `generic_type_expr`: `T?` -> Optional<T>, | |
| 968 | 967 | // `[T]` -> Array<T>, `[K: V]` -> Dictionary<K, V>. | |
@@ -1029,9 +1028,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 1029 | 1028 | // expansions uniformly as a `macroExpansionExpr`). | |
| 1030 | 1029 | rule!((macroExpansionExpr) => (unsupported_node)), | |
| 1031 | 1030 | // A nominal type's `inheritanceClause` (`: Base, Proto`) becomes a list | |
| 1032 | - // of `base_type`s, one per inherited type. The tree-sitter path dropped | ||
| 1033 | - // it (no corpus target had a `base_type`) and the mapping matched that | ||
| 1034 | - // for parity; swift-syntax exposes it cleanly. Each declaration keyword | ||
| 1031 | + // of `base_type`s, one per inherited type. Each declaration keyword | ||
| 1035 | 1032 | // gets its own rule; the bodies are identical but for the keyword. | |
| 1036 | 1033 | // Class declaration with body containing members | |
| 1037 | 1034 | rule!( | |
@@ -1100,8 +1097,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> { | |||
| 1100 | 1097 | // An `extension Foo { … }` is likewise a `class_like_declaration`, named | |
| 1101 | 1098 | // by the extended type. The extended type is captured opaquely (as its | |
| 1102 | 1099 | // source text) so that qualified names (`extension String.Interpolation`, | |
| 1103 | - // a `memberType`) name the declaration just like simple ones, matching the | ||
| 1104 | - // old tree-sitter `user_type` behaviour. | ||
| 1100 | + // a `memberType`) name the declaration just like simple ones. | ||
| 1105 | 1101 | rule!( | |
| 1106 | 1102 | (extensionDecl | |
| 1107 | 1103 | extensionKeyword: @kind | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,6 @@ | |||
| 1 | - // TODO: tree-sitter-swift parses `xs[0]` as a call_expression (same shape | ||
| 2 | - // as `xs(0)`), so the mapping currently produces a call_expr. Update the | ||
| 3 | - // parser / add a separate subscript_expr node and remap when fixed. | ||
| 1 | + // TODO: `xs[0]` is mapped to a call_expr, even though swift-syntax reports a | ||
| 2 | + // distinct subscriptCallExpr. Giving subscripts their own shape needs only a | ||
| 3 | + // subscript_expr node in ast_types.yml and a remap. | ||
| 4 | 4 | let first = xs[0] | |
| 5 | 5 | ||
| 6 | 6 | --- | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - // TODO: tree-sitter-swift parses `xs[0]` as a call_expression (same shape | ||
| 2 | - // as `xs(0)`), so the mapping currently produces a call_expr. Update the | ||
| 3 | - // parser / add a separate subscript_expr node and remap when fixed. | ||
| 1 | + // TODO: `xs[0]` is mapped to a call_expr, even though swift-syntax reports a | ||
| 2 | + // distinct subscriptCallExpr. Giving subscripts their own shape needs only a | ||
| 3 | + // subscript_expr node in ast_types.yml and a remap. | ||
| 4 | 4 | let first = xs[0] | |
| Back | FazBrowse Home | New Git URL |
0 commit comments