|
1 | 1 | use codeql_extractor::extractor::simple; |
2 | | -use yeast::{rule, DesugaringConfig, PhaseKind}; |
| 2 | +use yeast::{build::BuildCtx, rule, DesugaringConfig, PhaseKind}; |
| 3 | + |
| 4 | +/// Names of output AST kinds that belong to the `expr` supertype. Kept in |
| 5 | +/// sync with `ast_types.yml`. `unsupported_node` is intentionally omitted |
| 6 | +/// because it is also a member of the `stmt` supertype. |
| 7 | +const EXPR_KINDS: &[&str] = &[ |
| 8 | + "name_expr", |
| 9 | + "int_literal", |
| 10 | + "string_literal", |
| 11 | + "binary_expr", |
| 12 | + "unary_expr", |
| 13 | + "call_expr", |
| 14 | + "member_access_expr", |
| 15 | + "lambda_expr", |
| 16 | +]; |
| 17 | + |
| 18 | +/// If `id` is an `expr`, wrap it in `expr_stmt` so it can sit in a `stmt` |
| 19 | +/// position; otherwise return it unchanged. |
| 20 | +fn wrap_expr_in_stmt(ctx: &mut BuildCtx, id: usize) -> usize { |
| 21 | + let kind = ctx.ast.get_node(id).map(|n| n.kind()).unwrap_or(""); |
| 22 | + if EXPR_KINDS.contains(&kind) { |
| 23 | + yeast::tree!(ctx, (expr_stmt expr: {id})) |
| 24 | + } else { |
| 25 | + id |
| 26 | + } |
| 27 | +} |
3 | 28 |
|
4 | 29 | fn translation_rules() -> Vec<yeast::Rule> { |
5 | 30 | vec![ |
@@ -149,11 +174,44 @@ fn translation_rules() -> Vec<yeast::Rule> { |
149 | 174 | // ---- Block / statement wrapping ---- |
150 | 175 | // A `(statements ...)` node corresponds to a brace-delimited block. |
151 | 176 | // Each child is mapped through translation; bare expression results |
152 | | - // get wrapped in `expr_stmt`. |
| 177 | + // get wrapped in `expr_stmt` so they fit the `body*: stmt` field. |
153 | 178 | rule!( |
154 | 179 | (statements (_)* @stmts) |
155 | 180 | => |
156 | | - (block_stmt body: {..stmts}) |
| 181 | + (block_stmt body: {..stmts.iter().copied().map(|n| |
| 182 | + wrap_expr_in_stmt(&mut __yeast_ctx, n.into()) |
| 183 | + ).collect::<Vec<usize>>()}) |
| 184 | + ), |
| 185 | + // ---- Calls and member access ---- |
| 186 | + // Member access, e.g. `obj.member`. The Swift parser wraps the |
| 187 | + // member name as `(navigation_suffix suffix: (simple_identifier))`. |
| 188 | + rule!( |
| 189 | + (navigation_expression |
| 190 | + target: (_) @target |
| 191 | + suffix: (navigation_suffix |
| 192 | + suffix: (simple_identifier) @member)) |
| 193 | + => |
| 194 | + (member_access_expr |
| 195 | + target: {target} |
| 196 | + member: (identifier #{member})) |
| 197 | + ), |
| 198 | + // Function / method call. The callee is the first child of |
| 199 | + // `call_expression`; the second is a `call_suffix` whose |
| 200 | + // `value_arguments` (if present) hold the parenthesized args. A |
| 201 | + // trailing closure (`call_suffix` with a `lambda_literal` child) |
| 202 | + // is appended as a final argument. |
| 203 | + rule!( |
| 204 | + (call_expression |
| 205 | + (_) @callee |
| 206 | + (call_suffix |
| 207 | + (value_arguments |
| 208 | + (value_argument value: (_) @args)*)? |
| 209 | + (lambda_literal)? @trailing)) |
| 210 | + => |
| 211 | + (call_expr |
| 212 | + function: {callee} |
| 213 | + argument: {..args.iter().copied().map(Into::into) |
| 214 | + .chain(trailing.map(Into::into)).collect::<Vec<usize>>()}) |
157 | 215 | ), |
158 | 216 | // ---- Guard statement ---- |
159 | 217 | // `guard let x = e else { ... }` — currently only handles the |
|
0 commit comments