From 28c879f58cefa5ac1332098d4a7a03203cea1511 Mon Sep 17 00:00:00 2001 From: Asger F Date: Sat, 30 May 2026 07:18:13 +0200 Subject: [PATCH 01/29] yeast-macros: add .map(p -> tpl) chain syntax for tree templates After a {expr} or {..expr} placeholder, an optional chain of .() calls may follow. Currently the only builtin is: .map(param -> template) which applies the template to each element of the iterable and collects the resulting node IDs. A chain auto-splices into the enclosing field/child position. Example: path: {parts}.map(p -> (identifier #{p})) The framework is extensible: additional builtins can be added by matching on the method name in parse_chain_suffix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/lib.rs | 6 ++ shared/yeast-macros/src/parse.rs | 107 ++++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 17 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 1d7236b500a9..3945ddd3e27f 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -44,8 +44,14 @@ pub fn query(input: TokenStream) -> TokenStream { /// {expr} - embed a Rust expression returning Id /// {..expr} - splice an iterable of Id (in child/field position) /// field: {..expr} - splice into a named field +/// {expr}.map(p -> tpl) - apply tpl to each element; splice result /// ``` /// +/// Chain syntax after `{expr}` or `{..expr}`: +/// - `.map(param -> template)` — produces one node per element of the iterable. +/// The lambda parameter is bound in `template` (e.g. `{parts}.map(p -> (identifier #{p}))`). +/// - Chains always splice (the result is iterable). +/// /// Can be called with an explicit context or using the implicit context /// from an enclosing `rule!`: /// diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index eb3b161b295a..a02538ffec9e 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -419,23 +419,35 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result into the field + // Check for field: {..expr}.chain or field: {expr}.chain — splice a Vec into the field if peek_is_group(tokens, Delimiter::Brace) { let group_clone = tokens.clone().next().unwrap(); if let TokenTree::Group(g) = &group_clone { let mut inner_check = g.stream().into_iter(); let is_splice = matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.') && matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); - if is_splice { + // Determine if a chain (.map(..)) follows the `{}` group. + let mut after = tokens.clone(); + after.next(); // skip the brace group + let has_chain = matches!(after.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.'); + + if is_splice || has_chain { let group = expect_group(tokens, Delimiter::Brace)?; - let mut inner = group.stream().into_iter().peekable(); - inner.next(); // consume first . - inner.next(); // consume second . - let expr: proc_macro2::TokenStream = inner.collect(); + let base: TokenStream = if is_splice { + let mut inner = group.stream().into_iter().peekable(); + inner.next(); // consume first . + inner.next(); // consume second . + let expr: TokenStream = inner.collect(); + quote! { + (#expr).into_iter().map(::std::convert::Into::::into) + } + } else { + let expr = group.stream(); + quote! { (#expr).into_iter() } + }; + let chained = parse_chain_suffix(tokens, ctx, base)?; stmts.push(quote! { - let #temp: Vec = (#expr).into_iter() - .map(::std::convert::Into::::into) - .collect(); + let #temp: Vec = #chained.collect(); }); // An empty splice means the field is absent — skip it // entirely rather than emitting an empty named field. @@ -472,6 +484,58 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result template) -- iterator map: produces Vec +/// ``` +/// +/// The chain may be empty (returns `base` unchanged). Multiple chained calls +/// are supported, e.g. `.map(p -> ...).map(q -> ...)`. +/// +/// Each call expects the receiver to be an iterator. The `base` argument +/// should therefore already be an iterator (use `.into_iter()` on it before +/// calling this function). +fn parse_chain_suffix( + tokens: &mut Tokens, + ctx: &Ident, + base: TokenStream, +) -> Result { + let mut current = base; + while matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.') { + tokens.next(); // consume . + let method = expect_ident(tokens, "expected method name after `.`")?; + let method_str = method.to_string(); + let args_group = expect_group(tokens, Delimiter::Parenthesis)?; + match method_str.as_str() { + "map" => { + let mut inner = args_group.stream().into_iter().peekable(); + let param = expect_ident(&mut inner, "expected lambda parameter name")?; + expect_punct(&mut inner, '-', "expected `->` after lambda parameter")?; + expect_punct(&mut inner, '>', "expected `->` after lambda parameter")?; + let body = parse_direct_node(&mut inner, ctx)?; + if let Some(tok) = inner.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after lambda body", + )); + } + current = quote! { + #current.map(|#param| #body) + }; + } + _ => { + return Err(syn::Error::new_spanned( + method, + format!("unknown builtin method `.{method_str}()`"), + )); + } + } + } + Ok(current) +} + /// Parse the top-level list of a `trees!` template. /// Each item is a node template or `{expr}` splice. fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result> { @@ -492,18 +556,27 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result::into) - ); + } + } else { + let expr = group.stream(); + quote! { (#expr).into_iter() } + }; + let chained = parse_chain_suffix(tokens, ctx, base)?; + items.push(quote! { + __nodes.extend(#chained); }); } else { let expr = group.stream(); From 00068948c16c229645a8812dace2a0f76f3f3bf7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Sat, 30 May 2026 07:27:51 +0200 Subject: [PATCH 02/29] yeast-macros: add .reduce_left(first -> init, acc, elem -> fold) chain A left fold over an iterable where the first element seeds the accumulator: - first -> init : converts the first element to the initial accumulator - acc, elem -> fold : fold step; acc = current accumulator, elem = next element - Empty iterable produces nothing (0-element splice) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/lib.rs | 9 +++++-- shared/yeast-macros/src/parse.rs | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/shared/yeast-macros/src/lib.rs b/shared/yeast-macros/src/lib.rs index 3945ddd3e27f..07077be51f04 100644 --- a/shared/yeast-macros/src/lib.rs +++ b/shared/yeast-macros/src/lib.rs @@ -45,12 +45,17 @@ pub fn query(input: TokenStream) -> TokenStream { /// {..expr} - splice an iterable of Id (in child/field position) /// field: {..expr} - splice into a named field /// {expr}.map(p -> tpl) - apply tpl to each element; splice result +/// {expr}.reduce_left(f -> init, acc, e -> fold) +/// - fold with per-element init; splice 0 or 1 result /// ``` /// /// Chain syntax after `{expr}` or `{..expr}`: -/// - `.map(param -> template)` — produces one node per element of the iterable. -/// The lambda parameter is bound in `template` (e.g. `{parts}.map(p -> (identifier #{p}))`). +/// - `.map(param -> template)` — one output node per input element. +/// - `.reduce_left(first -> init, acc, elem -> fold)` — fold left; the first +/// element is converted by `init`, subsequent elements are folded by `fold` +/// with the accumulator bound to `acc`. An empty iterable yields nothing. /// - Chains always splice (the result is iterable). +/// - Multiple chains can be chained, e.g. `.map(...).reduce_left(...)`. /// /// Can be called with an explicit context or using the implicit context /// from an enclosing `rule!`: diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index a02538ffec9e..5ee3156bcd41 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -525,6 +525,46 @@ fn parse_chain_suffix( #current.map(|#param| #body) }; } + "reduce_left" => { + // Syntax: reduce_left(first -> init_tpl, acc, elem -> fold_tpl) + // - first -> init_tpl : converts the first element to the initial accumulator + // - acc, elem -> fold_tpl : fold step (acc = current accumulator, elem = next element) + // Empty iterator produces an empty iterator; non-empty produces a single-element iterator. + let mut inner = args_group.stream().into_iter().peekable(); + let init_param = expect_ident(&mut inner, "expected initial lambda parameter")?; + expect_punct(&mut inner, '-', "expected `->` after init parameter")?; + expect_punct(&mut inner, '>', "expected `->` after init parameter")?; + let init_body = parse_direct_node(&mut inner, ctx)?; + expect_punct(&mut inner, ',', "expected `,` after init template")?; + let acc_param = expect_ident(&mut inner, "expected accumulator parameter")?; + expect_punct(&mut inner, ',', "expected `,` after accumulator parameter")?; + let elem_param = expect_ident(&mut inner, "expected element parameter")?; + expect_punct(&mut inner, '-', "expected `->` after element parameter")?; + expect_punct(&mut inner, '>', "expected `->` after element parameter")?; + let fold_body = parse_direct_node(&mut inner, ctx)?; + if let Some(tok) = inner.next() { + return Err(syn::Error::new_spanned( + tok, + "unexpected token after fold template", + )); + } + current = quote! { + { + let mut __iter = #current; + let __result: Option = if let Some(#init_param) = __iter.next() { + let mut __acc: usize = #init_body; + for #elem_param in __iter { + let #acc_param: usize = __acc; + __acc = #fold_body; + } + Some(__acc) + } else { + None + }; + __result.into_iter() + } + }; + } _ => { return Err(syn::Error::new_spanned( method, From ddc9516e920daaff896c344fa9416563fa0d819b Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 13:15:22 +0200 Subject: [PATCH 03/29] Yeast: better support for rewriting unnamed nodes - Ensure the full wildcard _ supports quantifiers - Also rewrite unnamed nodes in one-shot phases --- shared/yeast-macros/src/parse.rs | 9 +++++++-- shared/yeast/src/lib.rs | 8 -------- shared/yeast/tests/test.rs | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 5ee3156bcd41..169b1c91c0c4 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -259,6 +259,7 @@ fn parse_query_list(tokens: &mut Tokens) -> Result> { yeast::query::QueryListElem::SingleNode(#node) }, )?; + let elem = maybe_wrap_list_capture(tokens, elem)?; elems.push(elem); continue; } @@ -276,6 +277,7 @@ fn parse_query_list(tokens: &mut Tokens) -> Result> { yeast::query::QueryListElem::SingleNode(#node) }, )?; + let elem = maybe_wrap_list_capture(tokens, elem)?; elems.push(elem); continue; } @@ -717,8 +719,11 @@ fn extract_captures_inner( } last_mult = CaptureMultiplicity::Single; } - TokenTree::Punct(p) if matches!(p.as_char(), '*' | '+' | '?') => { - // Keep last_mult — the @capture follows + TokenTree::Punct(p) if p.as_char() == '*' || p.as_char() == '+' => { + last_mult = CaptureMultiplicity::Repeated; + } + TokenTree::Punct(p) if p.as_char() == '?' => { + last_mult = CaptureMultiplicity::Optional; } _ => { last_mult = CaptureMultiplicity::Single; diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 0717d27e4b8b..63ad76625bb9 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -825,14 +825,6 @@ fn apply_one_shot_rules_inner( let node_kind = ast.get_node(id).map(|n| n.kind()).unwrap_or(""); - // Don't rewrite unnamed nodes (punctuation, keywords, etc.); leave them - // as-is. Rules target named nodes only. - if let Some(node) = ast.get_node(id) { - if !node.is_named() { - return Ok(vec![id]); - } - } - for rule in index.rules_for_kind(node_kind) { if let Some(mut captures) = rule.try_match(ast, id)? { // Recursively translate every captured node before invoking the diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 7645f3776f87..3ae8e2072d9f 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -389,6 +389,29 @@ fn test_capture_unnamed_node_parenthesized() { assert!(!op_node.is_named()); } +#[test] +fn test_capture_bare_underscore_repeated() { + // `_` matches named and unnamed nodes in bare-child position. On this + // assignment shape, bare children correspond to unnamed tokens (the `=`). + let runner = Runner::new(tree_sitter_ruby::LANGUAGE.into(), &[]); + let ast = runner.run("x = 1").unwrap(); + + let query = yeast::query!((assignment _* @all)); + + let mut cursor = AstCursor::new(&ast); + cursor.goto_first_child(); + let assignment_id = cursor.node_id(); + + let mut captures = yeast::captures::Captures::new(); + let matched = query.do_match(&ast, assignment_id, &mut captures).unwrap(); + assert!(matched); + + let all = captures.get_all("all"); + assert_eq!(all.len(), 1); + assert_eq!(ast.get_node(all[0]).unwrap().kind(), "="); + assert!(!ast.get_node(all[0]).unwrap().is_named()); +} + #[test] fn test_capture_unnamed_node_bare_literal() { // `"=" @op` (without surrounding parens) is the same as `("=") @op`. From d11b428292169ffb1b3999efda149dadb42df40f Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 4 Jun 2026 13:35:53 +0200 Subject: [PATCH 04/29] yeast-macros: desugar 'field: @cap' to 'field: _ @cap' When a field pattern has a bare capture with no preceding pattern atom (i.e. `foo: @bar`), implicitly use a true wildcard (`_`, match_unnamed: true) as the node pattern, making it equivalent to `foo: _ @bar`. This is a convenience shorthand: in practice every `field: _ @cap` in the Swift rules can now be written more concisely as `field: @cap`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- shared/yeast-macros/src/parse.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 169b1c91c0c4..0980d41f9567 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -141,7 +141,12 @@ fn parse_query_fields(tokens: &mut Tokens) -> Result> { // Parse the field's pattern. To support repetition like // `field: (kind)* @cap`, parse the atom first, then check for // a quantifier, and lastly handle a trailing `@capture`. - let atom = parse_query_atom(tokens)?; + // `field: @cap` is sugar for `field: _ @cap`. + let atom = if peek_is_at(tokens) { + quote! { yeast::query::QueryNode::Any { match_unnamed: true } } + } else { + parse_query_atom(tokens)? + }; if peek_is_repetition(tokens) { let rep = expect_repetition(tokens)?; let elem = quote! { From 0baa1264731e64196f8e7f5bb411af4d3e8b777a Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:54:46 +0200 Subject: [PATCH 05/29] Add ability to prepend fields in Yeast --- shared/yeast/src/build.rs | 9 +++++++++ shared/yeast/src/lib.rs | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/shared/yeast/src/build.rs b/shared/yeast/src/build.rs index bee4c4f7d034..5b018ee8edc5 100644 --- a/shared/yeast/src/build.rs +++ b/shared/yeast/src/build.rs @@ -88,4 +88,13 @@ impl<'a> BuildCtx<'a> { self.ast .create_named_token_with_range(kind, generated, self.source_range) } + + /// Prepend a value to a field of an existing node. + pub fn prepend_field(&mut self, node_id: Id, field_name: &str, value_id: Id) { + let field_id = self + .ast + .field_id_for_name(field_name) + .unwrap_or_else(|| panic!("build: field '{field_name}' not found")); + self.ast.prepend_field_child(node_id, field_id, value_id); + } } diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 63ad76625bb9..40b90567537c 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -387,6 +387,12 @@ impl Ast { self.create_named_token_with_range(kind, content, None) } + /// Prepend a child id to the given field of the given node. + pub fn prepend_field_child(&mut self, node_id: Id, field_id: FieldId, value_id: Id) { + let node = self.nodes.get_mut(node_id).expect("prepend_field_child: invalid node id"); + node.fields.entry(field_id).or_default().insert(0, value_id); + } + pub fn create_named_token_with_range( &mut self, kind: &'static str, From 1d8e682e5f49df9fdbb921188e27dfc1d1d8c2ec Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:39:22 +0200 Subject: [PATCH 06/29] Reset mappings --- .../extractor/src/languages/swift/swift.rs | 332 ------------------ 1 file changed, 332 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 595f56a0b39a..d7ad7d871294 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -1,340 +1,8 @@ use codeql_extractor::extractor::simple; use yeast::{build::BuildCtx, rule, DesugaringConfig, PhaseKind}; -/// Names of output AST kinds that belong to the `expr` supertype. Kept in -/// sync with `ast_types.yml`. `unsupported_node` is intentionally omitted -/// because it is also a member of the `stmt` supertype. -const EXPR_KINDS: &[&str] = &[ - "name_expr", - "int_literal", - "string_literal", - "binary_expr", - "unary_expr", - "call_expr", - "member_access_expr", - "lambda_expr", -]; - -/// If `id` is an `expr`, wrap it in `expr_stmt` so it can sit in a `stmt` -/// position; otherwise return it unchanged. -fn wrap_expr_in_stmt(ctx: &mut BuildCtx, id: usize) -> usize { - let kind = ctx.ast.get_node(id).map(|n| n.kind()).unwrap_or(""); - if EXPR_KINDS.contains(&kind) { - yeast::tree!(ctx, (expr_stmt expr: {id})) - } else { - id - } -} - fn translation_rules() -> Vec { vec![ - rule!( - (source_file (_)* @children) - => - (top_level - body: {..children} - ) - ), - // ---- Binary expressions ---- - // Swift's parser produces a different node kind for each operator - // family, but the field shape (`lhs` / `op` / `rhs`) is uniform, so - // each maps onto `binary_expr`. - rule!( - (additive_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (multiplicative_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (comparison_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (equality_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (conjunction_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (disjunction_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (nil_coalescing_expression - lhs: (_) @left - op: _ @operator - rhs: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - rule!( - (range_expression - start: (_) @left - op: _ @operator - end: (_) @right) - => - (binary_expr - left: {left} - operator: (operator #{operator}) - right: {right}) - ), - // ---- Unary expressions ---- - rule!( - (prefix_expression - operation: _ @operator - target: (_) @operand) - => - (unary_expr - operand: {operand} - operator: (operator #{operator})) - ), - // ---- Identifiers / name expressions ---- - rule!( - (simple_identifier) @name - => - (name_expr - identifier: (identifier #{name})) - ), - // ---- Literals ---- - rule!( - (integer_literal) @lit - => - (int_literal #{lit}) - ), - // String literals: render the *raw* source text, including the - // surrounding quotes. Interpolations (e.g. `"hi \(x)"`) are not - // yet broken out into structured pieces \u2014 they show up as part - // of the literal's source text. - rule!( - (line_string_literal) @lit - => - (string_literal #{lit}) - ), // ---- Lambdas / closures ---- - // Map a `lambda_literal` whose body is a single statement to - // `lambda_expr`. Multi-statement bodies fall through to - // `unsupported_node` because `lambda_expr.body` is single-valued - // in the current `ast_types.yml`. Parameters from explicit-typed - // closures (`{ (x: Int) -> Int in ... }`) are not yet captured. - rule!( - (lambda_literal - (statements (_) @body)) - => - (lambda_expr - body: {body}) - ), - // ---- Block / statement wrapping ---- - // A `(statements ...)` node corresponds to a brace-delimited block. - // Each child is mapped through translation; bare expression results - // get wrapped in `expr_stmt` so they fit the `body*: stmt` field. - rule!( - (statements (_)* @stmts) - => - (block_stmt body: {..stmts.iter().copied().map(|n| - wrap_expr_in_stmt(&mut __yeast_ctx, n.into()) - ).collect::>()}) - ), - // ---- Calls and member access ---- - // Member access, e.g. `obj.member`. The Swift parser wraps the - // member name as `(navigation_suffix suffix: (simple_identifier))`. - rule!( - (navigation_expression - target: (_) @target - suffix: (navigation_suffix - suffix: (simple_identifier) @member)) - => - (member_access_expr - target: {target} - member: (identifier #{member})) - ), - // Function / method call. The callee is the first child of - // `call_expression`; the second is a `call_suffix` whose - // `value_arguments` (if present) hold the parenthesized args. A - // trailing closure (`call_suffix` with a `lambda_literal` child) - // is appended as a final argument. - rule!( - (call_expression - (_) @callee - (call_suffix - (value_arguments - (value_argument value: (_) @args)*)? - (lambda_literal)? @trailing)) - => - (call_expr - function: {callee} - argument: {..args} - argument: {..trailing} - ) - ), - // ---- Guard statement ---- - // `guard let x = e else { ... }` — currently only handles the - // let-binding form. The Swift parser models the `let` keyword as a - // `value_binding_pattern` child of `condition`, followed by an - // unnamed `=` and the source expression. - rule!( - (guard_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (else) - (statements) @else_branch) - => - (guard_if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - else: {else_branch}) - ), - // ---- If statement ---- - // if-let binding (with optional else branch). The Swift parser puts - // the bound name in `bound_identifier`, the `let` keyword as a - // `value_binding_pattern` child of `condition`, and the source - // expression as a separate child of `condition`. - rule!( - (if_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (statements) @then - (else) - (_) @else_branch) - => - (if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - then: {then} - else: {else_branch}) - ), - rule!( - (if_statement - bound_identifier: (simple_identifier) @id - condition: (value_binding_pattern) - condition: (_) @value - (statements) @then) - => - (if_stmt - condition: (let_pattern_condition - pattern: (var_pattern identifier: (identifier #{id})) - value: {value}) - then: {then}) - ), - // With explicit else branch (block or chained if). - rule!( - (if_statement - condition: (_) @cond - (statements) @then - (else) - (_) @else_branch) - => - (if_stmt - condition: (expr_condition expr: {cond}) - then: {then} - else: {else_branch}) - ), - // Without else branch. - rule!( - (if_statement - condition: (_) @cond - (statements) @then) - => - (if_stmt - condition: (expr_condition expr: {cond}) - then: {then}) - ), // ---- Patterns ---- - // The Swift parser uses a `pattern` node with a `bound_identifier` - // field for simple bindings such as `let x = ...`. - rule!( - (pattern bound_identifier: (simple_identifier) @id) - => - (var_pattern - identifier: (identifier #{id})) - ), - // Inside tuple patterns, the inner `pattern` node holds a bare - // `simple_identifier` (with no `bound_identifier` field). - rule!( - (pattern (simple_identifier) @id) - => - (var_pattern - identifier: (identifier #{id})) - ), - // Tuple destructuring pattern, e.g. `let (a, b) = pair`. The parser - // emits a `pattern` node whose unnamed children are themselves - // `pattern` nodes. - rule!( - (pattern (pattern)+ @parts) - => - (tuple_pattern element: {..parts}) - ), - // ---- Variable declarations ---- - // Handles single (`let x = e`), multiple (`let x = 1, y = 2`), - // and uninitialized (`var x: T`) bindings. - rule!( - (property_declaration - name: (_)* @pats - value: (_)* @vals) - => - (variable_declaration_stmt - variable_declarator: {..pats.iter().enumerate().map(|(i, &pat)| { - match vals.get(i).copied() { - Some(val) => yeast::tree!( - (variable_declarator - pattern: {pat} - value: {val})), - None => yeast::tree!( - (variable_declarator - pattern: {pat})), - } - })}) - ), // ---- Fallbacks ---- rule!( (_) From 6dd7dedc19a91a4d85fae1ebe070fbd0d36c630e Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:54:16 +0200 Subject: [PATCH 07/29] Rewrite AST --- unified/extractor/ast_types.yml | 561 ++++++++++++++++++++++++++++---- 1 file changed, 496 insertions(+), 65 deletions(-) diff --git a/unified/extractor/ast_types.yml b/unified/extractor/ast_types.yml index 22a5e8b19fb8..b6b3a5e17014 100644 --- a/unified/extractor/ast_types.yml +++ b/unified/extractor/ast_types.yml @@ -2,36 +2,103 @@ supertypes: expr: - name_expr - int_literal + - float_literal + - boolean_literal - string_literal + - regex_literal + - builtin_expr - binary_expr - unary_expr - call_expr - member_access_expr - - lambda_expr - - unsupported_node - stmt: - - empty_stmt - - block_stmt - - expr_stmt - - if_stmt - - variable_declaration_stmt - - guard_if_stmt - - unsupported_node - condition: - - expr_condition - - let_pattern_condition - - sequence_condition + - super_expr + - function_expr + - array_literal + - map_literal + - key_value_pair + - tuple_expr + - type_cast_expr + - type_test_expr + - if_expr + - assign_expr + - compound_assign_expr + - pattern_guard_expr + - empty_expr + - block + - break_expr + - continue_expr + - return_expr + - throw_expr + - try_expr + - switch_expr - unsupported_node + expr_or_pattern: + - expr + - pattern + expr_or_type: + - expr + - type_expr pattern: - - var_pattern - - apply_pattern + - name_pattern - tuple_pattern + - constructor_pattern - ignore_pattern + - expr_equality_pattern + - bulk_importing_pattern - unsupported_node + # A statement is anything that can appear in a block. + # This type contains all of 'expr' and has partial overlap with 'member'. + # For example, type_alias_declaration can appear either as a stmt or member. + # constructor_declaration and destructor_declaration appear here because + # tree-sitter-swift's error recovery for #if/#endif in class bodies can place + # init/deinit declarations at the wrong (statement) level. + stmt: + - expr + - variable_declaration + - type_alias_declaration + - function_declaration + - import_declaration + - operator_syntax_declaration + - class_like_declaration + - accessor_declaration + - constructor_declaration + - destructor_declaration + - guard_if_stmt + - for_each_stmt + - while_stmt + - do_while_stmt + - labeled_stmt + # A member is anything that can appear in the body of a class-like declaration + member: + - constructor_declaration + - destructor_declaration + - function_declaration + - variable_declaration + - accessor_declaration + - initializer_declaration + - class_like_declaration + - type_alias_declaration + - associated_type_declaration + - unsupported_node + type_expr: + - named_type_expr + - generic_type_expr + - tuple_type_expr + - function_type_expr + - inferred_type_expr + - unsupported_node + type_constraint: + - equality_type_constraint + - bound_type_constraint + operator: + - infix_operator + - prefix_operator + - postfix_operator named: - # Top-level is the root node, currently containing a list of expressions + # Top-level is the root node, containing a single block of statements + # (which are themselves expressions or declarations). top_level: - body*: [expr, stmt] + body: block # An identifier used in the context of an expression name_expr: @@ -40,13 +107,28 @@ named: # An integer literal int_literal: + # A floating-point literal + float_literal: + + # A boolean literal + boolean_literal: + + # A literal backed by a keyword such as `nil`, `null`, or `nullptr`. + # + # Altough nil/null are keyword literals in many languages there should be + # no attempt to normalize "null-like" named entities, like Python's `None`. + builtin_expr: + # A string literal string_literal: + # A regex literal + regex_literal: + # Application of a binary operator, such as `a + b` binary_expr: left: expr - operator: operator + operator: infix_operator right: expr # Application of a unary operator, such as `!x` @@ -54,86 +136,310 @@ named: operand: expr operator: operator - # A function or method call, such as `f(x)` or `obj.m(x)`. Method calls - # are represented as a call whose `function` is a `member_access_expr`. + # Plain assignment + assign_expr: + target: expr_or_pattern + value: expr + + # Compound assignment + compound_assign_expr: + target: expr + operator: infix_operator + value: expr + + # A function or method call, such as `f(x)` or `obj.m(x)`. + # + # Method calls are represented as a call whose `function` is a `member_access_expr`. + # + # Constructor calls are marked by a language-specific modifier, and the target may be + # a `type_expr` if the parser can deduce that the target is a type. call_expr: - function: expr - argument*: expr + modifier*: modifier + callee: expr_or_type + argument*: argument + + argument: + modifier*: modifier + name?: identifier + value: expr # Member access, such as `obj.member`. + # + # The base may be a type expression when it is a static member access like `Array.method`. + # In ambiguous cases where the parser cannot distinguish static and instance member access, the base + # will be typically be an expression. + # + # For `super.x` the base will be an instance of `super_expr`. member_access_expr: - target: expr + base: expr_or_type member: identifier - lambda_expr: + # A type expression that refers to a type inferred from the contextual type. + # This is used to translate Swift's leading-dot syntax, `.foo`, which means `T.foo` where + # `T` is the contextual type of some enclosing expression. This is translated to a member_access + # with an inferred_type_expr as the base. + inferred_type_expr: + + # A `super` token, which can usually only appear as the base of member access. + super_expr: + + function_expr: + modifier*: modifier + capture_declaration*: variable_declaration parameter*: parameter - body: [expr, stmt] + return_type?: type_expr + body: block - # A parameter - parameter: - pattern: pattern + array_literal: + element*: expr - empty_stmt: + map_literal: + element*: expr - block_stmt: - body*: stmt + # A key-value pair, usually appearing as a named argument or as part of a map literal. + # + # For some languages, the key-value pair is a first class value and this type of expression + # may thus appear anywhere in the general case. + key_value_pair: + key: expr + value: expr - expr_stmt: - expr: expr + # A tuple expression, such as `(a, b, c)`. + tuple_expr: + element*: expr - if_stmt: - condition: condition - then?: stmt - else?: stmt + # A parameter. + # + # `type` is its declared type annotation (if any) + # + # `pattern` binds the parameter's internal name(s). For a simple parameter this is a + # `name_pattern`, but may be an arbitrary pattern for languages where patterns may appear + # in the parameter list. + # + # `external_name` is the name by which to call sites refer to the parameter, if the parameter + # can be passed as a named parameter. For example, the Swift function `func greet(person id: String)` + # would have `person` as the external name and a `name_pattern` wrapping `id` is the parameter's pattern. + parameter: + modifier*: modifier + external_name?: identifier + type?: type_expr + pattern?: pattern + default?: expr + + # An expression that does nothing. Used where the grammar permits an + # empty statement (e.g. a stray `;`). + empty_expr: - variable_declaration_stmt: - variable_declarator+: variable_declarator + # A brace-delimited sequence of statements (`{ ... }`). Blocks are the + # only nodes that can directly contain statements; every other body-like + # field holds a single `block`. + block: + stmt*: stmt - # A variable declaration, or assignment to a pattern. - # The initializer is optional (but typically only possible in combination with a simple variable pattern). - variable_declarator: + if_expr: + condition: expr + then?: expr + else?: expr + + # A variable declaration or destructuring assignment that introduces new variables. + # + # Any occurrence of `var_patterns` in 'pattern' result in fresh bindings that are + # in scope for the rest of the enclosing block. + # + # The initializer is optional (but typically cannot be omitted if combined with a non-trivial pattern). + # + # Modifiers should include 'var', 'let', 'const', etc, if they are significant. + # A grouped declaration like `let x = 1, y = 2` is emitted as a sequence of + # `variable_declaration`s directly into the enclosing stmt/member slot; every + # declaration after the first in such a group is tagged with a synthetic + # `chained_declaration` modifier so the grouping can be recovered downstream. + variable_declaration: + modifier*: modifier pattern: pattern + type?: type_expr value?: expr # Evaluate 'condition', and if false, execute 'else' which must break from the enclosing block scope (return, break, etc). # Any variables bound by 'condition' will be in scope for the remainder of the enclosing block scope - # (which differs from how if_stmt works). + # (which differs from how if_expr works). guard_if_stmt: - condition: condition - else: stmt + condition: expr + else: block - # Evaluates the given condition and interprets it as a boolean (by language conventions) - expr_condition: - expr: expr + # `break` (with optional label) + break_expr: + label?: identifier - # A series of statements that are executed before evaluating the trailing condition. - # Useful for languages where a conditional clause may be preceded by side-effecting - # syntactic elements (e.g. binding clauses) that don't themselves form a condition. - sequence_condition: - stmt*: stmt - condition: condition + # `continue` (with optional label) + continue_expr: + label?: identifier + + # A labeled statement, such as `outer: for ... { ... }`. The labeled + # statement appears as the `stmt` field; `break`/`continue` may target + # the label. + labeled_stmt: + label: identifier + stmt: stmt + + # `return value` or bare `return` + return_expr: + value?: expr + + # `throw value` + throw_expr: + value?: expr + + # An import declaration. + # + # The semantics of an import are generally: + # - Evaluate the 'imported_expr' to a value (possibly a compile-time value, such as namespace) + # - Filter away possible values based on modifiers (e.g. type-only imports only accept types) + # - Assign the value to the pattern, binding variables and/or type names in scope + # + import_declaration: + modifier*: modifier + imported_expr: expr # Qualified names are encoded as a chain of member_access_expr ending with a name_expr + pattern?: pattern # Binds local names in scope (possibly via bulk_importing_pattern) + + # `typealias Name = Type` + type_alias_declaration: + modifier*: modifier + name: identifier + type_parameter*: type_parameter + type_constraint*: type_constraint + type: type_expr + + # A top-level function declaration. + function_declaration: + modifier*: modifier + name: identifier + type_parameter*: type_parameter + type_constraint*: type_constraint + parameter*: parameter + return_type?: type_expr + body?: block + + # `for pattern in iterable [where guard] { body }`. + for_each_stmt: + modifier*: modifier + pattern: pattern + iterable: expr + guard?: expr + body?: block + + # `while condition { body }`. + while_stmt: + modifier*: modifier + condition: expr + body?: block + + # `repeat { body } while condition`. + do_while_stmt: + modifier*: modifier + body?: block + condition: expr + + # `do { body } catch pattern { ... } catch ...`. Swift uses `do`/`catch` + # for error handling; for languages with `try`/`catch`, this is the same shape. + try_expr: + modifier*: modifier + body: block + catch_clause*: catch_clause + + catch_clause: + modifier*: modifier + pattern?: pattern + guard?: expr + body: block + + # `switch value { case pattern: body case ...: default: body }` + switch_expr: + modifier*: modifier + value: expr + case*: switch_case + + # A single `case ...:` (or `default:`) entry in a switch. + # An entry with multiple `case p1, p2:` patterns has multiple `pattern`s. + # A `default:` entry has no patterns. + # An optional `guard` corresponds to a `where`-clause on the case. + switch_case: + modifier*: modifier + pattern*: pattern + guard?: expr + body: block # Evaluate 'expr' and match its result against 'pattern', and return true if it matches. - # Variables bound by the pattern will be in scope within the 'true' branch controlled by this condition. - let_pattern_condition: + # Variables bound by the pattern will be in scope within the 'true' branch controlled by this expression. + # + # In Swift, `if case let PATTERN = EXPR` maps to this node + # + # Java: 'if (x instanceof Foo y && w ...) { ... }' + pattern_guard_expr: pattern: pattern value: expr - # A pattern matching anything, binding its value to the given variable - var_pattern: + # A type cast expression, such as `x as T`, `x as? T`, or `x as! T`. The + # operator distinguishes between the variants. + type_cast_expr: + expr: expr + operator: infix_operator + type: type_expr + + # A type-test expression, such as `x is T`. Yields a boolean indicating + # whether `expr` is an instance of `type`. + type_test_expr: + expr: expr + operator: infix_operator + type: type_expr + + # An identifier that introduces a variable. + # + # When used as a pattern, the pattern matches anything and binds its incoming value to the variable + name_pattern: + modifier*: modifier identifier: identifier # A pattern matching anything, binding no variables, usually using the syntax "_" ignore_pattern: - # A pattern such as `Some(x)` where `Some` is the constructor and `x` is an argument - apply_pattern: - constructor: expr - argument*: pattern + # A pattern that matches if the incoming value is equal to the value of the given expression. + # Used for literal patterns in switch (e.g. `case 1:`). + expr_equality_pattern: + expr: expr # A tuple pattern such as `(a, b)` in `let (a, b) = pair`. + # + # Elements of the tuple pattern can have names, such as Swift's `let (foo: x, bar: y) = tuple`. tuple_pattern: - element*: pattern + modifier*: modifier + element*: pattern_element + + # A pattern such as `Some(x)` where `Some` is the constructor and `x` is an element. + # The element names are interpreted as argument labels and/or field names. + constructor_pattern: + modifier*: modifier + constructor: expr_or_type + element*: pattern_element + + # A pattern with an optional associated name. + pattern_element: + modifier*: modifier + key?: identifier + pattern: pattern + + # A pattern that checks if the incoming value has the given type, and if so, the + # value is matched against the given nested pattern (and succeeds iff the nested match succeeds). + # + # In Swift: `if let y = x as? Foo` is a pattern_guard_expr containing a type_test_pattern + # In Java: `x instanceof Foo y` is a type_test_pattern wrapping a name_pattern + type_test_pattern: + pattern: pattern + type: type_expr + + # A '*' pattern that imports all members of the incoming value into the local scope + # Currently this can only appear in import declarations. + bulk_importing_pattern: + modifier*: modifier # An simple unqualified identifier token identifier: @@ -141,4 +447,129 @@ named: # A node that we don't yet translate unsupported_node: - operator: + infix_operator: + + prefix_operator: + + postfix_operator: + + # The fixity of a custom operator declaration (e.g. "prefix", "infix", + # "postfix"). The value is the keyword string. + fixity: + + type_parameter: + modifier*: modifier + name: identifier + bound?: type_expr + + # A generic constraint of the form `T == U`, requiring two types to be + # equal. Appears in `where` clauses on generic declarations + # (e.g. Swift `func foo() where T == U`). + equality_type_constraint: + left: type_expr + right: type_expr + + # A generic constraint of the form `T: Bound`, requiring a type parameter + # to conform to (or inherit from) some other type. Appears in `where` + # clauses on generic declarations (e.g. Swift `where T: Equatable`). + bound_type_constraint: + type: type_expr + bound: type_expr + + # `infix operator +++` (and the like) — a declaration of a custom operator. + operator_syntax_declaration: + modifier*: modifier + name: identifier + # The fixity specifier (`prefix`, `infix`, `postfix`), when applicable. + fixity?: fixity + # The declared precedence level, when present (e.g. Swift's + # `infix operator +++ : AdditionPrecedence`). + precedence?: expr + + # A class-like declaration: class, struct, interface (protocol), enum (or actor). + # The syntactic kind is carried as a `modifier` (e.g. "class", "struct", + # "interface", "enum", "extension"). The `"enum_case"` modifier additionally + # marks a declaration as an enum case with associated values. Extensions are + # represented as a class-like declaration with the `"extension"` modifier and + # no `name`; the extended type appears as a `base_type`. + class_like_declaration: + modifier*: modifier + name?: identifier + type_parameter*: type_parameter + type_constraint*: type_constraint + base_type*: base_type + member*: member + + # One of the base types of a class declaration. + # + # If the language has multiple kinds of base classes (e.g. extends/implements) the + # kind should be included as a modifier on this node. + base_type: + modifier*: modifier + type: type_expr + + constructor_declaration: + modifier*: modifier + name?: identifier + parameter*: parameter + body: block + + # A destructor / finalizer (Swift `deinit`, C++ `~T()`, etc.). + destructor_declaration: + modifier*: modifier + body: block + + # Declaration of a single accessor for a property (such as a getter, setter, + # or observer like Swift's `willSet`/`didSet`). + # + # Multiple accessors for the same property are emitted as a sequence of + # accessor_declaration nodes; every accessor after the first is tagged with + # a synthetic `chained_declaration` modifier so the grouping can be recovered + # downstream. Stored properties with observers are emitted as a + # variable_declaration followed by one accessor_declaration per observer + # (each observer also tagged with `chained_declaration`). + accessor_declaration: + modifier*: modifier + name: identifier + accessor_kind: accessor_kind + parameter*: parameter + type?: type_expr + body?: block + + # "get", "set", or a language-specific kind like "didSet" + accessor_kind: + + # Static or instance initializer block. That is, code that runs at initialization time of either the class or an instance. + initializer_declaration: + modifier*: modifier + body: block + + associated_type_declaration: + modifier*: modifier + name: identifier + bound?: type_expr + + named_type_expr: + qualifier?: type_expr + name: identifier + + generic_type_expr: + base: type_expr + type_argument*: type_expr + + # A tuple type such as `(Int, String)` or `(a: A, b: B)`. + tuple_type_expr: + element*: tuple_type_element + + # An element of a `tuple_type_expr`, optionally carrying a label. + tuple_type_element: + name?: identifier + type: type_expr + + # A function type such as `(Int, String) -> Bool` or `(x: Int) -> Bool`. + function_type_expr: + parameter*: parameter + return_type: type_expr + + # A modifier such as 'static', 'public', or 'async'. For now this is just a leaf node with a string value. + modifier: From b40cb5dedda94dd9fee823b15e94cdc4134091a7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 2 Jun 2026 10:54:46 +0200 Subject: [PATCH 08/29] Regenerate QL --- unified/ql/lib/codeql/unified/Ast.qll | 1324 +++++++++++++++++++++---- unified/ql/lib/unified.dbscheme | 888 +++++++++++++++-- 2 files changed, 1954 insertions(+), 258 deletions(-) diff --git a/unified/ql/lib/codeql/unified/Ast.qll b/unified/ql/lib/codeql/unified/Ast.qll index b6d6a76b5492..32ddcd4c16cd 100644 --- a/unified/ql/lib/codeql/unified/Ast.qll +++ b/unified/ql/lib/codeql/unified/Ast.qll @@ -93,298 +93,1280 @@ module Unified { ) } - /** A class representing `apply_pattern` nodes. */ - class ApplyPattern extends @unified_apply_pattern, AstNode { + /** A class representing `accessor_declaration` nodes. */ + class AccessorDeclaration extends @unified_accessor_declaration, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ApplyPattern" } + final override string getAPrimaryQlClass() { result = "AccessorDeclaration" } + + /** Gets the node corresponding to the field `accessor_kind`. */ + final AccessorKind getAccessorKind() { unified_accessor_declaration_def(this, result, _) } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_accessor_declaration_body(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_accessor_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_accessor_declaration_def(this, _, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_accessor_declaration_parameter(this, i, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_accessor_declaration_type(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_accessor_declaration_def(this, result, _) or + unified_accessor_declaration_body(this, result) or + unified_accessor_declaration_modifier(this, _, result) or + unified_accessor_declaration_def(this, _, result) or + unified_accessor_declaration_parameter(this, _, result) or + unified_accessor_declaration_type(this, result) + } + } + + /** A class representing `accessor_kind` tokens. */ + class AccessorKind extends @unified_token_accessor_kind, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AccessorKind" } + } + + /** A class representing `argument` nodes. */ + class Argument extends @unified_argument, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Argument" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_argument_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_argument_name(this, result) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_argument_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_argument_modifier(this, _, result) or + unified_argument_name(this, result) or + unified_argument_def(this, result) + } + } + + /** A class representing `array_literal` nodes. */ + class ArrayLiteral extends @unified_array_literal, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ArrayLiteral" } + + /** Gets the node corresponding to the field `element`. */ + final Expr getElement(int i) { unified_array_literal_element(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_array_literal_element(this, _, result) } + } + + /** A class representing `assign_expr` nodes. */ + class AssignExpr extends @unified_assign_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AssignExpr" } + + /** Gets the node corresponding to the field `target`. */ + final ExprOrPattern getTarget() { unified_assign_expr_def(this, result, _) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_assign_expr_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_assign_expr_def(this, result, _) or unified_assign_expr_def(this, _, result) + } + } + + /** A class representing `associated_type_declaration` nodes. */ + class AssociatedTypeDeclaration extends @unified_associated_type_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "AssociatedTypeDeclaration" } + + /** Gets the node corresponding to the field `bound`. */ + final TypeExpr getBound() { unified_associated_type_declaration_bound(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { + unified_associated_type_declaration_modifier(this, i, result) + } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_associated_type_declaration_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_associated_type_declaration_bound(this, result) or + unified_associated_type_declaration_modifier(this, _, result) or + unified_associated_type_declaration_def(this, result) + } + } + + /** A class representing `base_type` nodes. */ + class BaseType extends @unified_base_type, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BaseType" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_base_type_modifier(this, i, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_base_type_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_base_type_modifier(this, _, result) or unified_base_type_def(this, result) + } + } + + /** A class representing `binary_expr` nodes. */ + class BinaryExpr extends @unified_binary_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BinaryExpr" } + + /** Gets the node corresponding to the field `left`. */ + final Expr getLeft() { unified_binary_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_binary_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `right`. */ + final Expr getRight() { unified_binary_expr_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_binary_expr_def(this, result, _, _) or + unified_binary_expr_def(this, _, result, _) or + unified_binary_expr_def(this, _, _, result) + } + } + + /** A class representing `block` nodes. */ + class Block extends @unified_block, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Block" } + + /** Gets the node corresponding to the field `stmt`. */ + final Stmt getStmt(int i) { unified_block_stmt(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_block_stmt(this, _, result) } + } + + /** A class representing `boolean_literal` tokens. */ + class BooleanLiteral extends @unified_token_boolean_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BooleanLiteral" } + } + + /** A class representing `bound_type_constraint` nodes. */ + class BoundTypeConstraint extends @unified_bound_type_constraint, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BoundTypeConstraint" } + + /** Gets the node corresponding to the field `bound`. */ + final TypeExpr getBound() { unified_bound_type_constraint_def(this, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_bound_type_constraint_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_bound_type_constraint_def(this, result, _) or + unified_bound_type_constraint_def(this, _, result) + } + } + + /** A class representing `break_expr` nodes. */ + class BreakExpr extends @unified_break_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BreakExpr" } + + /** Gets the node corresponding to the field `label`. */ + final Identifier getLabel() { unified_break_expr_label(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_break_expr_label(this, result) } + } + + /** A class representing `builtin_expr` tokens. */ + class BuiltinExpr extends @unified_token_builtin_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BuiltinExpr" } + } + + /** A class representing `bulk_importing_pattern` nodes. */ + class BulkImportingPattern extends @unified_bulk_importing_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BulkImportingPattern" } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_bulk_importing_pattern_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_bulk_importing_pattern_modifier(this, _, result) + } + } + + /** A class representing `call_expr` nodes. */ + class CallExpr extends @unified_call_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "CallExpr" } /** Gets the node corresponding to the field `argument`. */ - final Pattern getArgument(int i) { unified_apply_pattern_argument(this, i, result) } + final Argument getArgument(int i) { unified_call_expr_argument(this, i, result) } + + /** Gets the node corresponding to the field `callee`. */ + final ExprOrType getCallee() { unified_call_expr_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_call_expr_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_call_expr_argument(this, _, result) or + unified_call_expr_def(this, result) or + unified_call_expr_modifier(this, _, result) + } + } + + /** A class representing `catch_clause` nodes. */ + class CatchClause extends @unified_catch_clause, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "CatchClause" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_catch_clause_def(this, result) } + + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_catch_clause_guard(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_catch_clause_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_catch_clause_pattern(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_catch_clause_def(this, result) or + unified_catch_clause_guard(this, result) or + unified_catch_clause_modifier(this, _, result) or + unified_catch_clause_pattern(this, result) + } + } + + /** A class representing `class_like_declaration` nodes. */ + class ClassLikeDeclaration extends @unified_class_like_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ClassLikeDeclaration" } + + /** Gets the node corresponding to the field `base_type`. */ + final BaseType getBaseType(int i) { unified_class_like_declaration_base_type(this, i, result) } + + /** Gets the node corresponding to the field `member`. */ + final Member getMember(int i) { unified_class_like_declaration_member(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_class_like_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_class_like_declaration_name(this, result) } + + /** Gets the node corresponding to the field `type_constraint`. */ + final TypeConstraint getTypeConstraint(int i) { + unified_class_like_declaration_type_constraint(this, i, result) + } + + /** Gets the node corresponding to the field `type_parameter`. */ + final TypeParameter getTypeParameter(int i) { + unified_class_like_declaration_type_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_class_like_declaration_base_type(this, _, result) or + unified_class_like_declaration_member(this, _, result) or + unified_class_like_declaration_modifier(this, _, result) or + unified_class_like_declaration_name(this, result) or + unified_class_like_declaration_type_constraint(this, _, result) or + unified_class_like_declaration_type_parameter(this, _, result) + } + } + + /** A class representing `compound_assign_expr` nodes. */ + class CompoundAssignExpr extends @unified_compound_assign_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "CompoundAssignExpr" } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_compound_assign_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `target`. */ + final Expr getTarget() { unified_compound_assign_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_compound_assign_expr_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_compound_assign_expr_def(this, result, _, _) or + unified_compound_assign_expr_def(this, _, result, _) or + unified_compound_assign_expr_def(this, _, _, result) + } + } + + /** A class representing `constructor_declaration` nodes. */ + class ConstructorDeclaration extends @unified_constructor_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ConstructorDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_constructor_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_constructor_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_constructor_declaration_name(this, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { + unified_constructor_declaration_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_constructor_declaration_def(this, result) or + unified_constructor_declaration_modifier(this, _, result) or + unified_constructor_declaration_name(this, result) or + unified_constructor_declaration_parameter(this, _, result) + } + } + + /** A class representing `constructor_pattern` nodes. */ + class ConstructorPattern extends @unified_constructor_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ConstructorPattern" } /** Gets the node corresponding to the field `constructor`. */ - final Expr getConstructor() { unified_apply_pattern_def(this, result) } + final ExprOrType getConstructor() { unified_constructor_pattern_def(this, result) } + + /** Gets the node corresponding to the field `element`. */ + final PatternElement getElement(int i) { unified_constructor_pattern_element(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_constructor_pattern_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_constructor_pattern_def(this, result) or + unified_constructor_pattern_element(this, _, result) or + unified_constructor_pattern_modifier(this, _, result) + } + } + + /** A class representing `continue_expr` nodes. */ + class ContinueExpr extends @unified_continue_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ContinueExpr" } + + /** Gets the node corresponding to the field `label`. */ + final Identifier getLabel() { unified_continue_expr_label(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_continue_expr_label(this, result) } + } + + /** A class representing `destructor_declaration` nodes. */ + class DestructorDeclaration extends @unified_destructor_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "DestructorDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_destructor_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_destructor_declaration_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_destructor_declaration_def(this, result) or + unified_destructor_declaration_modifier(this, _, result) + } + } + + /** A class representing `do_while_stmt` nodes. */ + class DoWhileStmt extends @unified_do_while_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "DoWhileStmt" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_do_while_stmt_body(this, result) } + + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_do_while_stmt_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_do_while_stmt_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_do_while_stmt_body(this, result) or + unified_do_while_stmt_def(this, result) or + unified_do_while_stmt_modifier(this, _, result) + } + } + + /** A class representing `empty_expr` tokens. */ + class EmptyExpr extends @unified_token_empty_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "EmptyExpr" } + } + + /** A class representing `equality_type_constraint` nodes. */ + class EqualityTypeConstraint extends @unified_equality_type_constraint, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "EqualityTypeConstraint" } + + /** Gets the node corresponding to the field `left`. */ + final TypeExpr getLeft() { unified_equality_type_constraint_def(this, result, _) } + + /** Gets the node corresponding to the field `right`. */ + final TypeExpr getRight() { unified_equality_type_constraint_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_equality_type_constraint_def(this, result, _) or + unified_equality_type_constraint_def(this, _, result) + } + } + + class Expr extends @unified_expr, AstNode { } + + /** A class representing `expr_equality_pattern` nodes. */ + class ExprEqualityPattern extends @unified_expr_equality_pattern, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ExprEqualityPattern" } + + /** Gets the node corresponding to the field `expr`. */ + final Expr getExpr() { unified_expr_equality_pattern_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_expr_equality_pattern_def(this, result) } + } + + class ExprOrPattern extends @unified_expr_or_pattern, AstNode { } + + class ExprOrType extends @unified_expr_or_type, AstNode { } + + /** A class representing `fixity` tokens. */ + class Fixity extends @unified_token_fixity, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Fixity" } + } + + /** A class representing `float_literal` tokens. */ + class FloatLiteral extends @unified_token_float_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FloatLiteral" } + } + + /** A class representing `for_each_stmt` nodes. */ + class ForEachStmt extends @unified_for_each_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ForEachStmt" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_for_each_stmt_body(this, result) } + + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_for_each_stmt_guard(this, result) } + + /** Gets the node corresponding to the field `iterable`. */ + final Expr getIterable() { unified_for_each_stmt_def(this, result, _) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_for_each_stmt_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_for_each_stmt_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_for_each_stmt_body(this, result) or + unified_for_each_stmt_guard(this, result) or + unified_for_each_stmt_def(this, result, _) or + unified_for_each_stmt_modifier(this, _, result) or + unified_for_each_stmt_def(this, _, result) + } + } + + /** A class representing `function_declaration` nodes. */ + class FunctionDeclaration extends @unified_function_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FunctionDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_function_declaration_body(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_function_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_function_declaration_def(this, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_function_declaration_parameter(this, i, result) } + + /** Gets the node corresponding to the field `return_type`. */ + final TypeExpr getReturnType() { unified_function_declaration_return_type(this, result) } + + /** Gets the node corresponding to the field `type_constraint`. */ + final TypeConstraint getTypeConstraint(int i) { + unified_function_declaration_type_constraint(this, i, result) + } + + /** Gets the node corresponding to the field `type_parameter`. */ + final TypeParameter getTypeParameter(int i) { + unified_function_declaration_type_parameter(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_function_declaration_body(this, result) or + unified_function_declaration_modifier(this, _, result) or + unified_function_declaration_def(this, result) or + unified_function_declaration_parameter(this, _, result) or + unified_function_declaration_return_type(this, result) or + unified_function_declaration_type_constraint(this, _, result) or + unified_function_declaration_type_parameter(this, _, result) + } + } + + /** A class representing `function_expr` nodes. */ + class FunctionExpr extends @unified_function_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FunctionExpr" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_function_expr_def(this, result) } + + /** Gets the node corresponding to the field `capture_declaration`. */ + final VariableDeclaration getCaptureDeclaration(int i) { + unified_function_expr_capture_declaration(this, i, result) + } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_function_expr_modifier(this, i, result) } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_function_expr_parameter(this, i, result) } + + /** Gets the node corresponding to the field `return_type`. */ + final TypeExpr getReturnType() { unified_function_expr_return_type(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_function_expr_def(this, result) or + unified_function_expr_capture_declaration(this, _, result) or + unified_function_expr_modifier(this, _, result) or + unified_function_expr_parameter(this, _, result) or + unified_function_expr_return_type(this, result) + } + } + + /** A class representing `function_type_expr` nodes. */ + class FunctionTypeExpr extends @unified_function_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "FunctionTypeExpr" } + + /** Gets the node corresponding to the field `parameter`. */ + final Parameter getParameter(int i) { unified_function_type_expr_parameter(this, i, result) } + + /** Gets the node corresponding to the field `return_type`. */ + final TypeExpr getReturnType() { unified_function_type_expr_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_function_type_expr_parameter(this, _, result) or + unified_function_type_expr_def(this, result) + } + } + + /** A class representing `generic_type_expr` nodes. */ + class GenericTypeExpr extends @unified_generic_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "GenericTypeExpr" } + + /** Gets the node corresponding to the field `base`. */ + final TypeExpr getBase() { unified_generic_type_expr_def(this, result) } + + /** Gets the node corresponding to the field `type_argument`. */ + final TypeExpr getTypeArgument(int i) { + unified_generic_type_expr_type_argument(this, i, result) + } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_generic_type_expr_def(this, result) or + unified_generic_type_expr_type_argument(this, _, result) + } + } + + /** A class representing `guard_if_stmt` nodes. */ + class GuardIfStmt extends @unified_guard_if_stmt, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "GuardIfStmt" } + + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_guard_if_stmt_def(this, result, _) } + + /** Gets the node corresponding to the field `else`. */ + final Block getElse() { unified_guard_if_stmt_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_guard_if_stmt_def(this, result, _) or unified_guard_if_stmt_def(this, _, result) + } + } + + /** A class representing `identifier` tokens. */ + class Identifier extends @unified_token_identifier, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "Identifier" } + } + + /** A class representing `if_expr` nodes. */ + class IfExpr extends @unified_if_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "IfExpr" } + + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_if_expr_def(this, result) } + + /** Gets the node corresponding to the field `else`. */ + final Expr getElse() { unified_if_expr_else(this, result) } + + /** Gets the node corresponding to the field `then`. */ + final Expr getThen() { unified_if_expr_then(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_if_expr_def(this, result) or + unified_if_expr_else(this, result) or + unified_if_expr_then(this, result) + } + } + + /** A class representing `ignore_pattern` tokens. */ + class IgnorePattern extends @unified_token_ignore_pattern, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "IgnorePattern" } + } + + /** A class representing `import_declaration` nodes. */ + class ImportDeclaration extends @unified_import_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ImportDeclaration" } + + /** Gets the node corresponding to the field `imported_expr`. */ + final Expr getImportedExpr() { unified_import_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_import_declaration_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_import_declaration_pattern(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_import_declaration_def(this, result) or + unified_import_declaration_modifier(this, _, result) or + unified_import_declaration_pattern(this, result) + } + } + + /** A class representing `inferred_type_expr` tokens. */ + class InferredTypeExpr extends @unified_token_inferred_type_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "InferredTypeExpr" } + } + + /** A class representing `infix_operator` tokens. */ + class InfixOperator extends @unified_token_infix_operator, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "InfixOperator" } + } + + /** A class representing `initializer_declaration` nodes. */ + class InitializerDeclaration extends @unified_initializer_declaration, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "InitializerDeclaration" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_initializer_declaration_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_initializer_declaration_modifier(this, i, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_apply_pattern_argument(this, _, result) or unified_apply_pattern_def(this, result) + unified_initializer_declaration_def(this, result) or + unified_initializer_declaration_modifier(this, _, result) } } - /** A class representing `binary_expr` nodes. */ - class BinaryExpr extends @unified_binary_expr, AstNode { + /** A class representing `int_literal` tokens. */ + class IntLiteral extends @unified_token_int_literal, Token { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "BinaryExpr" } + final override string getAPrimaryQlClass() { result = "IntLiteral" } + } - /** Gets the node corresponding to the field `left`. */ - final Expr getLeft() { unified_binary_expr_def(this, result, _, _) } + /** A class representing `key_value_pair` nodes. */ + class KeyValuePair extends @unified_key_value_pair, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "KeyValuePair" } - /** Gets the node corresponding to the field `operator`. */ - final Operator getOperator() { unified_binary_expr_def(this, _, result, _) } + /** Gets the node corresponding to the field `key`. */ + final Expr getKey() { unified_key_value_pair_def(this, result, _) } - /** Gets the node corresponding to the field `right`. */ - final Expr getRight() { unified_binary_expr_def(this, _, _, result) } + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_key_value_pair_def(this, _, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_binary_expr_def(this, result, _, _) or - unified_binary_expr_def(this, _, result, _) or - unified_binary_expr_def(this, _, _, result) + unified_key_value_pair_def(this, result, _) or unified_key_value_pair_def(this, _, result) } } - /** A class representing `block_stmt` nodes. */ - class BlockStmt extends @unified_block_stmt, AstNode { + /** A class representing `labeled_stmt` nodes. */ + class LabeledStmt extends @unified_labeled_stmt, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "BlockStmt" } + final override string getAPrimaryQlClass() { result = "LabeledStmt" } - /** Gets the node corresponding to the field `body`. */ - final Stmt getBody(int i) { unified_block_stmt_body(this, i, result) } + /** Gets the node corresponding to the field `label`. */ + final Identifier getLabel() { unified_labeled_stmt_def(this, result, _) } + + /** Gets the node corresponding to the field `stmt`. */ + final Stmt getStmt() { unified_labeled_stmt_def(this, _, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_block_stmt_body(this, _, result) } + final override AstNode getAFieldOrChild() { + unified_labeled_stmt_def(this, result, _) or unified_labeled_stmt_def(this, _, result) + } } - /** A class representing `call_expr` nodes. */ - class CallExpr extends @unified_call_expr, AstNode { + /** A class representing `map_literal` nodes. */ + class MapLiteral extends @unified_map_literal, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "CallExpr" } + final override string getAPrimaryQlClass() { result = "MapLiteral" } - /** Gets the node corresponding to the field `argument`. */ - final Expr getArgument(int i) { unified_call_expr_argument(this, i, result) } + /** Gets the node corresponding to the field `element`. */ + final Expr getElement(int i) { unified_map_literal_element(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_map_literal_element(this, _, result) } + } + + class Member extends @unified_member, AstNode { } + + /** A class representing `member_access_expr` nodes. */ + class MemberAccessExpr extends @unified_member_access_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "MemberAccessExpr" } - /** Gets the node corresponding to the field `function`. */ - final Expr getFunction() { unified_call_expr_def(this, result) } + /** Gets the node corresponding to the field `base`. */ + final ExprOrType getBase() { unified_member_access_expr_def(this, result, _) } + + /** Gets the node corresponding to the field `member`. */ + final Identifier getMember() { unified_member_access_expr_def(this, _, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_call_expr_argument(this, _, result) or unified_call_expr_def(this, result) + unified_member_access_expr_def(this, result, _) or + unified_member_access_expr_def(this, _, result) } } - class Condition extends @unified_condition, AstNode { } - - /** A class representing `empty_stmt` tokens. */ - class EmptyStmt extends @unified_token_empty_stmt, Token { + /** A class representing `modifier` tokens. */ + class Modifier extends @unified_token_modifier, Token { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "EmptyStmt" } + final override string getAPrimaryQlClass() { result = "Modifier" } } - class Expr extends @unified_expr, AstNode { } + /** A class representing `name_expr` nodes. */ + class NameExpr extends @unified_name_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "NameExpr" } + + /** Gets the node corresponding to the field `identifier`. */ + final Identifier getIdentifier() { unified_name_expr_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_name_expr_def(this, result) } + } - /** A class representing `expr_condition` nodes. */ - class ExprCondition extends @unified_expr_condition, AstNode { + /** A class representing `name_pattern` nodes. */ + class NamePattern extends @unified_name_pattern, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ExprCondition" } + final override string getAPrimaryQlClass() { result = "NamePattern" } - /** Gets the node corresponding to the field `expr`. */ - final Expr getExpr() { unified_expr_condition_def(this, result) } + /** Gets the node corresponding to the field `identifier`. */ + final Identifier getIdentifier() { unified_name_pattern_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_name_pattern_modifier(this, i, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_expr_condition_def(this, result) } + final override AstNode getAFieldOrChild() { + unified_name_pattern_def(this, result) or unified_name_pattern_modifier(this, _, result) + } } - /** A class representing `expr_stmt` nodes. */ - class ExprStmt extends @unified_expr_stmt, AstNode { + /** A class representing `named_type_expr` nodes. */ + class NamedTypeExpr extends @unified_named_type_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "ExprStmt" } + final override string getAPrimaryQlClass() { result = "NamedTypeExpr" } - /** Gets the node corresponding to the field `expr`. */ - final Expr getExpr() { unified_expr_stmt_def(this, result) } + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_named_type_expr_def(this, result) } + + /** Gets the node corresponding to the field `qualifier`. */ + final TypeExpr getQualifier() { unified_named_type_expr_qualifier(this, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_expr_stmt_def(this, result) } + final override AstNode getAFieldOrChild() { + unified_named_type_expr_def(this, result) or unified_named_type_expr_qualifier(this, result) + } } - /** A class representing `guard_if_stmt` nodes. */ - class GuardIfStmt extends @unified_guard_if_stmt, AstNode { + class Operator extends @unified_operator, AstNode { } + + /** A class representing `operator_syntax_declaration` nodes. */ + class OperatorSyntaxDeclaration extends @unified_operator_syntax_declaration, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "GuardIfStmt" } + final override string getAPrimaryQlClass() { result = "OperatorSyntaxDeclaration" } - /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_guard_if_stmt_def(this, result, _) } + /** Gets the node corresponding to the field `fixity`. */ + final Fixity getFixity() { unified_operator_syntax_declaration_fixity(this, result) } - /** Gets the node corresponding to the field `else`. */ - final Stmt getElse() { unified_guard_if_stmt_def(this, _, result) } + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { + unified_operator_syntax_declaration_modifier(this, i, result) + } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_operator_syntax_declaration_def(this, result) } + + /** Gets the node corresponding to the field `precedence`. */ + final Expr getPrecedence() { unified_operator_syntax_declaration_precedence(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_guard_if_stmt_def(this, result, _) or unified_guard_if_stmt_def(this, _, result) + unified_operator_syntax_declaration_fixity(this, result) or + unified_operator_syntax_declaration_modifier(this, _, result) or + unified_operator_syntax_declaration_def(this, result) or + unified_operator_syntax_declaration_precedence(this, result) } } - /** A class representing `identifier` tokens. */ - class Identifier extends @unified_token_identifier, Token { + /** A class representing `parameter` nodes. */ + class Parameter extends @unified_parameter, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "Identifier" } + final override string getAPrimaryQlClass() { result = "Parameter" } + + /** Gets the node corresponding to the field `default`. */ + final Expr getDefault() { unified_parameter_default(this, result) } + + /** Gets the node corresponding to the field `external_name`. */ + final Identifier getExternalName() { unified_parameter_external_name(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_parameter_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_parameter_pattern(this, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_parameter_type(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_parameter_default(this, result) or + unified_parameter_external_name(this, result) or + unified_parameter_modifier(this, _, result) or + unified_parameter_pattern(this, result) or + unified_parameter_type(this, result) + } } - /** A class representing `if_stmt` nodes. */ - class IfStmt extends @unified_if_stmt, AstNode { + class Pattern extends @unified_pattern, AstNode { } + + /** A class representing `pattern_element` nodes. */ + class PatternElement extends @unified_pattern_element, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "IfStmt" } + final override string getAPrimaryQlClass() { result = "PatternElement" } - /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_if_stmt_def(this, result) } + /** Gets the node corresponding to the field `key`. */ + final Identifier getKey() { unified_pattern_element_key(this, result) } - /** Gets the node corresponding to the field `else`. */ - final Stmt getElse() { unified_if_stmt_else(this, result) } + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_pattern_element_modifier(this, i, result) } - /** Gets the node corresponding to the field `then`. */ - final Stmt getThen() { unified_if_stmt_then(this, result) } + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_pattern_element_def(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_if_stmt_def(this, result) or - unified_if_stmt_else(this, result) or - unified_if_stmt_then(this, result) + unified_pattern_element_key(this, result) or + unified_pattern_element_modifier(this, _, result) or + unified_pattern_element_def(this, result) } } - /** A class representing `ignore_pattern` tokens. */ - class IgnorePattern extends @unified_token_ignore_pattern, Token { + /** A class representing `pattern_guard_expr` nodes. */ + class PatternGuardExpr extends @unified_pattern_guard_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "IgnorePattern" } + final override string getAPrimaryQlClass() { result = "PatternGuardExpr" } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_pattern_guard_expr_def(this, result, _) } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_pattern_guard_expr_def(this, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_pattern_guard_expr_def(this, result, _) or + unified_pattern_guard_expr_def(this, _, result) + } } - /** A class representing `int_literal` tokens. */ - class IntLiteral extends @unified_token_int_literal, Token { + /** A class representing `postfix_operator` tokens. */ + class PostfixOperator extends @unified_token_postfix_operator, Token { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "IntLiteral" } + final override string getAPrimaryQlClass() { result = "PostfixOperator" } + } + + /** A class representing `prefix_operator` tokens. */ + class PrefixOperator extends @unified_token_prefix_operator, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "PrefixOperator" } + } + + /** A class representing `regex_literal` tokens. */ + class RegexLiteral extends @unified_token_regex_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "RegexLiteral" } + } + + /** A class representing `return_expr` nodes. */ + class ReturnExpr extends @unified_return_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "ReturnExpr" } + + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_return_expr_value(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_return_expr_value(this, result) } + } + + class Stmt extends @unified_stmt, AstNode { } + + /** A class representing `string_literal` tokens. */ + class StringLiteral extends @unified_token_string_literal, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "StringLiteral" } + } + + /** A class representing `super_expr` tokens. */ + class SuperExpr extends @unified_token_super_expr, Token { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "SuperExpr" } } - /** A class representing `lambda_expr` nodes. */ - class LambdaExpr extends @unified_lambda_expr, AstNode { + /** A class representing `switch_case` nodes. */ + class SwitchCase extends @unified_switch_case, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "LambdaExpr" } + final override string getAPrimaryQlClass() { result = "SwitchCase" } /** Gets the node corresponding to the field `body`. */ - final AstNode getBody() { unified_lambda_expr_def(this, result) } + final Block getBody() { unified_switch_case_def(this, result) } - /** Gets the node corresponding to the field `parameter`. */ - final Parameter getParameter(int i) { unified_lambda_expr_parameter(this, i, result) } + /** Gets the node corresponding to the field `guard`. */ + final Expr getGuard() { unified_switch_case_guard(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_switch_case_modifier(this, i, result) } + + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern(int i) { unified_switch_case_pattern(this, i, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_lambda_expr_def(this, result) or unified_lambda_expr_parameter(this, _, result) + unified_switch_case_def(this, result) or + unified_switch_case_guard(this, result) or + unified_switch_case_modifier(this, _, result) or + unified_switch_case_pattern(this, _, result) } } - /** A class representing `let_pattern_condition` nodes. */ - class LetPatternCondition extends @unified_let_pattern_condition, AstNode { + /** A class representing `switch_expr` nodes. */ + class SwitchExpr extends @unified_switch_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "LetPatternCondition" } + final override string getAPrimaryQlClass() { result = "SwitchExpr" } - /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_let_pattern_condition_def(this, result, _) } + /** Gets the node corresponding to the field `case`. */ + final SwitchCase getCase(int i) { unified_switch_expr_case(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_switch_expr_modifier(this, i, result) } /** Gets the node corresponding to the field `value`. */ - final Expr getValue() { unified_let_pattern_condition_def(this, _, result) } + final Expr getValue() { unified_switch_expr_def(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_let_pattern_condition_def(this, result, _) or - unified_let_pattern_condition_def(this, _, result) + unified_switch_expr_case(this, _, result) or + unified_switch_expr_modifier(this, _, result) or + unified_switch_expr_def(this, result) } } - /** A class representing `member_access_expr` nodes. */ - class MemberAccessExpr extends @unified_member_access_expr, AstNode { + /** A class representing `throw_expr` nodes. */ + class ThrowExpr extends @unified_throw_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "MemberAccessExpr" } + final override string getAPrimaryQlClass() { result = "ThrowExpr" } - /** Gets the node corresponding to the field `member`. */ - final Identifier getMember() { unified_member_access_expr_def(this, result, _) } + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_throw_expr_value(this, result) } - /** Gets the node corresponding to the field `target`. */ - final Expr getTarget() { unified_member_access_expr_def(this, _, result) } + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_throw_expr_value(this, result) } + } + + /** A class representing `top_level` nodes. */ + class TopLevel extends @unified_top_level, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TopLevel" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_top_level_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_top_level_def(this, result) } + } + + /** A class representing `try_expr` nodes. */ + class TryExpr extends @unified_try_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TryExpr" } + + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_try_expr_def(this, result) } + + /** Gets the node corresponding to the field `catch_clause`. */ + final CatchClause getCatchClause(int i) { unified_try_expr_catch_clause(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_try_expr_modifier(this, i, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_member_access_expr_def(this, result, _) or - unified_member_access_expr_def(this, _, result) + unified_try_expr_def(this, result) or + unified_try_expr_catch_clause(this, _, result) or + unified_try_expr_modifier(this, _, result) } } - /** A class representing `name_expr` nodes. */ - class NameExpr extends @unified_name_expr, AstNode { + /** A class representing `tuple_expr` nodes. */ + class TupleExpr extends @unified_tuple_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "NameExpr" } + final override string getAPrimaryQlClass() { result = "TupleExpr" } - /** Gets the node corresponding to the field `identifier`. */ - final Identifier getIdentifier() { unified_name_expr_def(this, result) } + /** Gets the node corresponding to the field `element`. */ + final Expr getElement(int i) { unified_tuple_expr_element(this, i, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_name_expr_def(this, result) } + final override AstNode getAFieldOrChild() { unified_tuple_expr_element(this, _, result) } } - /** A class representing `operator` tokens. */ - class Operator extends @unified_token_operator, Token { + /** A class representing `tuple_pattern` nodes. */ + class TuplePattern extends @unified_tuple_pattern, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "Operator" } + final override string getAPrimaryQlClass() { result = "TuplePattern" } + + /** Gets the node corresponding to the field `element`. */ + final PatternElement getElement(int i) { unified_tuple_pattern_element(this, i, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_tuple_pattern_modifier(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_tuple_pattern_element(this, _, result) or + unified_tuple_pattern_modifier(this, _, result) + } } - /** A class representing `parameter` nodes. */ - class Parameter extends @unified_parameter, AstNode { + /** A class representing `tuple_type_element` nodes. */ + class TupleTypeElement extends @unified_tuple_type_element, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "Parameter" } + final override string getAPrimaryQlClass() { result = "TupleTypeElement" } - /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_parameter_def(this, result) } + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_tuple_type_element_name(this, result) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_tuple_type_element_def(this, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_parameter_def(this, result) } + final override AstNode getAFieldOrChild() { + unified_tuple_type_element_name(this, result) or unified_tuple_type_element_def(this, result) + } } - class Pattern extends @unified_pattern, AstNode { } + /** A class representing `tuple_type_expr` nodes. */ + class TupleTypeExpr extends @unified_tuple_type_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TupleTypeExpr" } + + /** Gets the node corresponding to the field `element`. */ + final TupleTypeElement getElement(int i) { unified_tuple_type_expr_element(this, i, result) } - /** A class representing `sequence_condition` nodes. */ - class SequenceCondition extends @unified_sequence_condition, AstNode { + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { unified_tuple_type_expr_element(this, _, result) } + } + + /** A class representing `type_alias_declaration` nodes. */ + class TypeAliasDeclaration extends @unified_type_alias_declaration, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "SequenceCondition" } + final override string getAPrimaryQlClass() { result = "TypeAliasDeclaration" } - /** Gets the node corresponding to the field `condition`. */ - final Condition getCondition() { unified_sequence_condition_def(this, result) } + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_type_alias_declaration_modifier(this, i, result) } - /** Gets the node corresponding to the field `stmt`. */ - final Stmt getStmt(int i) { unified_sequence_condition_stmt(this, i, result) } + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_type_alias_declaration_def(this, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_alias_declaration_def(this, _, result) } + + /** Gets the node corresponding to the field `type_constraint`. */ + final TypeConstraint getTypeConstraint(int i) { + unified_type_alias_declaration_type_constraint(this, i, result) + } + + /** Gets the node corresponding to the field `type_parameter`. */ + final TypeParameter getTypeParameter(int i) { + unified_type_alias_declaration_type_parameter(this, i, result) + } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_sequence_condition_def(this, result) or - unified_sequence_condition_stmt(this, _, result) + unified_type_alias_declaration_modifier(this, _, result) or + unified_type_alias_declaration_def(this, result, _) or + unified_type_alias_declaration_def(this, _, result) or + unified_type_alias_declaration_type_constraint(this, _, result) or + unified_type_alias_declaration_type_parameter(this, _, result) } } - class Stmt extends @unified_stmt, AstNode { } + /** A class representing `type_cast_expr` nodes. */ + class TypeCastExpr extends @unified_type_cast_expr, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "TypeCastExpr" } - /** A class representing `string_literal` tokens. */ - class StringLiteral extends @unified_token_string_literal, Token { + /** Gets the node corresponding to the field `expr`. */ + final Expr getExpr() { unified_type_cast_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_type_cast_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_cast_expr_def(this, _, _, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_cast_expr_def(this, result, _, _) or + unified_type_cast_expr_def(this, _, result, _) or + unified_type_cast_expr_def(this, _, _, result) + } + } + + class TypeConstraint extends @unified_type_constraint, AstNode { } + + class TypeExpr extends @unified_type_expr, AstNode { } + + /** A class representing `type_parameter` nodes. */ + class TypeParameter extends @unified_type_parameter, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "StringLiteral" } + final override string getAPrimaryQlClass() { result = "TypeParameter" } + + /** Gets the node corresponding to the field `bound`. */ + final TypeExpr getBound() { unified_type_parameter_bound(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_type_parameter_modifier(this, i, result) } + + /** Gets the node corresponding to the field `name`. */ + final Identifier getName() { unified_type_parameter_def(this, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { + unified_type_parameter_bound(this, result) or + unified_type_parameter_modifier(this, _, result) or + unified_type_parameter_def(this, result) + } } - /** A class representing `top_level` nodes. */ - class TopLevel extends @unified_top_level, AstNode { + /** A class representing `type_test_expr` nodes. */ + class TypeTestExpr extends @unified_type_test_expr, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "TopLevel" } + final override string getAPrimaryQlClass() { result = "TypeTestExpr" } - /** Gets the node corresponding to the field `body`. */ - final AstNode getBody(int i) { unified_top_level_body(this, i, result) } + /** Gets the node corresponding to the field `expr`. */ + final Expr getExpr() { unified_type_test_expr_def(this, result, _, _) } + + /** Gets the node corresponding to the field `operator`. */ + final InfixOperator getOperator() { unified_type_test_expr_def(this, _, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_test_expr_def(this, _, _, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_top_level_body(this, _, result) } + final override AstNode getAFieldOrChild() { + unified_type_test_expr_def(this, result, _, _) or + unified_type_test_expr_def(this, _, result, _) or + unified_type_test_expr_def(this, _, _, result) + } } - /** A class representing `tuple_pattern` nodes. */ - class TuplePattern extends @unified_tuple_pattern, AstNode { + /** A class representing `type_test_pattern` nodes. */ + class TypeTestPattern extends @unified_type_test_pattern, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "TuplePattern" } + final override string getAPrimaryQlClass() { result = "TypeTestPattern" } - /** Gets the node corresponding to the field `element`. */ - final Pattern getElement(int i) { unified_tuple_pattern_element(this, i, result) } + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_type_test_pattern_def(this, result, _) } + + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_type_test_pattern_def(this, _, result) } /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_tuple_pattern_element(this, _, result) } + final override AstNode getAFieldOrChild() { + unified_type_test_pattern_def(this, result, _) or + unified_type_test_pattern_def(this, _, result) + } } /** A class representing `unary_expr` nodes. */ @@ -410,49 +1392,51 @@ module Unified { final override string getAPrimaryQlClass() { result = "UnsupportedNode" } } - /** A class representing `var_pattern` nodes. */ - class VarPattern extends @unified_var_pattern, AstNode { + /** A class representing `variable_declaration` nodes. */ + class VariableDeclaration extends @unified_variable_declaration, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "VarPattern" } + final override string getAPrimaryQlClass() { result = "VariableDeclaration" } - /** Gets the node corresponding to the field `identifier`. */ - final Identifier getIdentifier() { unified_var_pattern_def(this, result) } + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_variable_declaration_modifier(this, i, result) } - /** Gets a field or child node of this node. */ - final override AstNode getAFieldOrChild() { unified_var_pattern_def(this, result) } - } + /** Gets the node corresponding to the field `pattern`. */ + final Pattern getPattern() { unified_variable_declaration_def(this, result) } - /** A class representing `variable_declaration_stmt` nodes. */ - class VariableDeclarationStmt extends @unified_variable_declaration_stmt, AstNode { - /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "VariableDeclarationStmt" } + /** Gets the node corresponding to the field `type`. */ + final TypeExpr getType() { unified_variable_declaration_type(this, result) } - /** Gets the node corresponding to the field `variable_declarator`. */ - final VariableDeclarator getVariableDeclarator(int i) { - unified_variable_declaration_stmt_variable_declarator(this, i, result) - } + /** Gets the node corresponding to the field `value`. */ + final Expr getValue() { unified_variable_declaration_value(this, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_variable_declaration_stmt_variable_declarator(this, _, result) + unified_variable_declaration_modifier(this, _, result) or + unified_variable_declaration_def(this, result) or + unified_variable_declaration_type(this, result) or + unified_variable_declaration_value(this, result) } } - /** A class representing `variable_declarator` nodes. */ - class VariableDeclarator extends @unified_variable_declarator, AstNode { + /** A class representing `while_stmt` nodes. */ + class WhileStmt extends @unified_while_stmt, AstNode { /** Gets the name of the primary QL class for this element. */ - final override string getAPrimaryQlClass() { result = "VariableDeclarator" } + final override string getAPrimaryQlClass() { result = "WhileStmt" } - /** Gets the node corresponding to the field `pattern`. */ - final Pattern getPattern() { unified_variable_declarator_def(this, result) } + /** Gets the node corresponding to the field `body`. */ + final Block getBody() { unified_while_stmt_body(this, result) } - /** Gets the node corresponding to the field `value`. */ - final Expr getValue() { unified_variable_declarator_value(this, result) } + /** Gets the node corresponding to the field `condition`. */ + final Expr getCondition() { unified_while_stmt_def(this, result) } + + /** Gets the node corresponding to the field `modifier`. */ + final Modifier getModifier(int i) { unified_while_stmt_modifier(this, i, result) } /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - unified_variable_declarator_def(this, result) or - unified_variable_declarator_value(this, result) + unified_while_stmt_body(this, result) or + unified_while_stmt_def(this, result) or + unified_while_stmt_modifier(this, _, result) } } } diff --git a/unified/ql/lib/unified.dbscheme b/unified/ql/lib/unified.dbscheme index 31b3ec6c3ed5..a58f363853ec 100644 --- a/unified/ql/lib/unified.dbscheme +++ b/unified/ql/lib/unified.dbscheme @@ -132,107 +132,533 @@ overlayChangedFiles( ); /*- Unified dbscheme -*/ -#keyset[unified_apply_pattern, index] -unified_apply_pattern_argument( - int unified_apply_pattern: @unified_apply_pattern ref, +unified_accessor_declaration_body( + unique int unified_accessor_declaration: @unified_accessor_declaration ref, + unique int body: @unified_block ref +); + +#keyset[unified_accessor_declaration, index] +unified_accessor_declaration_modifier( + int unified_accessor_declaration: @unified_accessor_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_accessor_declaration, index] +unified_accessor_declaration_parameter( + int unified_accessor_declaration: @unified_accessor_declaration ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_accessor_declaration_type( + unique int unified_accessor_declaration: @unified_accessor_declaration ref, + unique int type__: @unified_type_expr ref +); + +unified_accessor_declaration_def( + unique int id: @unified_accessor_declaration, + int accessor_kind: @unified_token_accessor_kind ref, + int name: @unified_token_identifier ref +); + +#keyset[unified_argument, index] +unified_argument_modifier( + int unified_argument: @unified_argument ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_argument_name( + unique int unified_argument: @unified_argument ref, + unique int name: @unified_token_identifier ref +); + +unified_argument_def( + unique int id: @unified_argument, + int value: @unified_expr ref +); + +#keyset[unified_array_literal, index] +unified_array_literal_element( + int unified_array_literal: @unified_array_literal ref, + int index: int ref, + unique int element: @unified_expr ref +); + +unified_array_literal_def( + unique int id: @unified_array_literal +); + +unified_assign_expr_def( + unique int id: @unified_assign_expr, + int target: @unified_expr_or_pattern ref, + int value: @unified_expr ref +); + +unified_associated_type_declaration_bound( + unique int unified_associated_type_declaration: @unified_associated_type_declaration ref, + unique int bound: @unified_type_expr ref +); + +#keyset[unified_associated_type_declaration, index] +unified_associated_type_declaration_modifier( + int unified_associated_type_declaration: @unified_associated_type_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_associated_type_declaration_def( + unique int id: @unified_associated_type_declaration, + int name: @unified_token_identifier ref +); + +#keyset[unified_base_type, index] +unified_base_type_modifier( + int unified_base_type: @unified_base_type ref, int index: int ref, - unique int argument: @unified_pattern ref + unique int modifier: @unified_token_modifier ref ); -unified_apply_pattern_def( - unique int id: @unified_apply_pattern, - int constructor: @unified_expr ref +unified_base_type_def( + unique int id: @unified_base_type, + int type__: @unified_type_expr ref ); unified_binary_expr_def( unique int id: @unified_binary_expr, int left: @unified_expr ref, - int operator: @unified_token_operator ref, + int operator: @unified_token_infix_operator ref, int right: @unified_expr ref ); -#keyset[unified_block_stmt, index] -unified_block_stmt_body( - int unified_block_stmt: @unified_block_stmt ref, +#keyset[unified_block, index] +unified_block_stmt( + int unified_block: @unified_block ref, + int index: int ref, + unique int stmt: @unified_stmt ref +); + +unified_block_def( + unique int id: @unified_block +); + +unified_bound_type_constraint_def( + unique int id: @unified_bound_type_constraint, + int bound: @unified_type_expr ref, + int type__: @unified_type_expr ref +); + +unified_break_expr_label( + unique int unified_break_expr: @unified_break_expr ref, + unique int label: @unified_token_identifier ref +); + +unified_break_expr_def( + unique int id: @unified_break_expr +); + +#keyset[unified_bulk_importing_pattern, index] +unified_bulk_importing_pattern_modifier( + int unified_bulk_importing_pattern: @unified_bulk_importing_pattern ref, int index: int ref, - unique int body: @unified_stmt ref + unique int modifier: @unified_token_modifier ref ); -unified_block_stmt_def( - unique int id: @unified_block_stmt +unified_bulk_importing_pattern_def( + unique int id: @unified_bulk_importing_pattern ); #keyset[unified_call_expr, index] unified_call_expr_argument( int unified_call_expr: @unified_call_expr ref, int index: int ref, - unique int argument: @unified_expr ref + unique int argument: @unified_argument ref +); + +#keyset[unified_call_expr, index] +unified_call_expr_modifier( + int unified_call_expr: @unified_call_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref ); unified_call_expr_def( unique int id: @unified_call_expr, - int function: @unified_expr ref + int callee: @unified_expr_or_type ref +); + +unified_catch_clause_guard( + unique int unified_catch_clause: @unified_catch_clause ref, + unique int guard: @unified_expr ref ); -@unified_condition = @unified_expr_condition | @unified_let_pattern_condition | @unified_sequence_condition | @unified_token_unsupported_node +#keyset[unified_catch_clause, index] +unified_catch_clause_modifier( + int unified_catch_clause: @unified_catch_clause ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); -@unified_expr = @unified_binary_expr | @unified_call_expr | @unified_lambda_expr | @unified_member_access_expr | @unified_name_expr | @unified_token_int_literal | @unified_token_string_literal | @unified_token_unsupported_node | @unified_unary_expr +unified_catch_clause_pattern( + unique int unified_catch_clause: @unified_catch_clause ref, + unique int pattern: @unified_pattern ref +); -unified_expr_condition_def( - unique int id: @unified_expr_condition, - int expr: @unified_expr ref +unified_catch_clause_def( + unique int id: @unified_catch_clause, + int body: @unified_block ref +); + +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_base_type( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int base_type: @unified_base_type ref +); + +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_member( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int member: @unified_member ref +); + +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_modifier( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_class_like_declaration_name( + unique int unified_class_like_declaration: @unified_class_like_declaration ref, + unique int name: @unified_token_identifier ref ); -unified_expr_stmt_def( - unique int id: @unified_expr_stmt, +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_type_constraint( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int type_constraint: @unified_type_constraint ref +); + +#keyset[unified_class_like_declaration, index] +unified_class_like_declaration_type_parameter( + int unified_class_like_declaration: @unified_class_like_declaration ref, + int index: int ref, + unique int type_parameter: @unified_type_parameter ref +); + +unified_class_like_declaration_def( + unique int id: @unified_class_like_declaration +); + +unified_compound_assign_expr_def( + unique int id: @unified_compound_assign_expr, + int operator: @unified_token_infix_operator ref, + int target: @unified_expr ref, + int value: @unified_expr ref +); + +#keyset[unified_constructor_declaration, index] +unified_constructor_declaration_modifier( + int unified_constructor_declaration: @unified_constructor_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_constructor_declaration_name( + unique int unified_constructor_declaration: @unified_constructor_declaration ref, + unique int name: @unified_token_identifier ref +); + +#keyset[unified_constructor_declaration, index] +unified_constructor_declaration_parameter( + int unified_constructor_declaration: @unified_constructor_declaration ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_constructor_declaration_def( + unique int id: @unified_constructor_declaration, + int body: @unified_block ref +); + +#keyset[unified_constructor_pattern, index] +unified_constructor_pattern_element( + int unified_constructor_pattern: @unified_constructor_pattern ref, + int index: int ref, + unique int element: @unified_pattern_element ref +); + +#keyset[unified_constructor_pattern, index] +unified_constructor_pattern_modifier( + int unified_constructor_pattern: @unified_constructor_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_constructor_pattern_def( + unique int id: @unified_constructor_pattern, + int constructor: @unified_expr_or_type ref +); + +unified_continue_expr_label( + unique int unified_continue_expr: @unified_continue_expr ref, + unique int label: @unified_token_identifier ref +); + +unified_continue_expr_def( + unique int id: @unified_continue_expr +); + +#keyset[unified_destructor_declaration, index] +unified_destructor_declaration_modifier( + int unified_destructor_declaration: @unified_destructor_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_destructor_declaration_def( + unique int id: @unified_destructor_declaration, + int body: @unified_block ref +); + +unified_do_while_stmt_body( + unique int unified_do_while_stmt: @unified_do_while_stmt ref, + unique int body: @unified_block ref +); + +#keyset[unified_do_while_stmt, index] +unified_do_while_stmt_modifier( + int unified_do_while_stmt: @unified_do_while_stmt ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_do_while_stmt_def( + unique int id: @unified_do_while_stmt, + int condition: @unified_expr ref +); + +unified_equality_type_constraint_def( + unique int id: @unified_equality_type_constraint, + int left: @unified_type_expr ref, + int right: @unified_type_expr ref +); + +@unified_expr = @unified_array_literal | @unified_assign_expr | @unified_binary_expr | @unified_block | @unified_break_expr | @unified_call_expr | @unified_compound_assign_expr | @unified_continue_expr | @unified_function_expr | @unified_if_expr | @unified_key_value_pair | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_expr | @unified_throw_expr | @unified_token_boolean_literal | @unified_token_builtin_expr | @unified_token_empty_expr | @unified_token_float_literal | @unified_token_int_literal | @unified_token_regex_literal | @unified_token_string_literal | @unified_token_super_expr | @unified_token_unsupported_node | @unified_try_expr | @unified_tuple_expr | @unified_type_cast_expr | @unified_type_test_expr | @unified_unary_expr + +unified_expr_equality_pattern_def( + unique int id: @unified_expr_equality_pattern, int expr: @unified_expr ref ); +@unified_expr_or_pattern = @unified_expr | @unified_pattern + +@unified_expr_or_type = @unified_expr | @unified_type_expr + +unified_for_each_stmt_body( + unique int unified_for_each_stmt: @unified_for_each_stmt ref, + unique int body: @unified_block ref +); + +unified_for_each_stmt_guard( + unique int unified_for_each_stmt: @unified_for_each_stmt ref, + unique int guard: @unified_expr ref +); + +#keyset[unified_for_each_stmt, index] +unified_for_each_stmt_modifier( + int unified_for_each_stmt: @unified_for_each_stmt ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_for_each_stmt_def( + unique int id: @unified_for_each_stmt, + int iterable: @unified_expr ref, + int pattern: @unified_pattern ref +); + +unified_function_declaration_body( + unique int unified_function_declaration: @unified_function_declaration ref, + unique int body: @unified_block ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_modifier( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_parameter( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_function_declaration_return_type( + unique int unified_function_declaration: @unified_function_declaration ref, + unique int return_type: @unified_type_expr ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_type_constraint( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int type_constraint: @unified_type_constraint ref +); + +#keyset[unified_function_declaration, index] +unified_function_declaration_type_parameter( + int unified_function_declaration: @unified_function_declaration ref, + int index: int ref, + unique int type_parameter: @unified_type_parameter ref +); + +unified_function_declaration_def( + unique int id: @unified_function_declaration, + int name: @unified_token_identifier ref +); + +#keyset[unified_function_expr, index] +unified_function_expr_capture_declaration( + int unified_function_expr: @unified_function_expr ref, + int index: int ref, + unique int capture_declaration: @unified_variable_declaration ref +); + +#keyset[unified_function_expr, index] +unified_function_expr_modifier( + int unified_function_expr: @unified_function_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_function_expr, index] +unified_function_expr_parameter( + int unified_function_expr: @unified_function_expr ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_function_expr_return_type( + unique int unified_function_expr: @unified_function_expr ref, + unique int return_type: @unified_type_expr ref +); + +unified_function_expr_def( + unique int id: @unified_function_expr, + int body: @unified_block ref +); + +#keyset[unified_function_type_expr, index] +unified_function_type_expr_parameter( + int unified_function_type_expr: @unified_function_type_expr ref, + int index: int ref, + unique int parameter: @unified_parameter ref +); + +unified_function_type_expr_def( + unique int id: @unified_function_type_expr, + int return_type: @unified_type_expr ref +); + +#keyset[unified_generic_type_expr, index] +unified_generic_type_expr_type_argument( + int unified_generic_type_expr: @unified_generic_type_expr ref, + int index: int ref, + unique int type_argument: @unified_type_expr ref +); + +unified_generic_type_expr_def( + unique int id: @unified_generic_type_expr, + int base: @unified_type_expr ref +); + unified_guard_if_stmt_def( unique int id: @unified_guard_if_stmt, - int condition: @unified_condition ref, - int else: @unified_stmt ref + int condition: @unified_expr ref, + int else: @unified_block ref +); + +unified_if_expr_else( + unique int unified_if_expr: @unified_if_expr ref, + unique int else: @unified_expr ref +); + +unified_if_expr_then( + unique int unified_if_expr: @unified_if_expr ref, + unique int then: @unified_expr ref ); -unified_if_stmt_else( - unique int unified_if_stmt: @unified_if_stmt ref, - unique int else: @unified_stmt ref +unified_if_expr_def( + unique int id: @unified_if_expr, + int condition: @unified_expr ref ); -unified_if_stmt_then( - unique int unified_if_stmt: @unified_if_stmt ref, - unique int then: @unified_stmt ref +#keyset[unified_import_declaration, index] +unified_import_declaration_modifier( + int unified_import_declaration: @unified_import_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref ); -unified_if_stmt_def( - unique int id: @unified_if_stmt, - int condition: @unified_condition ref +unified_import_declaration_pattern( + unique int unified_import_declaration: @unified_import_declaration ref, + unique int pattern: @unified_pattern ref ); -@unified_lambda_expr_body_type = @unified_expr | @unified_stmt +unified_import_declaration_def( + unique int id: @unified_import_declaration, + int imported_expr: @unified_expr ref +); -#keyset[unified_lambda_expr, index] -unified_lambda_expr_parameter( - int unified_lambda_expr: @unified_lambda_expr ref, +#keyset[unified_initializer_declaration, index] +unified_initializer_declaration_modifier( + int unified_initializer_declaration: @unified_initializer_declaration ref, int index: int ref, - unique int parameter: @unified_parameter ref + unique int modifier: @unified_token_modifier ref ); -unified_lambda_expr_def( - unique int id: @unified_lambda_expr, - int body: @unified_lambda_expr_body_type ref +unified_initializer_declaration_def( + unique int id: @unified_initializer_declaration, + int body: @unified_block ref ); -unified_let_pattern_condition_def( - unique int id: @unified_let_pattern_condition, - int pattern: @unified_pattern ref, +unified_key_value_pair_def( + unique int id: @unified_key_value_pair, + int key__: @unified_expr ref, int value: @unified_expr ref ); +unified_labeled_stmt_def( + unique int id: @unified_labeled_stmt, + int label: @unified_token_identifier ref, + int stmt: @unified_stmt ref +); + +#keyset[unified_map_literal, index] +unified_map_literal_element( + int unified_map_literal: @unified_map_literal ref, + int index: int ref, + unique int element: @unified_expr ref +); + +unified_map_literal_def( + unique int id: @unified_map_literal +); + +@unified_member = @unified_accessor_declaration | @unified_associated_type_declaration | @unified_class_like_declaration | @unified_constructor_declaration | @unified_destructor_declaration | @unified_function_declaration | @unified_initializer_declaration | @unified_token_unsupported_node | @unified_type_alias_declaration | @unified_variable_declaration + unified_member_access_expr_def( unique int id: @unified_member_access_expr, - int member: @unified_token_identifier ref, - int target: @unified_expr ref + int base: @unified_expr_or_type ref, + int member: @unified_token_identifier ref ); unified_name_expr_def( @@ -240,83 +666,358 @@ unified_name_expr_def( int identifier: @unified_token_identifier ref ); +#keyset[unified_name_pattern, index] +unified_name_pattern_modifier( + int unified_name_pattern: @unified_name_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_name_pattern_def( + unique int id: @unified_name_pattern, + int identifier: @unified_token_identifier ref +); + +unified_named_type_expr_qualifier( + unique int unified_named_type_expr: @unified_named_type_expr ref, + unique int qualifier: @unified_type_expr ref +); + +unified_named_type_expr_def( + unique int id: @unified_named_type_expr, + int name: @unified_token_identifier ref +); + +@unified_operator = @unified_token_infix_operator | @unified_token_postfix_operator | @unified_token_prefix_operator + +unified_operator_syntax_declaration_fixity( + unique int unified_operator_syntax_declaration: @unified_operator_syntax_declaration ref, + unique int fixity: @unified_token_fixity ref +); + +#keyset[unified_operator_syntax_declaration, index] +unified_operator_syntax_declaration_modifier( + int unified_operator_syntax_declaration: @unified_operator_syntax_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_operator_syntax_declaration_precedence( + unique int unified_operator_syntax_declaration: @unified_operator_syntax_declaration ref, + unique int precedence: @unified_expr ref +); + +unified_operator_syntax_declaration_def( + unique int id: @unified_operator_syntax_declaration, + int name: @unified_token_identifier ref +); + +unified_parameter_default( + unique int unified_parameter: @unified_parameter ref, + unique int default: @unified_expr ref +); + +unified_parameter_external_name( + unique int unified_parameter: @unified_parameter ref, + unique int external_name: @unified_token_identifier ref +); + +#keyset[unified_parameter, index] +unified_parameter_modifier( + int unified_parameter: @unified_parameter ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_parameter_pattern( + unique int unified_parameter: @unified_parameter ref, + unique int pattern: @unified_pattern ref +); + +unified_parameter_type( + unique int unified_parameter: @unified_parameter ref, + unique int type__: @unified_type_expr ref +); + unified_parameter_def( - unique int id: @unified_parameter, + unique int id: @unified_parameter +); + +@unified_pattern = @unified_bulk_importing_pattern | @unified_constructor_pattern | @unified_expr_equality_pattern | @unified_name_pattern | @unified_token_ignore_pattern | @unified_token_unsupported_node | @unified_tuple_pattern + +unified_pattern_element_key( + unique int unified_pattern_element: @unified_pattern_element ref, + unique int key__: @unified_token_identifier ref +); + +#keyset[unified_pattern_element, index] +unified_pattern_element_modifier( + int unified_pattern_element: @unified_pattern_element ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_pattern_element_def( + unique int id: @unified_pattern_element, int pattern: @unified_pattern ref ); -@unified_pattern = @unified_apply_pattern | @unified_token_ignore_pattern | @unified_token_unsupported_node | @unified_tuple_pattern | @unified_var_pattern +unified_pattern_guard_expr_def( + unique int id: @unified_pattern_guard_expr, + int pattern: @unified_pattern ref, + int value: @unified_expr ref +); + +unified_return_expr_value( + unique int unified_return_expr: @unified_return_expr ref, + unique int value: @unified_expr ref +); + +unified_return_expr_def( + unique int id: @unified_return_expr +); + +@unified_stmt = @unified_accessor_declaration | @unified_class_like_declaration | @unified_constructor_declaration | @unified_destructor_declaration | @unified_do_while_stmt | @unified_expr | @unified_for_each_stmt | @unified_function_declaration | @unified_guard_if_stmt | @unified_import_declaration | @unified_labeled_stmt | @unified_operator_syntax_declaration | @unified_type_alias_declaration | @unified_variable_declaration | @unified_while_stmt -#keyset[unified_sequence_condition, index] -unified_sequence_condition_stmt( - int unified_sequence_condition: @unified_sequence_condition ref, +unified_switch_case_guard( + unique int unified_switch_case: @unified_switch_case ref, + unique int guard: @unified_expr ref +); + +#keyset[unified_switch_case, index] +unified_switch_case_modifier( + int unified_switch_case: @unified_switch_case ref, int index: int ref, - unique int stmt: @unified_stmt ref + unique int modifier: @unified_token_modifier ref ); -unified_sequence_condition_def( - unique int id: @unified_sequence_condition, - int condition: @unified_condition ref +#keyset[unified_switch_case, index] +unified_switch_case_pattern( + int unified_switch_case: @unified_switch_case ref, + int index: int ref, + unique int pattern: @unified_pattern ref ); -@unified_stmt = @unified_block_stmt | @unified_expr_stmt | @unified_guard_if_stmt | @unified_if_stmt | @unified_token_empty_stmt | @unified_token_unsupported_node | @unified_variable_declaration_stmt +unified_switch_case_def( + unique int id: @unified_switch_case, + int body: @unified_block ref +); -@unified_top_level_body_type = @unified_expr | @unified_stmt +#keyset[unified_switch_expr, index] +unified_switch_expr_case( + int unified_switch_expr: @unified_switch_expr ref, + int index: int ref, + unique int case__: @unified_switch_case ref +); -#keyset[unified_top_level, index] -unified_top_level_body( - int unified_top_level: @unified_top_level ref, +#keyset[unified_switch_expr, index] +unified_switch_expr_modifier( + int unified_switch_expr: @unified_switch_expr ref, int index: int ref, - unique int body: @unified_top_level_body_type ref + unique int modifier: @unified_token_modifier ref +); + +unified_switch_expr_def( + unique int id: @unified_switch_expr, + int value: @unified_expr ref +); + +unified_throw_expr_value( + unique int unified_throw_expr: @unified_throw_expr ref, + unique int value: @unified_expr ref +); + +unified_throw_expr_def( + unique int id: @unified_throw_expr ); unified_top_level_def( - unique int id: @unified_top_level + unique int id: @unified_top_level, + int body: @unified_block ref +); + +#keyset[unified_try_expr, index] +unified_try_expr_catch_clause( + int unified_try_expr: @unified_try_expr ref, + int index: int ref, + unique int catch_clause: @unified_catch_clause ref +); + +#keyset[unified_try_expr, index] +unified_try_expr_modifier( + int unified_try_expr: @unified_try_expr ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_try_expr_def( + unique int id: @unified_try_expr, + int body: @unified_block ref +); + +#keyset[unified_tuple_expr, index] +unified_tuple_expr_element( + int unified_tuple_expr: @unified_tuple_expr ref, + int index: int ref, + unique int element: @unified_expr ref +); + +unified_tuple_expr_def( + unique int id: @unified_tuple_expr ); #keyset[unified_tuple_pattern, index] unified_tuple_pattern_element( int unified_tuple_pattern: @unified_tuple_pattern ref, int index: int ref, - unique int element: @unified_pattern ref + unique int element: @unified_pattern_element ref +); + +#keyset[unified_tuple_pattern, index] +unified_tuple_pattern_modifier( + int unified_tuple_pattern: @unified_tuple_pattern ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref ); unified_tuple_pattern_def( unique int id: @unified_tuple_pattern ); +unified_tuple_type_element_name( + unique int unified_tuple_type_element: @unified_tuple_type_element ref, + unique int name: @unified_token_identifier ref +); + +unified_tuple_type_element_def( + unique int id: @unified_tuple_type_element, + int type__: @unified_type_expr ref +); + +#keyset[unified_tuple_type_expr, index] +unified_tuple_type_expr_element( + int unified_tuple_type_expr: @unified_tuple_type_expr ref, + int index: int ref, + unique int element: @unified_tuple_type_element ref +); + +unified_tuple_type_expr_def( + unique int id: @unified_tuple_type_expr +); + +#keyset[unified_type_alias_declaration, index] +unified_type_alias_declaration_modifier( + int unified_type_alias_declaration: @unified_type_alias_declaration ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +#keyset[unified_type_alias_declaration, index] +unified_type_alias_declaration_type_constraint( + int unified_type_alias_declaration: @unified_type_alias_declaration ref, + int index: int ref, + unique int type_constraint: @unified_type_constraint ref +); + +#keyset[unified_type_alias_declaration, index] +unified_type_alias_declaration_type_parameter( + int unified_type_alias_declaration: @unified_type_alias_declaration ref, + int index: int ref, + unique int type_parameter: @unified_type_parameter ref +); + +unified_type_alias_declaration_def( + unique int id: @unified_type_alias_declaration, + int name: @unified_token_identifier ref, + int type__: @unified_type_expr ref +); + +unified_type_cast_expr_def( + unique int id: @unified_type_cast_expr, + int expr: @unified_expr ref, + int operator: @unified_token_infix_operator ref, + int type__: @unified_type_expr ref +); + +@unified_type_constraint = @unified_bound_type_constraint | @unified_equality_type_constraint + +@unified_type_expr = @unified_function_type_expr | @unified_generic_type_expr | @unified_named_type_expr | @unified_token_inferred_type_expr | @unified_token_unsupported_node | @unified_tuple_type_expr + +unified_type_parameter_bound( + unique int unified_type_parameter: @unified_type_parameter ref, + unique int bound: @unified_type_expr ref +); + +#keyset[unified_type_parameter, index] +unified_type_parameter_modifier( + int unified_type_parameter: @unified_type_parameter ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_type_parameter_def( + unique int id: @unified_type_parameter, + int name: @unified_token_identifier ref +); + +unified_type_test_expr_def( + unique int id: @unified_type_test_expr, + int expr: @unified_expr ref, + int operator: @unified_token_infix_operator ref, + int type__: @unified_type_expr ref +); + +unified_type_test_pattern_def( + unique int id: @unified_type_test_pattern, + int pattern: @unified_pattern ref, + int type__: @unified_type_expr ref +); + unified_unary_expr_def( unique int id: @unified_unary_expr, int operand: @unified_expr ref, - int operator: @unified_token_operator ref + int operator: @unified_operator ref ); -unified_var_pattern_def( - unique int id: @unified_var_pattern, - int identifier: @unified_token_identifier ref -); - -#keyset[unified_variable_declaration_stmt, index] -unified_variable_declaration_stmt_variable_declarator( - int unified_variable_declaration_stmt: @unified_variable_declaration_stmt ref, +#keyset[unified_variable_declaration, index] +unified_variable_declaration_modifier( + int unified_variable_declaration: @unified_variable_declaration ref, int index: int ref, - unique int variable_declarator: @unified_variable_declarator ref + unique int modifier: @unified_token_modifier ref ); -unified_variable_declaration_stmt_def( - unique int id: @unified_variable_declaration_stmt +unified_variable_declaration_type( + unique int unified_variable_declaration: @unified_variable_declaration ref, + unique int type__: @unified_type_expr ref ); -unified_variable_declarator_value( - unique int unified_variable_declarator: @unified_variable_declarator ref, +unified_variable_declaration_value( + unique int unified_variable_declaration: @unified_variable_declaration ref, unique int value: @unified_expr ref ); -unified_variable_declarator_def( - unique int id: @unified_variable_declarator, +unified_variable_declaration_def( + unique int id: @unified_variable_declaration, int pattern: @unified_pattern ref ); +unified_while_stmt_body( + unique int unified_while_stmt: @unified_while_stmt ref, + unique int body: @unified_block ref +); + +#keyset[unified_while_stmt, index] +unified_while_stmt_modifier( + int unified_while_stmt: @unified_while_stmt ref, + int index: int ref, + unique int modifier: @unified_token_modifier ref +); + +unified_while_stmt_def( + unique int id: @unified_while_stmt, + int condition: @unified_expr ref +); + unified_tokeninfo( unique int id: @unified_token, int kind: int ref, @@ -324,13 +1025,24 @@ unified_tokeninfo( ); case @unified_token.kind of - 1 = @unified_token_empty_stmt -| 2 = @unified_token_identifier -| 3 = @unified_token_ignore_pattern -| 4 = @unified_token_int_literal -| 5 = @unified_token_operator -| 6 = @unified_token_string_literal -| 7 = @unified_token_unsupported_node + 1 = @unified_token_accessor_kind +| 2 = @unified_token_boolean_literal +| 3 = @unified_token_builtin_expr +| 4 = @unified_token_empty_expr +| 5 = @unified_token_fixity +| 6 = @unified_token_float_literal +| 7 = @unified_token_identifier +| 8 = @unified_token_ignore_pattern +| 9 = @unified_token_inferred_type_expr +| 10 = @unified_token_infix_operator +| 11 = @unified_token_int_literal +| 12 = @unified_token_modifier +| 13 = @unified_token_postfix_operator +| 14 = @unified_token_prefix_operator +| 15 = @unified_token_regex_literal +| 16 = @unified_token_string_literal +| 17 = @unified_token_super_expr +| 18 = @unified_token_unsupported_node ; @@ -340,7 +1052,7 @@ unified_trivia_tokeninfo( string value: string ref ); -@unified_ast_node = @unified_apply_pattern | @unified_binary_expr | @unified_block_stmt | @unified_call_expr | @unified_expr_condition | @unified_expr_stmt | @unified_guard_if_stmt | @unified_if_stmt | @unified_lambda_expr | @unified_let_pattern_condition | @unified_member_access_expr | @unified_name_expr | @unified_parameter | @unified_sequence_condition | @unified_token | @unified_top_level | @unified_trivia_token | @unified_tuple_pattern | @unified_unary_expr | @unified_var_pattern | @unified_variable_declaration_stmt | @unified_variable_declarator +@unified_ast_node = @unified_accessor_declaration | @unified_argument | @unified_array_literal | @unified_assign_expr | @unified_associated_type_declaration | @unified_base_type | @unified_binary_expr | @unified_block | @unified_bound_type_constraint | @unified_break_expr | @unified_bulk_importing_pattern | @unified_call_expr | @unified_catch_clause | @unified_class_like_declaration | @unified_compound_assign_expr | @unified_constructor_declaration | @unified_constructor_pattern | @unified_continue_expr | @unified_destructor_declaration | @unified_do_while_stmt | @unified_equality_type_constraint | @unified_expr_equality_pattern | @unified_for_each_stmt | @unified_function_declaration | @unified_function_expr | @unified_function_type_expr | @unified_generic_type_expr | @unified_guard_if_stmt | @unified_if_expr | @unified_import_declaration | @unified_initializer_declaration | @unified_key_value_pair | @unified_labeled_stmt | @unified_map_literal | @unified_member_access_expr | @unified_name_expr | @unified_name_pattern | @unified_named_type_expr | @unified_operator_syntax_declaration | @unified_parameter | @unified_pattern_element | @unified_pattern_guard_expr | @unified_return_expr | @unified_switch_case | @unified_switch_expr | @unified_throw_expr | @unified_token | @unified_top_level | @unified_trivia_token | @unified_try_expr | @unified_tuple_expr | @unified_tuple_pattern | @unified_tuple_type_element | @unified_tuple_type_expr | @unified_type_alias_declaration | @unified_type_cast_expr | @unified_type_parameter | @unified_type_test_expr | @unified_type_test_pattern | @unified_unary_expr | @unified_variable_declaration | @unified_while_stmt unified_ast_node_location( unique int node: @unified_ast_node ref, From 166406acbbab33c9050a7761c42dad278ad763a9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 8 Jun 2026 15:23:05 +0200 Subject: [PATCH 09/29] Unified: Elaborate a bit more on AGENTS.md --- unified/AGENTS.md | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/unified/AGENTS.md b/unified/AGENTS.md index aa5007a56561..6c7d697896f0 100644 --- a/unified/AGENTS.md +++ b/unified/AGENTS.md @@ -3,25 +3,21 @@ This is a CodeQL extractor based on tree-sitter. ## Building -To build the extractor, run `scripts/create-extractor-pack.sh` - -## Editing the Swift grammar -The vendored tree-sitter-swift grammar lives at -`extractor/tree-sitter-swift/`. After editing `grammar.js` (or any other -grammar source), run `scripts/regenerate-grammar.sh` to: -- regenerate `extractor/tree-sitter-swift/src/{parser.c, grammar.json, - node-types.json}` (and the `src/tree_sitter/*.h` headers) via - `tree-sitter generate`; and -- refresh `extractor/tree-sitter-swift/node-types.yml`, the - human-readable companion to `src/node-types.json` produced by yeast's - `node_types_yaml` binary. - -`node-types.yml` is the recommended review surface for grammar changes — -it shows the impact of a grammar tweak on the named node kinds, fields, -and child types in a form much easier to read than the raw JSON. - -## Extractor Testing -- To run extractor tests, run `cargo test` in the `extractor` directory. +- To build the extractor, run `scripts/create-extractor-pack.sh` + +## Swift Parser +- The Swift parser is defined by `extractor/tree-sitter-swift/grammar.js` and can be edited if needed. + +- After editing the grammar, always run `scripts/regenerate-grammar.sh`. + +- The raw parse tree is described by `extractor/tree-sitter-swift/node-types.yml` and should be reviewed after grammar changes. + +## AST Mapping +- The target AST shape is described by `extractor/ast_types.yml`. + +- The mapping from the parse tree to the target AST is found in `extractor/src/languages/swift/swift.rs` + +- To run tests for the parser and mapping, run `cargo test` in the `extractor` directory. - Do not edit the printed ASTs in `extractor/test/corpus` directly. To regenerate the ASTs, run `scripts/update-corpus.sh`. From 6c74cd31e49949995205e08026b01fa7ab9415cb Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 8 Jun 2026 15:23:05 +0200 Subject: [PATCH 10/29] Yeast: use child locations instead of rule target Previously, when a node was synthesized it would always take the location from the node that matched the current rule. This resulted in overly broad locations however. For (foo #{bar}) we now take the location of the 'bar' node. For non-leaf nodes we merge all its child node locations. --- shared/yeast-macros/src/parse.rs | 6 +- shared/yeast/src/build.rs | 15 +++++ shared/yeast/src/lib.rs | 99 ++++++++++++++++++++++++++++++++ shared/yeast/tests/test.rs | 44 ++++++++++++++ 4 files changed, 162 insertions(+), 2 deletions(-) diff --git a/shared/yeast-macros/src/parse.rs b/shared/yeast-macros/src/parse.rs index 0980d41f9567..4b27b9804392 100644 --- a/shared/yeast-macros/src/parse.rs +++ b/shared/yeast-macros/src/parse.rs @@ -396,8 +396,10 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result BuildCtx<'a> { .create_named_token_with_range(kind, value.to_string(), self.source_range) } + /// Create a leaf node with fixed content and an optional preferred source range. + /// If `source_range` is `None`, falls back to this context's inherited range. + pub fn literal_with_source_range( + &mut self, + kind: &'static str, + value: &str, + source_range: Option, + ) -> Id { + self.ast.create_named_token_with_range( + kind, + value.to_string(), + source_range.or(self.source_range), + ) + } + /// Create a leaf node with an auto-generated unique name. pub fn fresh(&mut self, kind: &'static str, name: &str) -> Id { let generated = self.fresh.resolve(name); diff --git a/shared/yeast/src/lib.rs b/shared/yeast/src/lib.rs index 40b90567537c..9c3a4ad41141 100644 --- a/shared/yeast/src/lib.rs +++ b/shared/yeast/src/lib.rs @@ -58,12 +58,30 @@ pub trait YeastDisplay { fn yeast_to_string(&self, ast: &Ast) -> String; } +/// Optional source range for values used in `#{expr}` interpolations. +/// +/// By default this returns `None`, so synthesized leaves inherit the matched +/// rule's source range. `NodeRef` returns the referenced node's range, letting +/// `(kind #{capture})` carry the captured node's location. +pub trait YeastSourceRange { + fn yeast_source_range(&self, ast: &Ast) -> Option; +} + impl YeastDisplay for NodeRef { fn yeast_to_string(&self, ast: &Ast) -> String { ast.source_text(self.0) } } +impl YeastSourceRange for NodeRef { + fn yeast_source_range(&self, ast: &Ast) -> Option { + ast.get_node(self.0).and_then(|n| match &n.content { + NodeContent::Range(r) => Some(r.clone()), + _ => n.source_range, + }) + } +} + macro_rules! impl_yeast_display_via_display { ($($t:ty),* $(,)?) => { $( @@ -72,6 +90,12 @@ macro_rules! impl_yeast_display_via_display { ::std::string::ToString::to_string(self) } } + + impl YeastSourceRange for $t { + fn yeast_source_range(&self, _ast: &Ast) -> Option { + None + } + } )* }; } @@ -90,6 +114,12 @@ impl YeastDisplay for &T { } } +impl YeastSourceRange for &T { + fn yeast_source_range(&self, ast: &Ast) -> Option { + (**self).yeast_source_range(ast) + } +} + pub const CHILD_FIELD: u16 = u16::MAX; #[derive(Debug)] @@ -368,6 +398,15 @@ impl Ast { is_named: bool, source_range: Option, ) -> Id { + let source_range = match &content { + // Parsed nodes already carry an exact source range in their content. + NodeContent::Range(_) => source_range, + // Synthesized nodes derive location from children when possible, + // and fall back to the inherited rule-match range otherwise. + _ => self + .union_source_range_of_children(&fields) + .or(source_range), + }; let id = self.nodes.len(); self.nodes.push(Node { kind, @@ -383,6 +422,66 @@ impl Ast { id } + fn union_source_range_of_children( + &self, + fields: &BTreeMap>, + ) -> Option { + let mut start_byte: Option = None; + let mut end_byte: Option = None; + let mut start_point = tree_sitter::Point { row: 0, column: 0 }; + let mut end_point = tree_sitter::Point { row: 0, column: 0 }; + + for child_ids in fields.values() { + for &child_id in child_ids { + let Some(child) = self.get_node(child_id) else { + continue; + }; + + let child_start_byte = child.start_byte(); + let child_end_byte = child.end_byte(); + + // Skip children that carry no usable location. + if child_start_byte == 0 && child_end_byte == 0 { + continue; + } + + match start_byte { + None => { + start_byte = Some(child_start_byte); + start_point = child.start_position(); + } + Some(current_start) if child_start_byte < current_start => { + start_byte = Some(child_start_byte); + start_point = child.start_position(); + } + _ => {} + } + + match end_byte { + None => { + end_byte = Some(child_end_byte); + end_point = child.end_position(); + } + Some(current_end) if child_end_byte > current_end => { + end_byte = Some(child_end_byte); + end_point = child.end_position(); + } + _ => {} + } + } + } + + match (start_byte, end_byte) { + (Some(start_byte), Some(end_byte)) => Some(tree_sitter::Range { + start_byte, + end_byte, + start_point, + end_point, + }), + _ => None, + } + } + pub fn create_named_token(&mut self, kind: &'static str, content: String) -> Id { self.create_named_token_with_range(kind, content, None) } diff --git a/shared/yeast/tests/test.rs b/shared/yeast/tests/test.rs index 3ae8e2072d9f..069132d09237 100644 --- a/shared/yeast/tests/test.rs +++ b/shared/yeast/tests/test.rs @@ -18,6 +18,16 @@ fn run_and_dump(input: &str, rules: Vec) -> String { run_phased_and_dump(input, vec![Phase::new("test", PhaseKind::Repeating, rules)]) } +/// Helper: parse Ruby source with custom rules and return the transformed AST. +fn run_and_ast(input: &str, rules: Vec) -> Ast { + let lang: tree_sitter::Language = tree_sitter_ruby::LANGUAGE.into(); + let schema = + yeast::node_types_yaml::schema_from_yaml_with_language(OUTPUT_SCHEMA_YAML, &lang).unwrap(); + let phases = vec![Phase::new("test", PhaseKind::Repeating, rules)]; + let runner = Runner::with_schema(lang, &schema, &phases); + runner.run(input).unwrap() +} + /// Helper: parse Ruby source with a custom output schema and multiple /// rule phases, return dump. fn run_phased_and_dump(input: &str, phases: Vec) -> String { @@ -1172,3 +1182,37 @@ fn test_hash_brace_renders_integer_expression() { "#, ); } + +/// Regression test: `(kind #{capture})` should inherit the captured node's +/// source location, not the full source range of the matched rule root. +#[test] +fn test_hash_brace_uses_capture_location_for_leaf() { + let rule = rule!( + (call + method: (identifier) @name + receiver: (identifier) @recv + ) + => + (call + method: (identifier #{name}) + receiver: (identifier #{recv}) + arguments: (argument_list) + ) + ); + + let ast = run_and_ast("foo.bar()", vec![rule]); + + let mut bar_ids: Vec = Vec::new(); + for id in ast.reachable_node_ids() { + let Some(node) = ast.get_node(id) else { continue; }; + if node.kind() == "identifier" && ast.source_text(id) == "bar" { + bar_ids.push(id); + } + } + + assert_eq!(bar_ids.len(), 1, "expected exactly one identifier 'bar'"); + let bar = ast.get_node(bar_ids[0]).unwrap(); + + assert_eq!(bar.start_byte(), 4); + assert_eq!(bar.end_byte(), 7); +} From 0e9d17b59cf9f70e187e27a2d286897504ea59dd Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:20 +0200 Subject: [PATCH 11/29] 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 d7ad7d871294..bc2f15ebd49f 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 0afea480a19f..32004a0973d6 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 afafc1e69ef2..69437de0111a 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 600e1126cbff..f621a2ca6659 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 9f9ffeb070a8..c4c486e995b9 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 0a8210a4cf76..5a5832c54506 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 5044831a869b..53c60b797967 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 8b9f3410d35d..0ce418219fa4 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 f1a4a5fcdb26..30726ad873f6 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 572e9181a681..e4d0e30f6885 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 0bebaa1238f3..4eab79716426 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 1911ddd02b1e..ea3b898f98ba 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" From 4e9c3fb436535269947d8edfa0153a1ef4377075 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:22 +0200 Subject: [PATCH 12/29] unified/swift: add literals, names, and operator expression mappings --- .../extractor/src/languages/swift/swift.rs | 68 ++++++++++ .../extractor/tests/corpus/swift/desugar.txt | 16 ++- .../extractor/tests/corpus/swift/literals.txt | 19 +-- .../tests/corpus/swift/operators.txt | 117 ++++++++++++++++-- 4 files changed, 198 insertions(+), 22 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index bc2f15ebd49f..c9f01a9e7ee5 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -15,6 +15,74 @@ fn translation_rules() -> Vec { // Declarations may be wrapped in local/global wrapper nodes. rule!((global_declaration _ @inner) => {inner}), rule!((local_declaration _ @inner) => {inner}), + // ---- Literals ---- + rule!((integer_literal) => (int_literal)), + rule!((hex_literal) => (int_literal)), + rule!((bin_literal) => (int_literal)), + rule!((oct_literal) => (int_literal)), + rule!((real_literal) => (float_literal)), + rule!((boolean_literal) => (boolean_literal)), + rule!("nil" => (builtin_expr)), + rule!((special_literal) => (builtin_expr)), + rule!((line_string_literal) => (string_literal)), + rule!((multi_line_string_literal) => (string_literal)), + rule!((raw_string_literal) => (string_literal)), + rule!((regex_literal) => (regex_literal)), + // ---- Names ---- + rule!((simple_identifier) @id => (name_expr identifier: (identifier #{id}))), + // A referenceable_operator (e.g. `+` used as a value, as in `reduce(0, +)`) + // is treated as a name reference to the operator symbol. + rule!((referenceable_operator) @op => (name_expr identifier: (identifier #{op}))), + // ---- Operators ---- + // All binary operators share the lhs/op/rhs shape. + rule!((additive_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((multiplicative_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((comparison_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((equality_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((conjunction_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((disjunction_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((infix_expression lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + // Range expression `a.. (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + // Open-ended ranges `a...` / `...b` + rule!((open_end_range_expression start: @l) => (unary_expr operator: (postfix_operator "...") operand: {l})), + rule!((open_start_range_expression end: @r) => (unary_expr operator: (prefix_operator "...") operand: {r})), + // Custom operator declaration: `[prefix|infix|postfix] operator OP [: PrecedenceGroup]`. + // The fixity keyword is an anonymous child of `operator_declaration`, so we + // dispatch on it with one rule per keyword. + rule!( + (operator_declaration "prefix" (referenceable_operator _ @op) (simple_identifier)? @prec) + => + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "prefix") precedence: {..prec}) + ), + rule!( + (operator_declaration "postfix" (referenceable_operator _ @op) (simple_identifier)? @prec) + => + (operator_syntax_declaration name: (identifier #{op}) fixity: (fixity "postfix") precedence: {..prec}) + ), + rule!( + (operator_declaration "infix" (referenceable_operator _ @op) (simple_identifier)? @prec) + => + (operator_syntax_declaration + name: (identifier #{op}) + fixity: (fixity "infix") + precedence: {..prec}) + ), + rule!((bitwise_operation lhs: @l op: @op rhs: @r) => (binary_expr left: {l} operator: (infix_operator #{op}) right: {r})), + rule!((nil_coalescing_expression value: @l if_nil: @r) => (binary_expr left: {l} operator: (infix_operator "??") right: {r})), + // Leading-dot member shorthand (e.g. `.some`, `.foo`) means member access + // on a contextually inferred type. + rule!((prefix_expression operation: "." target: @member) => (member_access_expr base: (inferred_type_expr) member: (identifier #{member}))), + // Prefix unary operators + rule!((prefix_expression operation: @op target: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})), + // Postfix unary operators + rule!((postfix_expression operation: @op target: @operand) => (unary_expr operator: (postfix_operator #{op}) operand: {operand})), + // Parenthesised single-value tuple is a grouping expression; pass through. + // Multi-value tuples become tuple_expr. + rule!((tuple_expression value: _* @v) => (tuple_expr element: {..v})), + // Blocks contain statement* directly. + rule!((block statement: _+ @stmts) => (block stmt: {..stmts})), + rule!((block) => (block)), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt index c4c486e995b9..abf8a23e9ea0 100644 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ b/unified/extractor/tests/corpus/swift/desugar.txt @@ -18,7 +18,11 @@ source_file top_level body: block - stmt: unsupported_node "1 + 2" + stmt: + binary_expr + operator: infix_operator "+" + left: int_literal "1" + right: int_literal "2" === Another additive expression is desugared @@ -40,4 +44,12 @@ source_file top_level body: block - stmt: unsupported_node "foo + bar" + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "foo" + right: + name_expr + identifier: identifier "bar" diff --git a/unified/extractor/tests/corpus/swift/literals.txt b/unified/extractor/tests/corpus/swift/literals.txt index 53c60b797967..bf0e4aae5609 100644 --- a/unified/extractor/tests/corpus/swift/literals.txt +++ b/unified/extractor/tests/corpus/swift/literals.txt @@ -14,7 +14,7 @@ source_file top_level body: block - stmt: unsupported_node "42" + stmt: int_literal "42" === Negative integer literal @@ -35,7 +35,10 @@ source_file top_level body: block - stmt: unsupported_node "-7" + stmt: + unary_expr + operand: int_literal "7" + operator: prefix_operator "-" === Floating-point literal @@ -53,7 +56,7 @@ source_file top_level body: block - stmt: unsupported_node "3.14" + stmt: float_literal "3.14" === Boolean literals @@ -75,8 +78,8 @@ top_level body: block stmt: - unsupported_node "true" - unsupported_node "false" + boolean_literal "true" + boolean_literal "false" === Nil literal @@ -94,7 +97,7 @@ source_file top_level body: block - stmt: unsupported_node "nil" + stmt: builtin_expr "nil" === String literal @@ -114,7 +117,7 @@ source_file top_level body: block - stmt: unsupported_node "\"hello\"" + stmt: string_literal "\"hello\"" === String with interpolation @@ -137,4 +140,4 @@ source_file top_level body: block - stmt: unsupported_node "\"hello \\(name)\"" + stmt: string_literal "\"hello \\(name)\"" diff --git a/unified/extractor/tests/corpus/swift/operators.txt b/unified/extractor/tests/corpus/swift/operators.txt index 30726ad873f6..d912a1085dc5 100644 --- a/unified/extractor/tests/corpus/swift/operators.txt +++ b/unified/extractor/tests/corpus/swift/operators.txt @@ -18,7 +18,15 @@ source_file top_level body: block - stmt: unsupported_node "a + b" + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Subtraction @@ -40,7 +48,15 @@ source_file top_level body: block - stmt: unsupported_node "a - b" + stmt: + binary_expr + operator: infix_operator "-" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Multiplication @@ -62,7 +78,15 @@ source_file top_level body: block - stmt: unsupported_node "a * b" + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Division @@ -84,7 +108,15 @@ source_file top_level body: block - stmt: unsupported_node "a / b" + stmt: + binary_expr + operator: infix_operator "/" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Operator precedence: addition and multiplication @@ -110,7 +142,21 @@ source_file top_level body: block - stmt: unsupported_node "a + b * c" + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "b" + right: + name_expr + identifier: identifier "c" === Parenthesised expression @@ -140,7 +186,13 @@ source_file top_level body: block - stmt: unsupported_node "(a + b) * c" + stmt: + binary_expr + operator: infix_operator "*" + left: tuple_expr "(a + b)" + right: + name_expr + identifier: identifier "c" === Comparison @@ -162,7 +214,15 @@ source_file top_level body: block - stmt: unsupported_node "a < b" + stmt: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Equality @@ -184,7 +244,15 @@ source_file top_level body: block - stmt: unsupported_node "a == b" + stmt: + binary_expr + operator: infix_operator "==" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Logical and @@ -206,7 +274,15 @@ source_file top_level body: block - stmt: unsupported_node "a && b" + stmt: + binary_expr + operator: infix_operator "&&" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Logical or @@ -228,7 +304,15 @@ source_file top_level body: block - stmt: unsupported_node "a || b" + stmt: + binary_expr + operator: infix_operator "||" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" === Logical not @@ -249,7 +333,12 @@ source_file top_level body: block - stmt: unsupported_node "!a" + stmt: + unary_expr + operand: + name_expr + identifier: identifier "a" + operator: prefix_operator "!" === Range operator @@ -271,4 +360,8 @@ source_file top_level body: block - stmt: unsupported_node "1...10" + stmt: + binary_expr + operator: infix_operator "..." + left: int_literal "1" + right: int_literal "10" From d17fd2d964dbcc97f6f9583cdf063bee30cd0ba2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:23 +0200 Subject: [PATCH 13/29] unified/swift: add variable/property/accessor and enum mappings --- .../extractor/src/languages/swift/swift.rs | 203 ++++++++++++++++++ .../extractor/tests/corpus/swift/closures.txt | 32 ++- .../tests/corpus/swift/collections.txt | 66 +++++- .../tests/corpus/swift/control-flow.txt | 8 +- .../corpus/swift/optionals-and-errors.txt | 60 +++++- .../tests/corpus/swift/variables.txt | 72 ++++++- 6 files changed, 414 insertions(+), 27 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c9f01a9e7ee5..5bed312ed6db 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -83,6 +83,209 @@ fn translation_rules() -> Vec { // Blocks contain statement* directly. rule!((block statement: _+ @stmts) => (block stmt: {..stmts})), rule!((block) => (block)), + // ---- Variables ---- + // property_binding rules — these produce variable_declaration and/or accessor_declaration + // nodes for individual declarators. The outer property_declaration rule splices these out + // and attaches binding/modifiers from the parent. + + // Computed property with explicit accessors (get/set/modify) → + // a sequence of accessor_declaration nodes, each with the property name + // attached. Subsequent accessors will be tagged chained_declaration by + // the outer property_declaration rule. + rule!( + (property_binding + name: @pattern + type: _? @ty + computed_value: (computed_property accessor: _+ @accessors)) + => + {..{ + let name_text = __yeast_ctx.ast.source_text(pattern.into()); + let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); + let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); + for &acc_id in &acc_ids { + let ident = __yeast_ctx.literal("identifier", &name_text); + __yeast_ctx.prepend_field(acc_id, "name", ident); + for &ty_id in ty_ids.iter().rev() { + __yeast_ctx.prepend_field(acc_id, "type", ty_id); + } + } + acc_ids + }} + ), + // Computed property: shorthand getter (no explicit get/set, just statements) → + // a single accessor_declaration with kind "get". + rule!( + (property_binding + name: (pattern bound_identifier: @name) + type: _? @ty + computed_value: (computed_property statement: _* @body)) + => + (accessor_declaration + name: (identifier #{name}) + type: {..ty} + accessor_kind: (accessor_kind "get") + body: (block stmt: {..body})) + ), + // Stored property with willSet/didSet observers (initializer optional) → + // variable_declaration followed by one accessor_declaration per observer, + // each carrying the property name. Subsequent items are tagged + // chained_declaration by the outer property_declaration rule. + rule!( + (property_binding + name: (pattern bound_identifier: @name) + type: _? @ty + value: _? @val + observers: (willset_didset_block willset: _? @ws didset: _? @ds)) + => + {..{ + let name_text = __yeast_ctx.ast.source_text(name.into()); + let val_ids: Vec = val.iter().map(|&v| v.into()).collect(); + let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); + let mut obs_ids: Vec = Vec::new(); + obs_ids.extend(ws.iter().map(|&o| { let id: usize = o.into(); id })); + obs_ids.extend(ds.iter().map(|&o| { let id: usize = o.into(); id })); + let ident_for_var = __yeast_ctx.literal("identifier", &name_text); + let pat = __yeast_ctx.node("name_pattern", vec![("identifier", vec![ident_for_var])]); + let mut var_fields: Vec<(&str, Vec)> = vec![("pattern", vec![pat])]; + if !ty_ids.is_empty() { + var_fields.push(("type", ty_ids)); + } + if !val_ids.is_empty() { + var_fields.push(("value", val_ids)); + } + let var_id = __yeast_ctx.node("variable_declaration", var_fields); + let mut result = vec![var_id]; + for obs_id in obs_ids { + let ident = __yeast_ctx.literal("identifier", &name_text); + __yeast_ctx.prepend_field(obs_id, "name", ident); + result.push(obs_id); + } + result + }} + ), + // property_binding with any pattern name (identifier or destructuring) + rule!( + (property_binding + name: @pattern + type: _? @ty + value: _? @val) + => + (variable_declaration + pattern: {pattern} + type: {..ty} + value: {..val}) + ), + // property_declaration: splice declarators (each may translate to multiple nodes — + // variable_declaration and/or accessor_declaration), and attach the binding modifier + // (let/var) and any outer modifiers to each. All children after the first additionally + // get a synthetic chained_declaration modifier so the grouping can be recovered. + rule!( + (property_declaration + binding: (value_binding_pattern mutability: @binding_kind) + declarator: _* @decls + (modifiers)* @mods) + => + {..{ + let binding_text = __yeast_ctx.ast.source_text(binding_kind.into()); + let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); + let decl_ids: Vec = decls.iter().map(|&d| d.into()).collect(); + for (i, &decl_id) in decl_ids.iter().enumerate() { + if i > 0 { + let chained = __yeast_ctx.literal("modifier", "chained_declaration"); + __yeast_ctx.prepend_field(decl_id, "modifier", chained); + } + for &mod_id in mod_ids.iter().rev() { + __yeast_ctx.prepend_field(decl_id, "modifier", mod_id); + } + let binding_mod = __yeast_ctx.literal("modifier", &binding_text); + __yeast_ctx.prepend_field(decl_id, "modifier", binding_mod); + } + decl_ids + }} + ), + // ---- Enums ---- + // enum_type_parameter → parameter (with optional name as pattern). + rule!( + (enum_type_parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (enum_type_parameter type: @ty) + => + (parameter type: {ty}) + ), + // enum_case_entry with associated values → class_like_declaration containing + // a constructor whose parameters are the data parameters. + rule!( + (enum_case_entry + name: @name + data_contents: (enum_type_parameters parameter: _* @params)) + => + (class_like_declaration + modifier: (modifier "enum_case") + name: (identifier #{name}) + member: (constructor_declaration parameter: {..params} body: (block))) + ), + // enum_case_entry with explicit raw value → variable_declaration with that value. + rule!( + (enum_case_entry name: @name raw_value: @val) + => + (variable_declaration + modifier: (modifier "enum_case") + pattern: (name_pattern identifier: (identifier #{name})) + value: {val}) + ), + // enum_case_entry without associated values → variable_declaration tagged enum_case. + rule!( + (enum_case_entry name: @name) + => + (variable_declaration + modifier: (modifier "enum_case") + pattern: (name_pattern identifier: (identifier #{name}))) + ), + // enum_entry: flatten case entries; attach outer modifiers to each, and + // chained_declaration on every entry after the first. + rule!( + (enum_entry case: _+ @cases (modifiers)* @mods) + => + {..{ + let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); + let case_ids: Vec = cases.iter().map(|&c| c.into()).collect(); + for (i, &case_id) in case_ids.iter().enumerate() { + if i > 0 { + let chained = __yeast_ctx.literal("modifier", "chained_declaration"); + __yeast_ctx.prepend_field(case_id, "modifier", chained); + } + for &mod_id in mod_ids.iter().rev() { + __yeast_ctx.prepend_field(case_id, "modifier", mod_id); + } + } + case_ids + }} + ), + // Plain assignment: `x = expr` + rule!( + (assignment operator: "=" target: (directly_assignable_expression expr: @target) result: @value) + => + (assign_expr target: {target} value: {value}) + ), + // Compound assignment: `x += expr` etc. + rule!( + (assignment operator: @op target: (directly_assignable_expression expr: @target) result: @value) + => + (compound_assign_expr target: {target} operator: (infix_operator #{op}) value: {value}) + ), + // Unwrap `type` wrapper node + rule!((type name: @inner) => {inner}), + // `directly_assignable_expression` is just a wrapper; unwrap it + rule!((directly_assignable_expression expr: @inner) => {inner}), + // Pattern with bound_identifier → name_pattern + rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), + // Tuple pattern (destructuring) + rule!((pattern (pattern)* @elems) => (tuple_pattern element: {..elems})), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 32004a0973d6..3bfe46ff4118 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -51,7 +51,13 @@ source_file top_level body: block - stmt: unsupported_node "let f = { (x: Int) -> Int in x * 2 }" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ (x: Int) -> Int in x * 2 }" === Closure with shorthand parameters @@ -85,7 +91,13 @@ source_file top_level body: block - stmt: unsupported_node "let f = { $0 + $1 }" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ $0 + $1 }" === Trailing closure @@ -170,7 +182,13 @@ source_file top_level body: block - stmt: unsupported_node "let f = { [weak self] in self?.doThing() }" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ [weak self] in self?.doThing() }" === Multi-statement closure @@ -245,4 +263,10 @@ 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}" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "f" + value: unsupported_node "{ (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 69437de0111a..7a7544604d4f 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -29,7 +29,13 @@ source_file top_level body: block - stmt: unsupported_node "let xs = [1, 2, 3]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "xs" + value: unsupported_node "[1, 2, 3]" === Empty array literal with type @@ -71,7 +77,14 @@ source_file top_level body: block - stmt: unsupported_node "let xs: [Int] = []" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "xs" + type: unsupported_node ": [Int]" + value: unsupported_node "[]" === Dictionary literal @@ -111,7 +124,13 @@ source_file top_level body: block - stmt: unsupported_node "let d = [\"a\": 1, \"b\": 2]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "d" + value: unsupported_node "[\"a\": 1, \"b\": 2]" === Set literal @@ -162,7 +181,14 @@ source_file top_level body: block - stmt: unsupported_node "let s: Set = [1, 2, 3]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "s" + type: unsupported_node ": Set" + value: unsupported_node "[1, 2, 3]" === Tuple literal @@ -200,7 +226,13 @@ source_file top_level body: block - stmt: unsupported_node "let t = (1, \"two\", 3.0)" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "t" + value: tuple_expr "(1, \"two\", 3.0)" === Subscript access @@ -243,7 +275,13 @@ source_file top_level body: block - stmt: unsupported_node "let first = xs[0]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "first" + value: unsupported_node "xs[0]" === Dictionary subscript @@ -286,7 +324,13 @@ source_file top_level body: block - stmt: unsupported_node "let v = d[\"key\"]" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "v" + value: unsupported_node "d[\"key\"]" === Tuple member access @@ -319,4 +363,10 @@ source_file top_level body: block - stmt: unsupported_node "let n = t.0" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: unsupported_node "t.0" diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index f621a2ca6659..5384df4abe4e 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -288,7 +288,13 @@ source_file top_level body: block - stmt: unsupported_node "let y = x > 0 ? 1 : -1" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: unsupported_node "x > 0 ? 1 : -1" === Switch statement diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index e4d0e30f6885..2a921d9a3021 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -35,7 +35,14 @@ source_file top_level body: block - stmt: unsupported_node "let x: Int? = nil" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node ": Int?" + value: builtin_expr "nil" === Optional chaining @@ -77,7 +84,13 @@ source_file top_level body: block - stmt: unsupported_node "let n = obj?.foo?.bar" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: unsupported_node "obj?.foo?.bar" === Force unwrap @@ -108,7 +121,18 @@ source_file top_level body: block - stmt: unsupported_node "let n = opt!" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + unary_expr + operand: + name_expr + identifier: identifier "opt" + operator: postfix_operator "!" === Nil-coalescing @@ -139,7 +163,19 @@ source_file top_level body: block - stmt: unsupported_node "let n = opt ?? 0" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "n" + value: + binary_expr + operator: infix_operator "??" + left: + name_expr + identifier: identifier "opt" + right: int_literal "0" === Throwing function @@ -265,7 +301,13 @@ source_file top_level body: block - stmt: unsupported_node "let result = try? foo()" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "result" + value: unsupported_node "try? foo()" === Try! expression @@ -303,4 +345,10 @@ source_file top_level body: block - stmt: unsupported_node "let result = try! foo()" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "result" + value: unsupported_node "try! foo()" diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index ea3b898f98ba..d7ff7a110a14 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -24,7 +24,13 @@ source_file top_level body: block - stmt: unsupported_node "let x = 1" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" === Var binding @@ -52,7 +58,13 @@ source_file top_level body: block - stmt: unsupported_node "var x = 1" + stmt: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" === Let with type annotation @@ -89,7 +101,14 @@ source_file top_level body: block - stmt: unsupported_node "let x: Int = 1" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node ": Int" + value: int_literal "1" === Var without initialiser @@ -125,7 +144,13 @@ source_file top_level body: block - stmt: unsupported_node "var x: Int" + stmt: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node ": Int" === Tuple destructuring binding @@ -163,7 +188,13 @@ source_file top_level body: block - stmt: unsupported_node "let (a, b) = pair" + stmt: + variable_declaration + modifier: modifier "let" + pattern: tuple_pattern "(a, b)" + value: + name_expr + identifier: identifier "pair" === Multiple bindings on one line @@ -196,7 +227,21 @@ source_file top_level body: block - stmt: unsupported_node "let x = 1, y = 2" + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: int_literal "1" + variable_declaration + modifier: + modifier "let" + modifier "chained_declaration" + pattern: + name_pattern + identifier: identifier "y" + value: int_literal "2" === Assignment @@ -220,7 +265,12 @@ source_file top_level body: block - stmt: unsupported_node "x = 1" + stmt: + assign_expr + target: + name_expr + identifier: identifier "x" + value: int_literal "1" === Compound assignment @@ -244,4 +294,10 @@ source_file top_level body: block - stmt: unsupported_node "x += 1" + stmt: + compound_assign_expr + operator: infix_operator "+=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" From 8f747a355cba668c5a5a4a819837ef642638b519 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:25 +0200 Subject: [PATCH 14/29] unified/swift: add function and parameter mappings --- .../extractor/src/languages/swift/swift.rs | 100 +++++++++++ .../tests/corpus/swift/collections.txt | 25 ++- .../tests/corpus/swift/functions.txt | 168 +++++++++++++++++- .../corpus/swift/optionals-and-errors.txt | 15 +- 4 files changed, 294 insertions(+), 14 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5bed312ed6db..db04f56ffab3 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -286,6 +286,106 @@ fn translation_rules() -> Vec { rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), // Tuple pattern (destructuring) rule!((pattern (pattern)* @elems) => (tuple_pattern element: {..elems})), + // ---- Functions ---- + // Function declaration + // Function declaration (return type optional, body statements optional). + rule!( + (function_declaration + name: @name + parameter: _* @params + return_type: _? @ret + body: (block statement: _* @body_stmts)) + => + (function_declaration + name: (identifier #{name}) + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body_stmts})) + ), + // Parameters are wrapped in function_parameter, which also carries + // optional default values. + rule!( + (function_parameter parameter: @p default_value: _? @def) + => + {..{ + let p_id: usize = p.into(); + for &d in def.iter().rev() { + __yeast_ctx.prepend_field(p_id, "default", d.into()); + } + vec![p_id] + }} + ), + // Parameter with external name and type + rule!( + (parameter external_name: @ext name: @name) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (parameter external_name: @ext name: @name type: @ty) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + // Parameter with just name and type (no external name) + rule!( + (parameter name: @name) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + // Reference to a function, f(x:y:z:). This is parsed as a call with a single argument with multiple reference_specifier labels. + // We don't want downstream QL to try to handle this as a call_expr with a weird argument, so explicitly mark it as unsupported for now. + // In the future we probably want to translate this to a lambda expression. + rule!( + (call_expression suffix: (call_suffix arguments: (value_arguments argument: (value_argument reference_specifier: _+) @ref_arg))) + => + (unsupported_node) + ), + // Call expression: function(args...) + rule!( + (call_expression function: @func suffix: (call_suffix arguments: (value_arguments argument: (value_argument)* @args))) + => + (call_expr callee: {func} argument: {..args}) + ), + // Value argument with label (value: _ matches both named nodes and anonymous tokens like nil) + rule!( + (value_argument name: (value_argument_label name: @label) value: @val) + => + (argument name: (identifier #{label}) value: {val}) + ), + // Value argument without label + rule!( + (value_argument value: @val) + => + (argument value: {val}) + ), + // Navigation expression → member_access_expr + rule!( + (navigation_expression target: @target suffix: (navigation_suffix suffix: @member)) + => + (member_access_expr base: {target} member: (identifier #{member})) + ), + // Return / break / continue, one rule per keyword. + // The anonymous "return"/"break"/"continue" keywords are matched as + // string literals. + rule!((control_transfer_statement kind: "return" result: _? @val) => (return_expr value: {..val})), + rule!((control_transfer_statement kind: "break" result: @lbl) => (break_expr label: (identifier #{lbl}))), + rule!((control_transfer_statement kind: "break") => (break_expr)), + rule!((control_transfer_statement kind: "continue" result: @lbl) => (continue_expr label: (identifier #{lbl}))), + rule!((control_transfer_statement kind: "continue") => (continue_expr)), + rule!((control_transfer_statement kind: (throw_keyword) result: @val) => (throw_expr value: {val})), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 7a7544604d4f..795bca6e6e2d 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -281,7 +281,14 @@ top_level pattern: name_pattern identifier: identifier "first" - value: unsupported_node "xs[0]" + value: + call_expr + argument: + argument + value: int_literal "0" + callee: + name_expr + identifier: identifier "xs" === Dictionary subscript @@ -330,7 +337,14 @@ top_level pattern: name_pattern identifier: identifier "v" - value: unsupported_node "d[\"key\"]" + value: + call_expr + argument: + argument + value: string_literal "\"key\"" + callee: + name_expr + identifier: identifier "d" === Tuple member access @@ -369,4 +383,9 @@ top_level pattern: name_pattern identifier: identifier "n" - value: unsupported_node "t.0" + value: + member_access_expr + base: + name_expr + identifier: identifier "t" + member: identifier "0" diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index 5a5832c54506..ea1e3ad9378d 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -32,7 +32,19 @@ source_file top_level body: block - stmt: unsupported_node "func greet() {\n print(\"hello\")\n}" + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"hello\"" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" === Function with parameters and return type @@ -96,7 +108,34 @@ source_file top_level body: block - stmt: unsupported_node "func add(_ a: Int, _ b: Int) -> Int {\n return a + b\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "a" + right: + name_expr + identifier: identifier "b" + name: identifier "add" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "a" + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "b" + return_type: unsupported_node "Int" === Function with named parameters @@ -143,7 +182,27 @@ source_file top_level body: block - stmt: unsupported_node "func greet(person name: String) {\n print(name)\n}" + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "name" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" + parameter: + parameter + external_name: identifier "person" + pattern: + name_pattern + identifier: identifier "name" === Function with default parameter value @@ -192,7 +251,27 @@ source_file top_level body: block - stmt: unsupported_node "func greet(name: String = \"world\") {\n print(name)\n}" + stmt: + function_declaration + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "name" + callee: + name_expr + identifier: identifier "print" + name: identifier "greet" + parameter: + parameter + default: string_literal "\"world\"" + pattern: + name_pattern + identifier: identifier "name" === Variadic function @@ -258,7 +337,35 @@ source_file top_level body: block - stmt: unsupported_node "func sum(_ values: Int...) -> Int {\n return values.reduce(0, +)\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + call_expr + argument: + argument + value: int_literal "0" + argument + value: + name_expr + identifier: identifier "+" + callee: + member_access_expr + base: + name_expr + identifier: identifier "values" + member: identifier "reduce" + name: identifier "sum" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "values" + return_type: unsupported_node "Int" === Function call @@ -287,7 +394,16 @@ source_file top_level body: block - stmt: unsupported_node "foo(1, 2)" + stmt: + call_expr + argument: + argument + value: int_literal "1" + argument + value: int_literal "2" + callee: + name_expr + identifier: identifier "foo" === Function call with labelled arguments @@ -319,7 +435,15 @@ source_file top_level body: block - stmt: unsupported_node "greet(person: \"Bob\")" + stmt: + call_expr + argument: + argument + name: identifier "person" + value: string_literal "\"Bob\"" + callee: + name_expr + identifier: identifier "greet" === Method call @@ -351,7 +475,17 @@ source_file top_level body: block - stmt: unsupported_node "list.append(1)" + stmt: + call_expr + argument: + argument + value: int_literal "1" + callee: + member_access_expr + base: + name_expr + identifier: identifier "list" + member: identifier "append" === Generic function @@ -404,4 +538,20 @@ source_file top_level body: block - stmt: unsupported_node "func identity(_ x: T) -> T {\n return x\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: + name_expr + identifier: identifier "x" + name: identifier "identity" + parameter: + parameter + external_name: identifier "_" + pattern: + name_pattern + identifier: identifier "x" + return_type: unsupported_node "T" diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 2a921d9a3021..6b8b62b2c2a2 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -90,7 +90,10 @@ top_level pattern: name_pattern identifier: identifier "n" - value: unsupported_node "obj?.foo?.bar" + value: + member_access_expr + base: unsupported_node "obj?.foo?" + member: identifier "bar" === Force unwrap @@ -212,7 +215,15 @@ source_file top_level body: block - stmt: unsupported_node "func read() throws -> String {\n return \"\"\n}" + stmt: + function_declaration + body: + block + stmt: + return_expr + value: string_literal "\"\"" + name: identifier "read" + return_type: unsupported_node "String" === Do-catch From 790d4f11be262adc32c64c53c448aa5138d07f74 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:27 +0200 Subject: [PATCH 15/29] unified/swift: add closure and capture mappings --- .../extractor/src/languages/swift/swift.rs | 64 +++++++++++ .../extractor/tests/corpus/swift/closures.txt | 105 +++++++++++++++++- 2 files changed, 164 insertions(+), 5 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index db04f56ffab3..c31b2ca882b7 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -386,6 +386,70 @@ fn translation_rules() -> Vec { rule!((control_transfer_statement kind: "continue" result: @lbl) => (continue_expr label: (identifier #{lbl}))), rule!((control_transfer_statement kind: "continue") => (continue_expr)), rule!((control_transfer_statement kind: (throw_keyword) result: @val) => (throw_expr value: {val})), + // ---- Closures ---- + // Lambda literal with optional type header (parameters + optional return type). + // The return_type capture is optional, so this rule covers both cases. + rule!( + (lambda_literal + attribute: _* @attrs + captures: (capture_list item: _* @captures)? + type: (lambda_function_type + params: (lambda_function_type_parameters parameter: _* @params) + return_type: _? @ret)? + statement: _* @body) + => + (function_expr + modifier: {..attrs} + capture_declaration: {..captures} + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body})) + ), + // capture_list_item with ownership modifier (e.g. [weak self], [unowned x]) + rule!( + (capture_list_item ownership: _? @ownership name: @name value: _? @val) + => + (variable_declaration + modifier: {..ownership} + pattern: (name_pattern identifier: (identifier #{name})) + value: {..val}) + ), + // Lambda parameter with type and optional external name + rule!( + (lambda_parameter external_name: @ext name: @name type: @ty) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (lambda_parameter name: @name type: @ty) + => + (parameter + pattern: (name_pattern identifier: (identifier #{name})) + type: {ty}) + ), + rule!( + (lambda_parameter external_name: @ext name: @name) + => + (parameter + external_name: (identifier #{ext}) + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (lambda_parameter name: @name) + => + (parameter pattern: (name_pattern identifier: (identifier #{name}))) + ), + // Call expression with trailing closure (no value_arguments) + rule!( + (call_expression function: @func suffix: (call_suffix lambda: (lambda_literal) @closure)) + => + (call_expr + callee: {func} + argument: (argument value: {closure})) + ), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 3bfe46ff4118..2d18062bb2cb 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -57,7 +57,24 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ (x: Int) -> Int in x * 2 }" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "x" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node "Int" + return_type: unsupported_node "Int" === Closure with shorthand parameters @@ -97,7 +114,19 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ $0 + $1 }" + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "$0" + right: + name_expr + identifier: identifier "$1" === Trailing closure @@ -131,7 +160,27 @@ source_file top_level body: block - stmt: unsupported_node "xs.map { $0 * 2 }" + stmt: + call_expr + argument: + argument + value: + function_expr + body: + block + stmt: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "$0" + right: int_literal "2" + callee: + member_access_expr + base: + name_expr + identifier: identifier "xs" + member: identifier "map" === Closure with capture list @@ -188,7 +237,22 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ [weak self] in self?.doThing() }" + value: + function_expr + body: + block + stmt: + call_expr + callee: + member_access_expr + base: unsupported_node "self?" + member: identifier "doThing" + capture_declaration: + variable_declaration + modifier: unsupported_node "weak" <-- ERROR: The field variable_declaration.modifier should contain modifier, but got unsupported_node + pattern: + name_pattern + identifier: identifier "self" === Multi-statement closure @@ -269,4 +333,35 @@ top_level pattern: name_pattern identifier: identifier "f" - value: unsupported_node "{ (x: Int) -> Int in\n let y = x + 1\n return y * 2\n}" + value: + function_expr + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + binary_expr + operator: infix_operator "+" + left: + name_expr + identifier: identifier "x" + right: int_literal "1" + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "y" + right: int_literal "2" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "x" + type: unsupported_node "Int" + return_type: unsupported_node "Int" From 938396a751b843f0eeac89fd6a40aa86a4cab4d7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:29 +0200 Subject: [PATCH 16/29] unified/swift: add control-flow and loop mappings --- .../extractor/src/languages/swift/swift.rs | 108 ++++++++ .../tests/corpus/swift/control-flow.txt | 253 +++++++++++++++++- .../extractor/tests/corpus/swift/loops.txt | 151 ++++++++++- 3 files changed, 498 insertions(+), 14 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index c31b2ca882b7..9c595c51aac3 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -450,6 +450,114 @@ fn translation_rules() -> Vec { callee: {func} argument: (argument value: {closure})) ), + // ---- Control flow ---- + rule!( + (if_statement condition: _* @cond body: @then_body else_branch: _? @else_stmts) + => + (if_expr + condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + then: {then_body} + else: {..else_stmts}) + ), + // Guard statement + rule!( + (guard_statement condition: _* @cond body: (block statement: _* @else_stmts)) + => + (guard_if_stmt + condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) + else: (block stmt: {..else_stmts})) + ), + // Ternary expression → if_expr + rule!( + (ternary_expression condition: @cond if_true: @then_val if_false: @else_val) + => + (if_expr condition: {cond} then: {then_val} else: {else_val}) + ), + // Switch statement + rule!( + (switch_statement expr: @val entry: (switch_entry)* @cases) + => + (switch_expr value: {val} case: {..cases}) + ), + // Switch entry with patterns and body + rule!( + (switch_entry pattern: (switch_pattern)* @pats statement: _* @body) + => + (switch_case pattern: {..pats} body: (block stmt: {..body})) + ), + // Switch entry: default case (no patterns) + rule!( + (switch_entry default: (default_keyword) statement: _* @body) + => + (switch_case body: (block stmt: {..body})) + ), + // Switch pattern — unwrap to inner pattern + rule!((switch_pattern (pattern)* @inner) => {..inner}), + // if case let x = expr — the pattern is taken as-is (no Optional wrapping) + rule!( + (if_let_binding "case" (value_binding_pattern) bound_identifier: @name _ @val) + => + (pattern_guard_expr + value: {val} + pattern: (name_pattern identifier: (identifier #{name}))) + ), + rule!( + (if_let_binding + pattern: (pattern binding: (value_binding_pattern) bound_identifier: @name) + value: @val) + => + (pattern_guard_expr + value: {val} + pattern: (constructor_pattern + constructor: (member_access_expr base: (named_type_expr name: (identifier "Optional")) member: (identifier "some")) + element: (pattern_element pattern: (name_pattern identifier: (identifier #{name}))))) + ), + // Shorthand if let x (Swift 5.7+) — also semantically .some(x) + rule!( + (if_let_binding + pattern: (pattern binding: (value_binding_pattern) bound_identifier: @name)) + => + (pattern_guard_expr + value: (name_expr identifier: (identifier #{name})) + pattern: (constructor_pattern + constructor: (member_access_expr base: (named_type_expr name: (identifier "Optional")) member: (identifier "some")) + element: (pattern_element pattern: (name_pattern identifier: (identifier #{name}))))) + ), + // If-condition — unwrap (pass through the inner expression/pattern) + rule!((if_condition kind: @inner) => {inner}), + // ---- Loops ---- + // For-in loop with optional where-clause guard. + rule!( + (for_statement + item: @pat + collection: @iter + where: (where_clause expr: @guard)? + body: (block statement: _* @body)) + => + (for_each_stmt + pattern: {pat} + iterable: {iter} + guard: {..guard} + body: (block stmt: {..body})) + ), + // While loop + rule!( + (while_statement condition: _* @cond body: (block statement: _* @body)) + => + (while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body})) + ), + // Repeat-while loop + rule!( + (repeat_while_statement condition: _* @cond body: (block statement: _* @body)) + => + (do_while_stmt condition: {..cond}.reduce_left(first -> {first}, acc, elem -> (binary_expr operator: (infix_operator "&&") left: {acc} right: {elem})) body: (block stmt: {..body})) + ), + // Labeled statement (e.g. `outer: for ...`). Strip the trailing ':' from the label token. + rule!((labeled_statement label: (statement_label) @lbl statement: @stmt) => {..{ + let text = __yeast_ctx.ast.source_text(lbl.into()); + let name = __yeast_ctx.literal("identifier", &text[..text.len() - 1]); + vec![__yeast_ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] + }}), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 5384df4abe4e..680c55898782 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -36,7 +36,27 @@ source_file top_level body: block - stmt: unsupported_node "if x > 0 {\n print(x)\n}" + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" === If-else @@ -93,7 +113,42 @@ source_file top_level body: block - stmt: unsupported_node "if x > 0 {\n print(x)\n} else {\n print(-x)\n}" + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + block + stmt: + call_expr + argument: + argument + value: + unary_expr + operand: + name_expr + identifier: identifier "x" + operator: prefix_operator "-" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" === If-else-if chain @@ -170,7 +225,54 @@ 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}" + stmt: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + if_expr + condition: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + block + stmt: + call_expr + argument: + argument + value: int_literal "3" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: int_literal "2" + callee: + name_expr + identifier: identifier "print" + then: + block + stmt: + call_expr + argument: + argument + value: int_literal "1" + callee: + name_expr + identifier: identifier "print" === If-let optional binding @@ -214,7 +316,38 @@ source_file top_level body: block - stmt: unsupported_node "if let value = optional {\n print(value)\n}" + stmt: + if_expr + condition: + pattern_guard_expr + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "value" + constructor: + member_access_expr + base: + named_type_expr + name: identifier "Optional" + member: identifier "some" + value: + name_expr + identifier: identifier "optional" + then: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "value" + callee: + name_expr + identifier: identifier "print" === Guard let @@ -249,7 +382,29 @@ source_file top_level body: block - stmt: unsupported_node "guard let value = optional else { return }" + stmt: + guard_if_stmt + condition: + pattern_guard_expr + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "value" + constructor: + member_access_expr + base: + named_type_expr + name: identifier "Optional" + member: identifier "some" + value: + name_expr + identifier: identifier "optional" + else: + block + stmt: return_expr "return" === Ternary expression @@ -294,7 +449,20 @@ top_level pattern: name_pattern identifier: identifier "y" - value: unsupported_node "x > 0 ? 1 : -1" + value: + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + else: + unary_expr + operand: int_literal "1" + operator: prefix_operator "-" + then: int_literal "1" === Switch statement @@ -376,7 +544,45 @@ 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}" + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"one\"" + callee: + name_expr + identifier: identifier "print" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"two or three\"" + callee: + name_expr + identifier: identifier "print" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"other\"" + callee: + name_expr + identifier: identifier "print" + value: + name_expr + identifier: identifier "x" === Switch with binding pattern @@ -466,4 +672,35 @@ 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}" + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "r" + callee: + name_expr + identifier: identifier "print" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "s" + callee: + name_expr + identifier: identifier "print" + value: + name_expr + identifier: identifier "shape" diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt index 0ce418219fa4..6fbd7ce18098 100644 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ b/unified/extractor/tests/corpus/swift/loops.txt @@ -38,7 +38,24 @@ source_file top_level body: block - stmt: unsupported_node "for x in [1, 2, 3] {\n print(x)\n}" + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + iterable: unsupported_node "[1, 2, 3]" === For-in over range @@ -79,7 +96,28 @@ source_file top_level body: block - stmt: unsupported_node "for i in 0..<10 {\n print(i)\n}" + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "i" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "i" + iterable: + binary_expr + operator: infix_operator "..<" + left: int_literal "0" + right: int_literal "10" === For-in with where clause @@ -124,7 +162,33 @@ source_file top_level body: block - stmt: unsupported_node "for x in xs where x > 0 {\n print(x)\n}" + stmt: + for_each_stmt + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + guard: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + iterable: + name_expr + identifier: identifier "xs" === While loop @@ -161,7 +225,24 @@ source_file top_level body: block - stmt: unsupported_node "while x > 0 {\n x -= 1\n}" + stmt: + while_stmt + body: + block + stmt: + compound_assign_expr + operator: infix_operator "-=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" === Repeat-while loop @@ -198,7 +279,24 @@ source_file top_level body: block - stmt: unsupported_node "repeat {\n x -= 1\n} while x > 0" + stmt: + do_while_stmt + body: + block + stmt: + compound_assign_expr + operator: infix_operator "-=" + target: + name_expr + identifier: identifier "x" + value: int_literal "1" + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" === Break and continue @@ -263,4 +361,45 @@ 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}" + stmt: + for_each_stmt + body: + block + stmt: + if_expr + condition: + binary_expr + operator: infix_operator "<" + left: + name_expr + identifier: identifier "x" + right: int_literal "0" + then: + block + stmt: continue_expr "continue" + if_expr + condition: + binary_expr + operator: infix_operator ">" + left: + name_expr + identifier: identifier "x" + right: int_literal "100" + then: + block + stmt: break_expr "break" + call_expr + argument: + argument + value: + name_expr + identifier: identifier "x" + callee: + name_expr + identifier: identifier "print" + pattern: + name_pattern + identifier: identifier "x" + iterable: + name_expr + identifier: identifier "xs" From 3522f35ab2b43852fd200fc5220189d02f6a5aa5 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:12:32 +0200 Subject: [PATCH 17/29] unified/swift: add collections, optionals/errors --- .../extractor/src/languages/swift/swift.rs | 75 +++++++++++++++++++ .../extractor/tests/corpus/swift/closures.txt | 2 +- .../tests/corpus/swift/collections.txt | 18 ++++- .../extractor/tests/corpus/swift/loops.txt | 7 +- .../corpus/swift/optionals-and-errors.txt | 52 ++++++++++++- 5 files changed, 144 insertions(+), 10 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 9c595c51aac3..bdb985342f51 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -558,6 +558,81 @@ fn translation_rules() -> Vec { let name = __yeast_ctx.literal("identifier", &text[..text.len() - 1]); vec![__yeast_ctx.node("labeled_stmt", vec![("label", vec![name]), ("stmt", vec![stmt.into()])])] }}), + // ---- Collections ---- + // Array literal + rule!((array_literal element: _* @elems) => (array_literal element: {..elems})), + // Empty array literal + rule!((array_literal) => (array_literal)), + // Dictionary literal — zip keys and values into key_value_pairs + rule!( + (dictionary_literal key: _* @keys value: _* @vals) + => + (map_literal element: {..{ + keys.iter().zip(vals.iter()).map(|(&k, &v)| { + let k_id: usize = k.into(); + let v_id: usize = v.into(); + __yeast_ctx.node("key_value_pair", vec![ + ("key", vec![k_id]), + ("value", vec![v_id]), + ]) + }).collect::>() + }}) + ), + rule!((dictionary_literal element: _* @elems) => (map_literal element: {..elems})), + rule!((dictionary_literal_item key: @k value: @v) => (key_value_pair key: {k} value: {v})), + // ---- Optionals and errors ---- + // Optional chaining — unwrap the marker + rule!((optional_chain_marker expr: @inner) => {inner}), + // try/try?/try! expr → unary_expr with operator "try", "try?" or "try!" + rule!((try_expression (try_operator) @op expr: @inner) => (unary_expr operator: (prefix_operator #{op}) operand: {inner})), + rule!((try_expression operator: (try_operator) @op expr: @inner) => (unary_expr operator: (prefix_operator #{op}) operand: {inner})), + // Do-catch → try_expr + rule!( + (do_statement body: (block statement: _* @body) catch: (catch_block)* @catches) + => + (try_expr + body: (block stmt: {..body}) + catch_clause: {..catches}) + ), + // Catch block with bound identifier; optional where-clause guard. + rule!( + (catch_block + keyword: (catch_keyword) + error: @pattern + where: (where_clause expr: @guard)? + body: (block statement: _* @body)) + => + (catch_clause + pattern: {pattern} + guard: {..guard} + body: (block stmt: {..body})) + ), + // Catch block without error binding + rule!( + (catch_block keyword: (catch_keyword) body: (block statement: _* @body)) + => + (catch_clause body: (block stmt: {..body})) + ), + // Empty catch block: catch {} + rule!( + (catch_block (catch_keyword)) + => + (catch_clause body: (block)) + ), + // Catch block with unhandled pattern — preserve pattern; optional body. + rule!( + (catch_block keyword: (catch_keyword) error: @pat body: (block statement: _* @body)) + => + (catch_clause + pattern: {pat} + body: (block stmt: {..body})) + ), + // As expression (type cast) — as?, as! + rule!((as_expression (as_operator) @op expr: @val type: @ty) => (type_cast_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), + // Check expression (`x is T`) → type_test_expr + rule!((check_expression op: @op target: @val type: @ty) => (type_test_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), + // Await expression → unary_expr with operator "await" + rule!((await_expression expr: @val) => (unary_expr operator: (prefix_operator "await") operand: {val})), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 2d18062bb2cb..638f8a328360 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -245,7 +245,7 @@ top_level call_expr callee: member_access_expr - base: unsupported_node "self?" + base: unsupported_node "self" member: identifier "doThing" capture_declaration: variable_declaration diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 795bca6e6e2d..5ff49dd48999 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -35,7 +35,12 @@ top_level pattern: name_pattern identifier: identifier "xs" - value: unsupported_node "[1, 2, 3]" + value: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" === Empty array literal with type @@ -84,7 +89,7 @@ top_level name_pattern identifier: identifier "xs" type: unsupported_node ": [Int]" - value: unsupported_node "[]" + value: array_literal "[]" === Dictionary literal @@ -130,7 +135,7 @@ top_level pattern: name_pattern identifier: identifier "d" - value: unsupported_node "[\"a\": 1, \"b\": 2]" + value: map_literal "[\"a\": 1, \"b\": 2]" === Set literal @@ -188,7 +193,12 @@ top_level name_pattern identifier: identifier "s" type: unsupported_node ": Set" - value: unsupported_node "[1, 2, 3]" + value: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" === Tuple literal diff --git a/unified/extractor/tests/corpus/swift/loops.txt b/unified/extractor/tests/corpus/swift/loops.txt index 6fbd7ce18098..b0e25debff52 100644 --- a/unified/extractor/tests/corpus/swift/loops.txt +++ b/unified/extractor/tests/corpus/swift/loops.txt @@ -55,7 +55,12 @@ top_level pattern: name_pattern identifier: identifier "x" - iterable: unsupported_node "[1, 2, 3]" + iterable: + array_literal + element: + int_literal "1" + int_literal "2" + int_literal "3" === For-in over range diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 6b8b62b2c2a2..1e4df4274ba1 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -92,7 +92,12 @@ top_level identifier: identifier "n" value: member_access_expr - base: unsupported_node "obj?.foo?" + base: + member_access_expr + base: + name_expr + identifier: identifier "obj" + member: identifier "foo" member: identifier "bar" === @@ -274,7 +279,32 @@ source_file top_level body: block - stmt: unsupported_node "do {\n try foo()\n} catch {\n print(error)\n}" + stmt: + try_expr + body: + block + stmt: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try" + catch_clause: + catch_clause + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "error" + callee: + name_expr + identifier: identifier "print" === Try? expression @@ -318,7 +348,14 @@ top_level pattern: name_pattern identifier: identifier "result" - value: unsupported_node "try? foo()" + value: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try?" === Try! expression @@ -362,4 +399,11 @@ top_level pattern: name_pattern identifier: identifier "result" - value: unsupported_node "try! foo()" + value: + unary_expr + operand: + call_expr + callee: + name_expr + identifier: identifier "foo" + operator: prefix_operator "try!" From 15208b70aa45595222cc795677468a3082794769 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:45:00 +0200 Subject: [PATCH 18/29] Unified: Add import_declaration.scoped_import_kind --- unified/extractor/tree-sitter-swift/grammar.js | 2 +- unified/extractor/tree-sitter-swift/node-types.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/unified/extractor/tree-sitter-swift/grammar.js b/unified/extractor/tree-sitter-swift/grammar.js index 1e63a7eaabb8..37d074eaec2b 100644 --- a/unified/extractor/tree-sitter-swift/grammar.js +++ b/unified/extractor/tree-sitter-swift/grammar.js @@ -1368,7 +1368,7 @@ module.exports = grammar({ seq( field("modifiers", optional($.modifiers)), "import", - optional($._import_kind), + optional(field("scoped_import_kind", $._import_kind)), field("name", $.identifier) ), _import_kind: ($) => diff --git a/unified/extractor/tree-sitter-swift/node-types.yml b/unified/extractor/tree-sitter-swift/node-types.yml index 8e1a4209d74b..837116c13e04 100644 --- a/unified/extractor/tree-sitter-swift/node-types.yml +++ b/unified/extractor/tree-sitter-swift/node-types.yml @@ -351,6 +351,7 @@ named: import_declaration: modifiers?: modifiers name: identifier + scoped_import_kind?: ["class", "enum", "func", "let", "protocol", "struct", "typealias", "var"] infix_expression: lhs: expression op: custom_operator From f36270749379036bd13d7ab8efb9597b3f8ad6db Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:12:47 +0200 Subject: [PATCH 19/29] unified/swift: Imports --- .../extractor/src/languages/swift/swift.rs | 32 +++++ .../extractor/tests/corpus/swift/desugar.txt | 131 ++++++++++++++++++ .../tests/corpus/swift/functions.txt | 94 +++++++++++++ 3 files changed, 257 insertions(+) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index bdb985342f51..5e4a7df6b92d 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -633,6 +633,38 @@ fn translation_rules() -> Vec { rule!((check_expression op: @op target: @val type: @ty) => (type_test_expr expr: {val} operator: (infix_operator #{op}) type: {ty})), // Await expression → unary_expr with operator "await" rule!((await_expression expr: @val) => (unary_expr operator: (prefix_operator "await") operand: {val})), + // A multi-part identifier (for example `Foo.Bar.Baz`) is translated to + // a member_access_expr chain with a name_expr base. + rule!( + (identifier part: _+ @parts) + => + {parts}.reduce_left( + first -> (name_expr identifier: (identifier #{first})), + acc, elem -> (member_access_expr base: {acc} member: (identifier #{elem}))) + ), + // Scoped import declaration (for example `import struct Foo.Bar`): + // flatten the identifier parts into a member_access_expr and bind the + // final segment as a name_pattern. + rule!( + (import_declaration scoped_import_kind: @kind name: (identifier part: _+ @parts) @name modifiers: (modifiers)? @mods) + => + (import_declaration + pattern: (name_pattern identifier: (identifier #{parts.last().unwrap()})) + imported_expr: {name} + modifier: (modifier #{kind}) + modifier: {..mods}) + ), + // Non-scoped import declaration (for example `import Foundation`): + // flatten the identifier parts into a member_access_expr and use a + // bulk_importing_pattern. + rule!( + (import_declaration name: @name modifiers: (modifiers)? @mods) + => + (import_declaration + pattern: (bulk_importing_pattern) + imported_expr: {name} + modifier: {..mods}) + ), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/desugar.txt b/unified/extractor/tests/corpus/swift/desugar.txt index abf8a23e9ea0..1611943bf1ae 100644 --- a/unified/extractor/tests/corpus/swift/desugar.txt +++ b/unified/extractor/tests/corpus/swift/desugar.txt @@ -53,3 +53,134 @@ top_level right: name_expr identifier: identifier "bar" + +=== +Simple import with single name +=== + +import Foundation + +--- + +source_file + statement: + import_declaration + name: + identifier + part: simple_identifier "Foundation" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation" + imported_expr: + name_expr + identifier: identifier "Foundation" + +=== +Import with dotted path (two parts) +=== + +import Foundation.Networking + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Networking" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation.Networking" + imported_expr: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Networking" + +=== +Import with deeply nested path (three parts) +=== + +import Foundation.Networking.URLSession + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Networking" + simple_identifier "URLSession" + +--- + +top_level + body: + block + stmt: + import_declaration + pattern: bulk_importing_pattern "import Foundation.Networking.URLSession" + imported_expr: + member_access_expr + base: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Networking" + member: identifier "URLSession" + +=== +Scoped import uses name_pattern +=== + +import struct Foundation.Date + +--- + +source_file + statement: + import_declaration + name: + identifier + part: + simple_identifier "Foundation" + simple_identifier "Date" + scoped_import_kind: struct + +--- + +top_level + body: + block + stmt: + import_declaration + modifier: modifier "struct" + pattern: + name_pattern + identifier: identifier "Date" + imported_expr: + member_access_expr + base: + name_expr + identifier: identifier "Foundation" + member: identifier "Date" diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index ea1e3ad9378d..ce4b3e7524a4 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -555,3 +555,97 @@ top_level name_pattern identifier: identifier "x" return_type: unsupported_node "T" + +=== +Leading-dot expression value +=== + +let x = .foo + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "x" + value: + prefix_expression + operation: . + target: simple_identifier "foo" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + value: + member_access_expr + base: inferred_type_expr ".foo" + member: identifier "foo" + +=== +Leading-dot expression call +=== + +let y = .some(1) + +--- + +source_file + statement: + property_declaration + binding: + value_binding_pattern + mutability: let + declarator: + property_binding + name: + pattern + bound_identifier: simple_identifier "y" + value: + call_expression + function: + prefix_expression + operation: . + target: simple_identifier "some" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: integer_literal "1" + +--- + +top_level + body: + block + stmt: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + value: + call_expr + argument: + argument + value: int_literal "1" + callee: + member_access_expr + base: inferred_type_expr ".some" + member: identifier "some" From 1e167dfa6b996916abcddc444f2f9e2114d2dbfd Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:42:33 +0200 Subject: [PATCH 20/29] unified/swift: add type and declaration-family mappings --- .../extractor/src/languages/swift/swift.rs | 268 ++++++++++++++++ .../extractor/tests/corpus/swift/closures.txt | 22 +- .../tests/corpus/swift/collections.txt | 13 +- .../tests/corpus/swift/functions.txt | 12 +- .../corpus/swift/optionals-and-errors.txt | 13 +- .../extractor/tests/corpus/swift/types.txt | 285 +++++++++++++++++- .../tests/corpus/swift/variables.txt | 8 +- 7 files changed, 594 insertions(+), 27 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 5e4a7df6b92d..26ec4c1b0ee9 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -665,6 +665,274 @@ fn translation_rules() -> Vec { imported_expr: {name} modifier: {..mods}) ), + // ---- Types and classes ---- + // Self expression → name_expr + rule!((self_expression) => (name_expr identifier: (identifier "self"))), + // Super expression → super_expr + rule!((super_expression) => (super_expr)), + // Modifiers — unwrap to individual modifier children + rule!((modifiers _* @mods) => {..mods}), + rule!((attribute) @m => (modifier #{m})), + rule!((visibility_modifier) @m => (modifier #{m})), + rule!((function_modifier) @m => (modifier #{m})), + rule!((member_modifier) @m => (modifier #{m})), + rule!((mutation_modifier) @m => (modifier #{m})), + rule!((ownership_modifier) @m => (modifier #{m})), + rule!((property_modifier) @m => (modifier #{m})), + rule!((parameter_modifier) @m => (modifier #{m})), + rule!((inheritance_modifier) @m => (modifier #{m})), + rule!((property_behavior_modifier) @m => (modifier #{m})), + // Type annotations — unwrap + rule!((type_annotation type: @inner) => {inner}), + // user_type is split into simple_user_type parts. + // Keep a conservative textual fallback to avoid dropping type information. + rule!((user_type) @ty => (named_type_expr name: (identifier #{ty}))), + // Tuple type → tuple_type_expr + rule!((tuple_type element: _* @elems) => (tuple_type_expr element: {..elems})), + rule!((tuple_type_item name: @name type: @ty) => (tuple_type_element name: (identifier #{name}) type: {ty})), + rule!((tuple_type_item type: @ty) => (tuple_type_element type: {ty})), + // Array type `[T]` → generic_type_expr with Array base + rule!((array_type element: @e) => (generic_type_expr + base: (named_type_expr name: (identifier "Array")) + type_argument: {e})), + // Dictionary type `[K: V]` → generic_type_expr with Dictionary base + rule!((dictionary_type key: @k value: @v) => (generic_type_expr + base: (named_type_expr name: (identifier "Dictionary")) + type_argument: {k} + type_argument: {v})), + // Optional type `T?` → generic_type_expr with Optional base + rule!((optional_type wrapped: @w) => (generic_type_expr + base: (named_type_expr name: (identifier "Optional")) + type_argument: {w})), + // Function type `(Params) -> Ret` → function_type_expr. + rule!((function_type parameter: _* @ps return_type: @ret) => (function_type_expr parameter: {..ps} return_type: {ret})), + rule!((function_type_parameter name: @name type: @ty) => (parameter external_name: (identifier #{name}) type: {ty})), + rule!((function_type_parameter type: @ty) => (parameter type: {ty})), + // Selector expression: `#selector(inner)` -- not yet supported + rule!( + (selector_expression _ @inner) + => + (unsupported_node) + ), + // Key path expressions are currently unsupported. + rule!((key_path_expression) => (unsupported_node)), + // Inheritance specifier → base_type + rule!((inheritance_specifier inherits_from: @ty) => (base_type type: {ty})), + // Class declaration with body containing members + rule!( + (class_declaration + declaration_kind: @kind + name: @name + body: (class_body member: _* @members) + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier #{kind}) + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases} + member: {..members}) + ), + // Enum class declaration: same as a regular class but with an enum body. + rule!( + (class_declaration + declaration_kind: @kind + name: @name + body: (enum_class_body member: _* @members) + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier #{kind}) + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases} + member: {..members}) + ), + // Class declaration with empty body + rule!( + (class_declaration + declaration_kind: @kind + name: @name + body: _ + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier #{kind}) + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases}) + ), + // Protocol declaration + rule!( + (protocol_declaration + name: @name + body: (protocol_body member: _* @members) + (inheritance_specifier)* @bases + (modifiers)* @mods) + => + (class_like_declaration + modifier: (modifier "protocol") + modifier: {..mods} + name: (identifier #{name}) + base_type: {..bases} + member: {..members}) + ), + // Protocol function — return type and body statements both optional. + rule!( + (protocol_function_declaration + name: @name + (parameter)* @params + return_type: _? @ret + body: (block statement: _* @body_stmts)? + (modifiers)* @mods) + => + (function_declaration + modifier: {..mods} + name: (identifier #{name}) + parameter: {..params} + return_type: {..ret} + body: (block stmt: {..body_stmts})) + ), + // Init declaration → constructor_declaration. Body statements optional; + // body itself is also optional (protocol requirement). + rule!( + (init_declaration + (parameter)* @params + body: (block statement: _* @body_stmts)? + (modifiers)* @mods) + => + (constructor_declaration + modifier: {..mods} + parameter: {..params} + body: (block stmt: {..body_stmts})) + ), + // Deinit declaration → destructor_declaration. Body statements optional. + rule!( + (deinit_declaration + body: (block statement: _* @body_stmts) + (modifiers)* @mods) + => + (destructor_declaration + modifier: {..mods} + body: (block stmt: {..body_stmts})) + ), + // Typealias declaration + rule!( + (typealias_declaration name: @name value: @val (modifiers)* @mods) + => + (type_alias_declaration + modifier: {..mods} + name: (identifier #{name}) + r#type: {val}) + ), + // Subscript declaration (not yet supported -- grammar needs to distinguish plain calls from subscript calls) + rule!( + (subscript_declaration (parameter)* @params (modifiers)* @mods) + => + (unsupported_node) + ), + // Associated type declaration (with optional bound) + rule!( + (associatedtype_declaration name: @name inherits_from: _? @bound (modifiers)* @mods) + => + (associated_type_declaration + modifier: {..mods} + name: (identifier #{name}) + bound: {..bound}) + ), + // Protocol property declaration: translate each accessor requirement to an + // accessor_declaration without a body, carrying the property name and type. + // Subsequent accessors get chained_declaration (same flattening as computed properties). + rule!( + (protocol_property_declaration + name: @pattern + requirements: (protocol_property_requirements accessor: _+ @accessors) + type: _? @ty + (modifiers)* @mods) + => + {..{ + let name_text = __yeast_ctx.ast.source_text(pattern.into()); + let mod_ids: Vec = mods.iter().map(|&m| m.into()).collect(); + let ty_ids: Vec = ty.iter().map(|&t| t.into()).collect(); + let acc_ids: Vec = accessors.iter().map(|&a| a.into()).collect(); + for (i, &acc_id) in acc_ids.iter().enumerate() { + if i > 0 { + let chained = __yeast_ctx.literal("modifier", "chained_declaration"); + __yeast_ctx.prepend_field(acc_id, "modifier", chained); + } + for &mod_id in mod_ids.iter().rev() { + __yeast_ctx.prepend_field(acc_id, "modifier", mod_id); + } + for &ty_id in ty_ids.iter().rev() { + __yeast_ctx.prepend_field(acc_id, "type", ty_id); + } + let ident = __yeast_ctx.literal("identifier", &name_text); + __yeast_ctx.prepend_field(acc_id, "name", ident); + } + acc_ids + }} + ), + // getter_specifier / setter_specifier → bodyless accessor_declaration + rule!((getter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "get"))), + rule!((setter_specifier) => (accessor_declaration accessor_kind: (accessor_kind "set"))), + // protocol_property_requirements wrapper — should be consumed by above; fallback + rule!((protocol_property_requirements accessor: _* @accs) => {..accs}), + // Computed getter → accessor_declaration (body optional). + rule!( + (computed_getter body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "get") + body: (block stmt: {..body})) + ), + // Computed setter with explicit parameter name. + rule!( + (computed_setter parameter: @param body: (block statement: _* @body)) + => + (accessor_declaration + accessor_kind: (accessor_kind "set") + parameter: (parameter pattern: (name_pattern identifier: (identifier #{param}))) + body: (block stmt: {..body})) + ), + // Computed setter without explicit parameter name; body optional. + rule!( + (computed_setter body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "set") + body: (block stmt: {..body})) + ), + // Computed modify → accessor_declaration + rule!( + (computed_modify body: (block statement: _* @body)) + => + (accessor_declaration + accessor_kind: (accessor_kind "modify") + body: (block stmt: {..body})) + ), + // willset/didset block — spread to children + rule!((willset_didset_block _* @clauses) => {..clauses}), + // willset clause → accessor_declaration (body optional). + rule!( + (willset_clause body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "willSet") + body: (block stmt: {..body})) + ), + // didset clause → accessor_declaration (body optional). + rule!( + (didset_clause body: (block statement: _* @body)?) + => + (accessor_declaration + accessor_kind: (accessor_kind "didSet") + body: (block stmt: {..body})) + ), + // Preprocessor conditionals — unsupported + rule!((diagnostic) => (unsupported_node)), // ---- Fallbacks ---- rule!( (_) diff --git a/unified/extractor/tests/corpus/swift/closures.txt b/unified/extractor/tests/corpus/swift/closures.txt index 638f8a328360..1d058dfb1e3c 100644 --- a/unified/extractor/tests/corpus/swift/closures.txt +++ b/unified/extractor/tests/corpus/swift/closures.txt @@ -73,8 +73,12 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node "Int" - return_type: unsupported_node "Int" + type: + named_type_expr + name: identifier "Int" + return_type: + named_type_expr + name: identifier "Int" === Closure with shorthand parameters @@ -245,11 +249,13 @@ top_level call_expr callee: member_access_expr - base: unsupported_node "self" + base: + name_expr + identifier: identifier "self" member: identifier "doThing" capture_declaration: variable_declaration - modifier: unsupported_node "weak" <-- ERROR: The field variable_declaration.modifier should contain modifier, but got unsupported_node + modifier: modifier "weak" pattern: name_pattern identifier: identifier "self" @@ -363,5 +369,9 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node "Int" - return_type: unsupported_node "Int" + type: + named_type_expr + name: identifier "Int" + return_type: + named_type_expr + name: identifier "Int" diff --git a/unified/extractor/tests/corpus/swift/collections.txt b/unified/extractor/tests/corpus/swift/collections.txt index 5ff49dd48999..2ecdf5a0179b 100644 --- a/unified/extractor/tests/corpus/swift/collections.txt +++ b/unified/extractor/tests/corpus/swift/collections.txt @@ -88,7 +88,14 @@ top_level pattern: name_pattern identifier: identifier "xs" - type: unsupported_node ": [Int]" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Array" + type_argument: + named_type_expr + name: identifier "Int" value: array_literal "[]" === @@ -192,7 +199,9 @@ top_level pattern: name_pattern identifier: identifier "s" - type: unsupported_node ": Set" + type: + named_type_expr + name: identifier "Set" value: array_literal element: diff --git a/unified/extractor/tests/corpus/swift/functions.txt b/unified/extractor/tests/corpus/swift/functions.txt index ce4b3e7524a4..ed86618910c8 100644 --- a/unified/extractor/tests/corpus/swift/functions.txt +++ b/unified/extractor/tests/corpus/swift/functions.txt @@ -135,7 +135,9 @@ top_level pattern: name_pattern identifier: identifier "b" - return_type: unsupported_node "Int" + return_type: + named_type_expr + name: identifier "Int" === Function with named parameters @@ -365,7 +367,9 @@ top_level pattern: name_pattern identifier: identifier "values" - return_type: unsupported_node "Int" + return_type: + named_type_expr + name: identifier "Int" === Function call @@ -554,7 +558,9 @@ top_level pattern: name_pattern identifier: identifier "x" - return_type: unsupported_node "T" + return_type: + named_type_expr + name: identifier "T" === Leading-dot expression value diff --git a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt index 1e4df4274ba1..23e545f54630 100644 --- a/unified/extractor/tests/corpus/swift/optionals-and-errors.txt +++ b/unified/extractor/tests/corpus/swift/optionals-and-errors.txt @@ -41,7 +41,14 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node ": Int?" + type: + generic_type_expr + base: + named_type_expr + name: identifier "Optional" + type_argument: + named_type_expr + name: identifier "Int" value: builtin_expr "nil" === @@ -228,7 +235,9 @@ top_level return_expr value: string_literal "\"\"" name: identifier "read" - return_type: unsupported_node "String" + return_type: + named_type_expr + name: identifier "String" === Do-catch diff --git a/unified/extractor/tests/corpus/swift/types.txt b/unified/extractor/tests/corpus/swift/types.txt index 4eab79716426..ef15ad87f594 100644 --- a/unified/extractor/tests/corpus/swift/types.txt +++ b/unified/extractor/tests/corpus/swift/types.txt @@ -19,7 +19,10 @@ source_file top_level body: block - stmt: unsupported_node "class Foo {}" + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Foo" === Class with stored properties @@ -82,7 +85,27 @@ source_file top_level body: block - stmt: unsupported_node "class Point {\n var x: Int\n var y: Int\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "y" + type: + named_type_expr + name: identifier "Int" + modifier: modifier "class" + name: identifier "Point" === Class with initializer @@ -157,7 +180,33 @@ 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}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + constructor_declaration + body: + block + stmt: + assign_expr + target: + member_access_expr + base: + name_expr + identifier: identifier "self" + member: identifier "x" + value: + name_expr + identifier: identifier "x" + modifier: modifier "class" + name: identifier "Point" === Class with method @@ -207,7 +256,28 @@ source_file top_level body: block - stmt: unsupported_node "class Counter {\n var n = 0\n func bump() {\n n += 1\n }\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "n" + value: int_literal "0" + function_declaration + body: + block + stmt: + compound_assign_expr + operator: infix_operator "+=" + target: + name_expr + identifier: identifier "n" + value: int_literal "1" + name: identifier "bump" + modifier: modifier "class" + name: identifier "Counter" === Class inheritance @@ -237,7 +307,10 @@ source_file top_level body: block - stmt: unsupported_node "class Dog: Animal {}" + stmt: + class_like_declaration + modifier: modifier "class" + name: identifier "Dog" === Struct @@ -300,7 +373,27 @@ source_file top_level body: block - stmt: unsupported_node "struct Point {\n let x: Int\n let y: Int\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "x" + type: + named_type_expr + name: identifier "Int" + variable_declaration + modifier: modifier "let" + pattern: + name_pattern + identifier: identifier "y" + type: + named_type_expr + name: identifier "Int" + modifier: modifier "struct" + name: identifier "Point" === Enum with cases @@ -345,7 +438,31 @@ source_file top_level body: block - stmt: unsupported_node "enum Direction {\n case north\n case south\n case east\n case west\n}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "north" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "south" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "east" + variable_declaration + modifier: modifier "enum_case" + pattern: + name_pattern + identifier: identifier "west" + modifier: modifier "enum" + name: identifier "Direction" === Enum with associated values @@ -404,7 +521,39 @@ source_file top_level body: block - stmt: unsupported_node "enum Shape {\n case circle(radius: Double)\n case square(side: Double)\n}" + stmt: + class_like_declaration + member: + class_like_declaration + member: + constructor_declaration + body: block "circle(radius: Double)" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "radius" + type: + named_type_expr + name: identifier "Double" + modifier: modifier "enum_case" + name: identifier "circle" + class_like_declaration + member: + constructor_declaration + body: block "square(side: Double)" + parameter: + parameter + pattern: + name_pattern + identifier: identifier "side" + type: + named_type_expr + name: identifier "Double" + modifier: modifier "enum_case" + name: identifier "square" + modifier: modifier "enum" + name: identifier "Shape" === Protocol declaration @@ -431,7 +580,14 @@ source_file top_level body: block - stmt: unsupported_node "protocol Drawable {\n func draw()\n}" + stmt: + class_like_declaration + member: + function_declaration + body: block "func draw()" + name: identifier "draw" + modifier: modifier "protocol" + name: identifier "Drawable" === Extension @@ -482,7 +638,29 @@ source_file top_level body: block - stmt: unsupported_node "extension Int {\n func squared() -> Int { return self * self }\n}" + stmt: + class_like_declaration + member: + function_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "self" + right: + name_expr + identifier: identifier "self" + name: identifier "squared" + return_type: + named_type_expr + name: identifier "Int" + modifier: modifier "extension" + name: identifier "Int" === Computed property @@ -576,7 +754,47 @@ 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}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "w" + type: + named_type_expr + name: identifier "Double" + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "h" + type: + named_type_expr + name: identifier "Double" + accessor_declaration + body: + block + stmt: + return_expr + value: + binary_expr + operator: infix_operator "*" + left: + name_expr + identifier: identifier "w" + right: + name_expr + identifier: identifier "h" + modifier: modifier "var" + name: identifier "area" + type: + named_type_expr + name: identifier "Double" + accessor_kind: accessor_kind "get" + modifier: modifier "class" + name: identifier "Rect" === Property with getter and setter @@ -662,4 +880,47 @@ 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}" + stmt: + class_like_declaration + member: + variable_declaration + modifier: modifier "var" + pattern: + name_pattern + identifier: identifier "_v" + value: int_literal "0" + accessor_declaration + body: + block + stmt: + return_expr + value: + name_expr + identifier: identifier "_v" + modifier: modifier "var" + name: identifier "v" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "get" + accessor_declaration + body: + block + stmt: + assign_expr + target: + name_expr + identifier: identifier "_v" + value: + name_expr + identifier: identifier "newValue" + modifier: + modifier "var" + modifier "chained_declaration" + name: identifier "v" + type: + named_type_expr + name: identifier "Int" + accessor_kind: accessor_kind "set" + modifier: modifier "class" + name: identifier "Box" diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index d7ff7a110a14..56d55313fb30 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -107,7 +107,9 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node ": Int" + type: + named_type_expr + name: identifier "Int" value: int_literal "1" === @@ -150,7 +152,9 @@ top_level pattern: name_pattern identifier: identifier "x" - type: unsupported_node ": Int" + type: + named_type_expr + name: identifier "Int" === Tuple destructuring binding From fa98557dd95ec352a2d7a4833e98d3550088ac2c Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 13:45:00 +0200 Subject: [PATCH 21/29] Update QL test output --- .../library-tests/BasicTest/test.expected | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/unified/ql/test/library-tests/BasicTest/test.expected b/unified/ql/test/library-tests/BasicTest/test.expected index 5298ec6f982f..b9f4eafe8653 100644 --- a/unified/ql/test/library-tests/BasicTest/test.expected +++ b/unified/ql/test/library-tests/BasicTest/test.expected @@ -1,9 +1,37 @@ nameExpr +| name_expr.swift:1:9:1:9 | NameExpr | y | +| test.swift:1:8:1:17 | NameExpr | Foundation | +| test.swift:8:9:8:13 | NameExpr | items | +| test.swift:8:22:8:25 | NameExpr | item | +| test.swift:12:16:12:20 | NameExpr | items | +| test.swift:12:31:12:34 | NameExpr | item | +| test.swift:25:18:25:22 | NameExpr | Array | +| test.swift:25:24:25:28 | NameExpr | first | +| test.swift:26:17:26:22 | NameExpr | second | +| test.swift:27:13:27:18 | NameExpr | result | +| test.swift:27:29:27:32 | NameExpr | item | +| test.swift:28:13:28:18 | NameExpr | result | +| test.swift:28:27:28:30 | NameExpr | item | +| test.swift:31:12:31:17 | NameExpr | result | +| test.swift:40:16:40:19 | NameExpr | data | +| test.swift:44:9:44:12 | NameExpr | data | +| test.swift:48:15:48:19 | NameExpr | index | +| test.swift:48:29:48:33 | NameExpr | index | +| test.swift:48:37:48:40 | NameExpr | data | +| test.swift:49:16:49:19 | NameExpr | data | +| test.swift:49:21:49:25 | NameExpr | index | +| test.swift:53:9:53:12 | NameExpr | data | +| test.swift:53:21:53:24 | NameExpr | item | +| test.swift:63:16:63:19 | NameExpr | self | +| test.swift:65:29:65:37 | NameExpr | transform | +| test.swift:65:39:65:43 | NameExpr | value | +| test.swift:67:29:67:33 | NameExpr | error | +| test.swift:76:16:76:19 | NameExpr | self | +| test.swift:76:21:76:21 | NameExpr | i | +| test.swift:76:26:76:29 | NameExpr | self | +| test.swift:76:31:76:31 | NameExpr | i | +| test.swift:86:12:86:17 | NameExpr | values | +| test.swift:87:12:87:17 | NameExpr | values | +| test.swift:87:38:87:43 | NameExpr | values | +| test.swift:87:49:87:57 | NameExpr | transform | unsupported -| test.swift:3:1:3:38 | | | -| test.swift:16:1:16:32 | | | -| test.swift:23:1:23:37 | | | -| test.swift:34:1:34:49 | | | -| test.swift:57:1:57:30 | | | -| test.swift:72:1:72:37 | | | -| test.swift:84:1:84:24 | | | From 2470c1388a20e8630eb53f83305177d7cf7d9f1f Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:36:26 +0200 Subject: [PATCH 22/29] Fix: preserve switch case patterns in desugared output The switch_entry rule was capturing switch_pattern wrapper nodes instead of drilling into them to extract the actual pattern nodes. This caused patterns from switch cases to be lost during desugaring. Changed the pattern match from: (switch_entry pattern: (switch_pattern)* @pats ...) to: (switch_entry pattern: (switch_pattern pattern: @pats)* ...) This now correctly extracts the pattern field from each switch_pattern node, ensuring that patterns from cases like 'case 1:' and 'case .circle(let r):' are preserved in the switch_case AST nodes. Updated control-flow.txt corpus outputs to reflect the new behavior. --- unified/extractor/src/languages/swift/swift.rs | 4 +--- unified/extractor/tests/corpus/swift/control-flow.txt | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 26ec4c1b0ee9..058cbcfe36e0 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -481,7 +481,7 @@ fn translation_rules() -> Vec { ), // Switch entry with patterns and body rule!( - (switch_entry pattern: (switch_pattern)* @pats statement: _* @body) + (switch_entry pattern: (switch_pattern pattern: @pats)* statement: _* @body) => (switch_case pattern: {..pats} body: (block stmt: {..body})) ), @@ -491,8 +491,6 @@ fn translation_rules() -> Vec { => (switch_case body: (block stmt: {..body})) ), - // Switch pattern — unwrap to inner pattern - rule!((switch_pattern (pattern)* @inner) => {..inner}), // if case let x = expr — the pattern is taken as-is (no Optional wrapping) rule!( (if_let_binding "case" (value_binding_pattern) bound_identifier: @name _ @val) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 680c55898782..6638b26e2694 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -558,6 +558,7 @@ top_level callee: name_expr identifier: identifier "print" + pattern: tuple_pattern "1" switch_case body: block @@ -569,6 +570,9 @@ top_level callee: name_expr identifier: identifier "print" + pattern: + tuple_pattern "2" + tuple_pattern "3" switch_case body: block @@ -688,6 +692,7 @@ top_level callee: name_expr identifier: identifier "print" + pattern: tuple_pattern ".circle(let r)" switch_case body: block @@ -701,6 +706,7 @@ top_level callee: name_expr identifier: identifier "print" + pattern: tuple_pattern ".square(let s)" value: name_expr identifier: identifier "shape" From 142ac47166c0fb7e88be8df3f521db6a2e1e19c1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:43:14 +0200 Subject: [PATCH 23/29] Refactor: map switch case patterns to constructor_pattern instead of tuple_pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed the desugaring rules to properly map case patterns with binding (e.g., 'case .circle(let r):') to constructor_pattern nodes instead of tuple_pattern. New rules added: - tuple_pattern_item → pattern_element (preserves optional name/key) - pattern.kind: binding_pattern → name_pattern (extracts bound identifier) - pattern.kind: case_pattern → constructor_pattern (creates proper constructor with bound arguments as pattern_elements) This provides a more semantically correct AST representation: - Constructor name: name_expr identifier 'circle' - Elements: pattern_element containing name_pattern identifier 'r' Instead of the previous tuple_pattern string representation. Updated control-flow.txt corpus outputs. --- .../extractor/src/languages/swift/swift.rs | 26 +++++++++++++++++++ .../tests/corpus/swift/control-flow.txt | 24 +++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 058cbcfe36e0..9b7bd5379327 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -282,6 +282,32 @@ fn translation_rules() -> Vec { rule!((type name: @inner) => {inner}), // `directly_assignable_expression` is just a wrapper; unwrap it rule!((directly_assignable_expression expr: @inner) => {inner}), + // tuple_pattern_item → pattern_element (preserves optional name/key) + rule!((tuple_pattern_item name: _? @key pattern: @pat) => (pattern_element key: {..key} pattern: {pat})), + rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), + // Pattern with 'let' or 'var' binding: extract the inner pattern + // TODO: Names in a pattern need to be translated to expr_equality_pattern if not under a 'var/let' but we lack a way to pass down context to do this. + rule!( + (pattern kind: (binding_pattern binding: _? pattern: @pattern)) + => + {pattern} + ), + // case T.foo(x,y) pattern + rule!( + (pattern kind: (case_pattern type: @typ name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + => + (constructor_pattern + constructor: (member_access_expr base: {typ} member: (identifier #{name})) + element: {..items}) + ), + // case .foo(x,y) pattern + rule!( + (pattern kind: (case_pattern name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + => + (constructor_pattern + constructor: (member_access_expr base: (inferred_type_expr) member: (identifier #{name})) + element: {..items}) + ), // Pattern with bound_identifier → name_pattern rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), // Tuple pattern (destructuring) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 6638b26e2694..1f06bc056015 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -692,7 +692,17 @@ top_level callee: name_expr identifier: identifier "print" - pattern: tuple_pattern ".circle(let r)" + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "r" + constructor: + member_access_expr + base: inferred_type_expr ".circle(let r)" + member: identifier "circle" switch_case body: block @@ -706,7 +716,17 @@ top_level callee: name_expr identifier: identifier "print" - pattern: tuple_pattern ".square(let s)" + pattern: + constructor_pattern + element: + pattern_element + pattern: + name_pattern + identifier: identifier "s" + constructor: + member_access_expr + base: inferred_type_expr ".square(let s)" + member: identifier "square" value: name_expr identifier: identifier "shape" From 0b666d47db4daaecc9eb491441b4cba0c4983c12 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:55:54 +0200 Subject: [PATCH 24/29] Preserve the dot token in case patterns --- unified/extractor/tree-sitter-swift/grammar.js | 2 +- unified/extractor/tree-sitter-swift/node-types.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/unified/extractor/tree-sitter-swift/grammar.js b/unified/extractor/tree-sitter-swift/grammar.js index 37d074eaec2b..7052d2ebdd5b 100644 --- a/unified/extractor/tree-sitter-swift/grammar.js +++ b/unified/extractor/tree-sitter-swift/grammar.js @@ -1930,7 +1930,7 @@ module.exports = grammar({ seq( optional("case"), optional(field("type", $.user_type)), // XXX this should just be _type but that creates ambiguity - $._dot, + field("dot", $._dot), field("name", $.simple_identifier), optional(field("arguments", $.tuple_pattern)) ), diff --git a/unified/extractor/tree-sitter-swift/node-types.yml b/unified/extractor/tree-sitter-swift/node-types.yml index 837116c13e04..35dfb985b4a8 100644 --- a/unified/extractor/tree-sitter-swift/node-types.yml +++ b/unified/extractor/tree-sitter-swift/node-types.yml @@ -173,6 +173,7 @@ named: value?: expression case_pattern: arguments?: tuple_pattern + dot: "." name: simple_identifier type?: user_type catch_block: From 21822651204e48675480b33cf2d21d2ed0936551 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 18 Jun 2026 14:57:55 +0200 Subject: [PATCH 25/29] unified/swift: Better source range for inferred_type_expr --- unified/extractor/src/languages/swift/swift.rs | 4 ++-- unified/extractor/tests/corpus/swift/control-flow.txt | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 9b7bd5379327..8d5e9f9052a8 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -302,10 +302,10 @@ fn translation_rules() -> Vec { ), // case .foo(x,y) pattern rule!( - (pattern kind: (case_pattern name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) + (pattern kind: (case_pattern dot: @dot name: @name arguments: (tuple_pattern item: (tuple_pattern_item)* @items)? )) => (constructor_pattern - constructor: (member_access_expr base: (inferred_type_expr) member: (identifier #{name})) + constructor: (member_access_expr base: (inferred_type_expr #{dot}) member: (identifier #{name})) element: {..items}) ), // Pattern with bound_identifier → name_pattern diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 1f06bc056015..369e3f8a3d23 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -626,6 +626,7 @@ source_file pattern: pattern bound_identifier: simple_identifier "r" + dot: . name: simple_identifier "circle" statement: call_expression @@ -658,6 +659,7 @@ source_file pattern: pattern bound_identifier: simple_identifier "s" + dot: . name: simple_identifier "square" statement: call_expression @@ -701,7 +703,7 @@ top_level identifier: identifier "r" constructor: member_access_expr - base: inferred_type_expr ".circle(let r)" + base: inferred_type_expr "." member: identifier "circle" switch_case body: @@ -725,7 +727,7 @@ top_level identifier: identifier "s" constructor: member_access_expr - base: inferred_type_expr ".square(let s)" + base: inferred_type_expr "." member: identifier "square" value: name_expr From 63e1cc90e91f810c1129ced6bd4b97b57fae8454 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 10:27:20 +0200 Subject: [PATCH 26/29] Test: add corpus test for switch case patterns with labeled arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a test case 'Switch with labeled case pattern arguments' covering: - case .implicit(isAcknowledged: false) — labeled bool literal - case .thread(threadRowId: _, let rowId) — labeled wildcard + binding The current output contains type errors: pattern_element::key is being produced as name_expr instead of identifier. These will be fixed in the following commit. --- .../tests/corpus/swift/control-flow.txt | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index 369e3f8a3d23..ee87cf2c00ff 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -732,3 +732,154 @@ top_level value: name_expr identifier: identifier "shape" + +=== +Switch with labeled case pattern arguments +=== + +switch x { +case .implicit(isAcknowledged: false): + print("yes") +case .thread(threadRowId: _, let rowId): + print(rowId) +} + +--- + +source_file + statement: + switch_statement + entry: + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + name: simple_identifier "isAcknowledged" + pattern: + pattern + kind: + boolean_literal + dot: . + name: simple_identifier "implicit" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: + line_string_literal + text: line_str_text "yes" + switch_entry + pattern: + switch_pattern + pattern: + pattern + kind: + case_pattern + arguments: + tuple_pattern + item: + tuple_pattern_item + name: simple_identifier "threadRowId" + pattern: + pattern + kind: wildcard_pattern "_" + tuple_pattern_item + pattern: + pattern + kind: + binding_pattern + binding: + value_binding_pattern + mutability: let + pattern: + pattern + bound_identifier: simple_identifier "rowId" + dot: . + name: simple_identifier "thread" + statement: + call_expression + function: simple_identifier "print" + suffix: + call_suffix + arguments: + value_arguments + argument: + value_argument + value: simple_identifier "rowId" + expr: simple_identifier "x" + +--- + +top_level + body: + block + stmt: + switch_expr + case: + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: string_literal "\"yes\"" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + key: + name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr + identifier: identifier "isAcknowledged" + pattern: tuple_pattern "false" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "implicit" + switch_case + body: + block + stmt: + call_expr + argument: + argument + value: + name_expr + identifier: identifier "rowId" + callee: + name_expr + identifier: identifier "print" + pattern: + constructor_pattern + element: + pattern_element + key: + name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr + identifier: identifier "threadRowId" + pattern: tuple_pattern "_" + pattern_element + pattern: + name_pattern + identifier: identifier "rowId" + constructor: + member_access_expr + base: inferred_type_expr "." + member: identifier "thread" + value: + name_expr + identifier: identifier "x" From c01264d05c9e01b6995dd6508cc21e76cf6bd798 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 10:31:34 +0200 Subject: [PATCH 27/29] Coerce pattern_element.key to be an identifier --- unified/extractor/src/languages/swift/swift.rs | 2 +- unified/extractor/tests/corpus/swift/control-flow.txt | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 8d5e9f9052a8..daf5d671981b 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -283,7 +283,7 @@ fn translation_rules() -> Vec { // `directly_assignable_expression` is just a wrapper; unwrap it rule!((directly_assignable_expression expr: @inner) => {inner}), // tuple_pattern_item → pattern_element (preserves optional name/key) - rule!((tuple_pattern_item name: _? @key pattern: @pat) => (pattern_element key: {..key} pattern: {pat})), + rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), // Pattern with 'let' or 'var' binding: extract the inner pattern // TODO: Names in a pattern need to be translated to expr_equality_pattern if not under a 'var/let' but we lack a way to pass down context to do this. diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index ee87cf2c00ff..ff7c529d6e99 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -843,9 +843,7 @@ top_level constructor_pattern element: pattern_element - key: - name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr - identifier: identifier "isAcknowledged" + key: identifier "isAcknowledged" pattern: tuple_pattern "false" constructor: member_access_expr @@ -868,9 +866,7 @@ top_level constructor_pattern element: pattern_element - key: - name_expr <-- ERROR: The field pattern_element.key should contain identifier, but got name_expr - identifier: identifier "threadRowId" + key: identifier "threadRowId" pattern: tuple_pattern "_" pattern_element pattern: From 267507029118d380d1efcefec52628fd52689ffb Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 11:32:07 +0200 Subject: [PATCH 28/29] unified/swift: Clean up translation of patterns Patterns have an unusual parse tree, but now the matching should at least be a bit easier to follow. The TODO regarding not being able to pass down context to handle var/let is still relevant, and can't be solved in the mapping alone. --- .../extractor/src/languages/swift/swift.rs | 21 ++++++++++++------- .../tests/corpus/swift/control-flow.txt | 16 +++++++++----- .../tests/corpus/swift/variables.txt | 16 +++++++++++++- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index daf5d671981b..65b9a7d418ea 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -282,9 +282,8 @@ fn translation_rules() -> Vec { rule!((type name: @inner) => {inner}), // `directly_assignable_expression` is just a wrapper; unwrap it rule!((directly_assignable_expression expr: @inner) => {inner}), - // tuple_pattern_item → pattern_element (preserves optional name/key) - rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), - rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), + // Pattern with bound_identifier → name_pattern + rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), // Pattern with 'let' or 'var' binding: extract the inner pattern // TODO: Names in a pattern need to be translated to expr_equality_pattern if not under a 'var/let' but we lack a way to pass down context to do this. rule!( @@ -308,10 +307,18 @@ fn translation_rules() -> Vec { constructor: (member_access_expr base: (inferred_type_expr #{dot}) member: (identifier #{name})) element: {..items}) ), - // Pattern with bound_identifier → name_pattern - rule!((pattern bound_identifier: @name) => (name_pattern identifier: (identifier #{name}))), - // Tuple pattern (destructuring) - rule!((pattern (pattern)* @elems) => (tuple_pattern element: {..elems})), + // Tuple pattern and its (optionally named) items + rule!((pattern kind: (tuple_pattern item: _* @elems)) => (tuple_pattern element: {..elems})), + rule!((tuple_pattern_item name: @key pattern: @pat) => (pattern_element key: (identifier #{key}) pattern: {pat})), + rule!((tuple_pattern_item pattern: @pat) => (pattern_element pattern: {pat})), + // Type casting pattern (TODO) + rule!((pattern kind: (type_casting_pattern)) => (unsupported_node)), + // Wildcard pattern + rule!((pattern kind: (wildcard_pattern)) => (ignore_pattern)), + // Expression pattern + // We lack a way to check if 'expr' is actually an expression, but due to rule ordering + // the 'expression' case is the only remaining possibility when this rule tries to match. + rule!((pattern kind: @expr) => (expr_equality_pattern expr: {expr})), // ---- Functions ---- // Function declaration // Function declaration (return type optional, body statements optional). diff --git a/unified/extractor/tests/corpus/swift/control-flow.txt b/unified/extractor/tests/corpus/swift/control-flow.txt index ff7c529d6e99..9a740cb9d450 100644 --- a/unified/extractor/tests/corpus/swift/control-flow.txt +++ b/unified/extractor/tests/corpus/swift/control-flow.txt @@ -558,7 +558,9 @@ top_level callee: name_expr identifier: identifier "print" - pattern: tuple_pattern "1" + pattern: + expr_equality_pattern + expr: int_literal "1" switch_case body: block @@ -571,8 +573,10 @@ top_level name_expr identifier: identifier "print" pattern: - tuple_pattern "2" - tuple_pattern "3" + expr_equality_pattern + expr: int_literal "2" + expr_equality_pattern + expr: int_literal "3" switch_case body: block @@ -844,7 +848,9 @@ top_level element: pattern_element key: identifier "isAcknowledged" - pattern: tuple_pattern "false" + pattern: + expr_equality_pattern + expr: boolean_literal "false" constructor: member_access_expr base: inferred_type_expr "." @@ -867,7 +873,7 @@ top_level element: pattern_element key: identifier "threadRowId" - pattern: tuple_pattern "_" + pattern: ignore_pattern "_" pattern_element pattern: name_pattern diff --git a/unified/extractor/tests/corpus/swift/variables.txt b/unified/extractor/tests/corpus/swift/variables.txt index 56d55313fb30..f1da058eef2e 100644 --- a/unified/extractor/tests/corpus/swift/variables.txt +++ b/unified/extractor/tests/corpus/swift/variables.txt @@ -195,7 +195,21 @@ top_level stmt: variable_declaration modifier: modifier "let" - pattern: tuple_pattern "(a, b)" + pattern: + tuple_pattern + element: + pattern_element + pattern: + expr_equality_pattern + expr: + name_expr + identifier: identifier "a" + pattern_element + pattern: + expr_equality_pattern + expr: + name_expr + identifier: identifier "b" value: name_expr identifier: identifier "pair" From 66c1f037f5df37cbea910805dfc5b78b3cc9c92a Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 19 Jun 2026 12:19:51 +0200 Subject: [PATCH 29/29] Add TODO --- unified/extractor/src/languages/swift/swift.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 65b9a7d418ea..79f0e65b02f5 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -77,7 +77,7 @@ fn translation_rules() -> Vec { rule!((prefix_expression operation: @op target: @operand) => (unary_expr operator: (prefix_operator #{op}) operand: {operand})), // Postfix unary operators rule!((postfix_expression operation: @op target: @operand) => (unary_expr operator: (postfix_operator #{op}) operand: {operand})), - // Parenthesised single-value tuple is a grouping expression; pass through. + // TODO: Parenthesised single-value tuple is a grouping expression and should pass through. // Multi-value tuples become tuple_expr. rule!((tuple_expression value: _* @v) => (tuple_expr element: {..v})), // Blocks contain statement* directly.